@xenon-device-management/xenon 1.1.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.
Files changed (228) hide show
  1. package/README.md +446 -0
  2. package/lib/package.json +207 -0
  3. package/lib/public/assets/Layouts-7IT8aFLI.js +11 -0
  4. package/lib/public/assets/Layouts-DPMls9vh.css +1 -0
  5. package/lib/public/assets/ai-settings-BbnfgdEx.js +11 -0
  6. package/lib/public/assets/apps-CRMrI4_p.js +16 -0
  7. package/lib/public/assets/apps-CcM77dgg.css +1 -0
  8. package/lib/public/assets/badge-B1nKs8zj.css +1 -0
  9. package/lib/public/assets/badge-CSvl5xIU.js +11 -0
  10. package/lib/public/assets/button-CJlKn4PZ.css +1 -0
  11. package/lib/public/assets/button-CvLaGFYj.js +26 -0
  12. package/lib/public/assets/calendar-6w-D6Oaw.js +6 -0
  13. package/lib/public/assets/clock-DcdeWBPr.js +6 -0
  14. package/lib/public/assets/cpu-DiSoXT9n.js +6 -0
  15. package/lib/public/assets/device-explorer-CajM63OJ.js +193 -0
  16. package/lib/public/assets/device-explorer-CxdUAoTL.css +1 -0
  17. package/lib/public/assets/index-ByQwMN5T.js +174 -0
  18. package/lib/public/assets/index-C1DBaoSh.js +1 -0
  19. package/lib/public/assets/index-qzCez_kk.css +1 -0
  20. package/lib/public/assets/lock-B23ibZmo.js +6 -0
  21. package/lib/public/assets/maintenance-settings-CirzA6yG.js +6 -0
  22. package/lib/public/assets/mouse-pointer-2-Cz76SHFb.js +6 -0
  23. package/lib/public/assets/plus-BBwlIevt.js +6 -0
  24. package/lib/public/assets/session-dashboard-C2k7FFv_.css +1 -0
  25. package/lib/public/assets/session-dashboard-HPDtwPOZ.js +62 -0
  26. package/lib/public/assets/settings-DrZsZwdc.js +1 -0
  27. package/lib/public/assets/trash-2-DQpvzJec.js +6 -0
  28. package/lib/public/assets/useSocket-Dxsqae2a.js +16 -0
  29. package/lib/public/assets/webhook-settings-CDPgsgkb.css +1 -0
  30. package/lib/public/assets/webhook-settings-Cp-B4Nrw.js +1 -0
  31. package/lib/public/assets/zap-DovP6iow.js +6 -0
  32. package/lib/public/favicon.ico +0 -0
  33. package/lib/public/favicon.png +0 -0
  34. package/lib/public/favicon.svg +9 -0
  35. package/lib/public/index.html +46 -0
  36. package/lib/public/logo.svg +17 -0
  37. package/lib/public/logo192.png +0 -0
  38. package/lib/public/logo512.png +0 -0
  39. package/lib/public/manifest.json +25 -0
  40. package/lib/public/robots.txt +3 -0
  41. package/lib/schema.json +348 -0
  42. package/lib/src/InternalHttpClient.js +212 -0
  43. package/lib/src/PluginContext.js +29 -0
  44. package/lib/src/XenonCapabilityManager.js +199 -0
  45. package/lib/src/app/index.js +167 -0
  46. package/lib/src/app/routers/apps.js +79 -0
  47. package/lib/src/app/routers/config.js +131 -0
  48. package/lib/src/app/routers/control.js +835 -0
  49. package/lib/src/app/routers/dashboard.js +301 -0
  50. package/lib/src/app/routers/grid.js +352 -0
  51. package/lib/src/app/routers/reservation.js +190 -0
  52. package/lib/src/app/routers/webhook.js +83 -0
  53. package/lib/src/app/swagger-docs.js +203 -0
  54. package/lib/src/app/swagger.js +366 -0
  55. package/lib/src/chromeUtils.js +148 -0
  56. package/lib/src/commands/handle.js +19 -0
  57. package/lib/src/commands/index.js +8 -0
  58. package/lib/src/config.js +73 -0
  59. package/lib/src/dashboard/asset-manager.js +84 -0
  60. package/lib/src/dashboard/commands.js +284 -0
  61. package/lib/src/dashboard/event-manager.js +699 -0
  62. package/lib/src/dashboard/services/app-service.js +134 -0
  63. package/lib/src/dashboard/services/failure-analysis-service.js +173 -0
  64. package/lib/src/dashboard/services/session-service.js +113 -0
  65. package/lib/src/data-service/CircuitBreaker.js +83 -0
  66. package/lib/src/data-service/config-service.js +155 -0
  67. package/lib/src/data-service/db.js +122 -0
  68. package/lib/src/data-service/device-service.js +320 -0
  69. package/lib/src/data-service/device-store.interface.js +2 -0
  70. package/lib/src/data-service/device-store.js +345 -0
  71. package/lib/src/data-service/pending-sessions-service.js +25 -0
  72. package/lib/src/data-service/pluginArgs.js +25 -0
  73. package/lib/src/data-service/prisma-service.js +31 -0
  74. package/lib/src/data-service/prisma-store.js +385 -0
  75. package/lib/src/data-service/queue-service.js +150 -0
  76. package/lib/src/data-service/web-config-service.js +130 -0
  77. package/lib/src/device-managers/AndroidDeviceManager.js +1155 -0
  78. package/lib/src/device-managers/ChromeDriverManager.js +68 -0
  79. package/lib/src/device-managers/HealthMonitorService.js +325 -0
  80. package/lib/src/device-managers/IOSDeviceManager.js +351 -0
  81. package/lib/src/device-managers/NodeDevices.js +82 -0
  82. package/lib/src/device-managers/android/AndroidStreamService.js +370 -0
  83. package/lib/src/device-managers/android/DeviceLockManager.js +45 -0
  84. package/lib/src/device-managers/cloud/CapabilityManager.js +26 -0
  85. package/lib/src/device-managers/cloud/Devices.js +86 -0
  86. package/lib/src/device-managers/iOSTracker.js +44 -0
  87. package/lib/src/device-managers/index.js +89 -0
  88. package/lib/src/device-managers/ios/IOSDiscoveryService.js +268 -0
  89. package/lib/src/device-managers/ios/IOSStreamService.js +893 -0
  90. package/lib/src/device-managers/ios/WDAClient.js +866 -0
  91. package/lib/src/device-utils.js +663 -0
  92. package/lib/src/enums/Capabilities.js +8 -0
  93. package/lib/src/enums/Cloud.js +11 -0
  94. package/lib/src/enums/Platform.js +9 -0
  95. package/lib/src/enums/SessionType.js +9 -0
  96. package/lib/src/enums/SocketEvents.js +15 -0
  97. package/lib/src/helpers/UniversalMjpegProxy.js +273 -0
  98. package/lib/src/helpers/index.js +229 -0
  99. package/lib/src/index.js +95 -0
  100. package/lib/src/interceptors/CommandInterceptor.js +524 -0
  101. package/lib/src/interfaces/ICloudManager.js +2 -0
  102. package/lib/src/interfaces/IDevice.js +2 -0
  103. package/lib/src/interfaces/IDeviceFilterOptions.js +2 -0
  104. package/lib/src/interfaces/IDeviceManager.js +2 -0
  105. package/lib/src/interfaces/IOptions.js +2 -0
  106. package/lib/src/interfaces/IPluginArgs.js +55 -0
  107. package/lib/src/interfaces/ISessionCapability.js +2 -0
  108. package/lib/src/logger.js +225 -0
  109. package/lib/src/plugin.js +244 -0
  110. package/lib/src/prisma.js +12 -0
  111. package/lib/src/profiling/AndroidAppProfiler.js +213 -0
  112. package/lib/src/proxy/wd-command-proxy.js +221 -0
  113. package/lib/src/scripts/generate-database-migration.js +59 -0
  114. package/lib/src/scripts/initialize-database.js +55 -0
  115. package/lib/src/scripts/install-go-ios.js +66 -0
  116. package/lib/src/scripts/prepare-prisma.js +89 -0
  117. package/lib/src/services/AICommandService.js +143 -0
  118. package/lib/src/services/AIService.js +466 -0
  119. package/lib/src/services/CleanupService.js +141 -0
  120. package/lib/src/services/EventBus.js +74 -0
  121. package/lib/src/services/InspectorService.js +395 -0
  122. package/lib/src/services/MetricsService.js +134 -0
  123. package/lib/src/services/NetworkConditioningService.js +173 -0
  124. package/lib/src/services/NotificationService.js +163 -0
  125. package/lib/src/services/RequestLogService.js +252 -0
  126. package/lib/src/services/ResourceIsolationService.js +122 -0
  127. package/lib/src/services/SecurityService.js +120 -0
  128. package/lib/src/services/ServerManager.js +284 -0
  129. package/lib/src/services/SessionHeartbeatService.js +158 -0
  130. package/lib/src/services/SessionLifecycleService.js +572 -0
  131. package/lib/src/services/SocketClient.js +71 -0
  132. package/lib/src/services/SocketServer.js +87 -0
  133. package/lib/src/services/TracingService.js +132 -0
  134. package/lib/src/services/VideoPipelineService.js +220 -0
  135. package/lib/src/services/healing/FuzzyXmlHealingProvider.js +333 -0
  136. package/lib/src/services/healing/HealEtalonService.js +98 -0
  137. package/lib/src/services/healing/HealedLocatorGenerator.js +132 -0
  138. package/lib/src/services/healing/HealingOrchestrator.js +165 -0
  139. package/lib/src/services/healing/LlmHealingProvider.js +77 -0
  140. package/lib/src/services/healing/OcrHealingProvider.js +119 -0
  141. package/lib/src/services/healing/ResilioTreeHealingProvider.js +100 -0
  142. package/lib/src/services/healing/VisualAiHealingProvider.js +90 -0
  143. package/lib/src/services/healing/types.js +12 -0
  144. package/lib/src/services/omni-vision/OmniVisionService.js +718 -0
  145. package/lib/src/services/omni-vision/VisionAssertionService.js +68 -0
  146. package/lib/src/sessions/CloudSession.js +42 -0
  147. package/lib/src/sessions/LocalSession.js +313 -0
  148. package/lib/src/sessions/RemoteSession.js +287 -0
  149. package/lib/src/sessions/SessionManager.js +238 -0
  150. package/lib/src/sessions/XenonSession.js +44 -0
  151. package/lib/src/types/CLIArgs.js +2 -0
  152. package/lib/src/types/CloudArgs.js +2 -0
  153. package/lib/src/types/CloudSchema.js +131 -0
  154. package/lib/src/types/DeviceType.js +2 -0
  155. package/lib/src/types/DeviceUpdate.js +2 -0
  156. package/lib/src/types/IOSDevice.js +2 -0
  157. package/lib/src/types/Platform.js +2 -0
  158. package/lib/src/types/SessionStatus.js +11 -0
  159. package/lib/src/validators/CapabilityValidator.js +93 -0
  160. package/lib/test/e2e/android/conf.spec.js +43 -0
  161. package/lib/test/e2e/android/conf2.spec.js +44 -0
  162. package/lib/test/e2e/android/conf3.spec.js +44 -0
  163. package/lib/test/e2e/e2ehelper.js +113 -0
  164. package/lib/test/e2e/hubnode/forward-request.spec.js +224 -0
  165. package/lib/test/e2e/hubnode/hubnode.spec.js +214 -0
  166. package/lib/test/e2e/ios/conf1.spec.js +39 -0
  167. package/lib/test/e2e/ios/conf2.spec.js +39 -0
  168. package/lib/test/e2e/plugin-harness.js +236 -0
  169. package/lib/test/e2e/plugin.spec.js +97 -0
  170. package/lib/test/e2e/telemetry_verification.spec.js +83 -0
  171. package/lib/test/e2e/video-recording-test.spec.js +63 -0
  172. package/lib/test/helpers/test-container.js +112 -0
  173. package/lib/test/integration/androidDevices.spec.js +137 -0
  174. package/lib/test/integration/cliArgs.js +73 -0
  175. package/lib/test/integration/ios/01iOSSimulator.spec.js +291 -0
  176. package/lib/test/integration/ios/02iOSDevices.spec.js +75 -0
  177. package/lib/test/integration/testHelpers.js +74 -0
  178. package/lib/test/unit/AndroidDeviceManager.spec.js +178 -0
  179. package/lib/test/unit/ChromeDriverManager.spec.js +26 -0
  180. package/lib/test/unit/CleanupService.spec.js +21 -0
  181. package/lib/test/unit/DeviceModel.spec.js +157 -0
  182. package/lib/test/unit/FuzzyXmlHealingProvider.test.js +294 -0
  183. package/lib/test/unit/GetAdbOriginal.js +42 -0
  184. package/lib/test/unit/HealingCascade.test.js +128 -0
  185. package/lib/test/unit/IOSDeviceManager.spec.js +261 -0
  186. package/lib/test/unit/RemoteIOs.spec.js +78 -0
  187. package/lib/test/unit/ResilioTreeHealingProvider.test.js +96 -0
  188. package/lib/test/unit/commands.spec.js +27 -0
  189. package/lib/test/unit/config.spec.js +27 -0
  190. package/lib/test/unit/device-service.spec.js +307 -0
  191. package/lib/test/unit/device-utils.spec.js +313 -0
  192. package/lib/test/unit/fixtures/device.config.js +4 -0
  193. package/lib/test/unit/fixtures/devices.js +89 -0
  194. package/lib/test/unit/helpers.spec.js +62 -0
  195. package/lib/test/unit/omni-vision.spec.js +100 -0
  196. package/lib/test/unit/plugin.spec.js +133 -0
  197. package/lib/tsconfig.tsbuildinfo +1 -0
  198. package/package.json +207 -0
  199. package/prisma/data.db +0 -0
  200. package/prisma/dev.db +0 -0
  201. package/prisma/dev.db-journal +0 -0
  202. package/prisma/migrations/20231011074725_initial_tables/migration.sql +47 -0
  203. package/prisma/migrations/20231226115334_update_session_log/migration.sql +2 -0
  204. package/prisma/migrations/20251204113710_add_video_recording_enabled/migration.sql +29 -0
  205. package/prisma/migrations/20251204132449_add_log_table/migration.sql +11 -0
  206. package/prisma/migrations/20251205050111_add_profiling_support/migration.sql +47 -0
  207. package/prisma/migrations/20251205050947_add_is_error_field/migration.sql +24 -0
  208. package/prisma/migrations/20260126201337_add_app_model/migration.sql +18 -0
  209. package/prisma/migrations/20260130115722_add_performance_trace_and_xenon_sync/migration.sql +2 -0
  210. package/prisma/migrations/20260130135114_add_device_models/migration.sql +57 -0
  211. package/prisma/migrations/20260130140655_make_systemport_optional/migration.sql +45 -0
  212. package/prisma/migrations/20260130140932_make_device_fields_optional/migration.sql +45 -0
  213. package/prisma/migrations/20260130141040_final_schema_fix/migration.sql +45 -0
  214. package/prisma/migrations/20260130143234_add_device_health_fields/migration.sql +4 -0
  215. package/prisma/migrations/20260130144921_add_failure_category/migration.sql +2 -0
  216. package/prisma/migrations/20260131151456_add_webhook_config/migration.sql +10 -0
  217. package/prisma/migrations/20260201094507_add_device_tags/migration.sql +11 -0
  218. package/prisma/migrations/20260201103410_add_managed_process/migration.sql +15 -0
  219. package/prisma/migrations/20260201140637_add_web_config/migration.sql +22 -0
  220. package/prisma/migrations/20260201162232_add_session_progress/migration.sql +2 -0
  221. package/prisma/migrations/20260201174231_add_total_healed_count/migration.sql +2 -0
  222. package/prisma/migrations/migration_lock.toml +3 -0
  223. package/prisma/schema.prisma +210 -0
  224. package/schema.json +348 -0
  225. package/scripts/build-xenon.sh +32 -0
  226. package/scripts/dev/debug-gemini.ts +44 -0
  227. package/scripts/generate-types-from-schema.js +86 -0
  228. package/scripts/install-compatible-driver.js +39 -0
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ const chai_1 = require("chai");
49
+ // we are using custom plugin harness as we want to run two instance of xenon simultaneously
50
+ const plugin_harness_1 = require("../plugin-harness");
51
+ const webdriverio_1 = require("webdriverio");
52
+ const e2ehelper_1 = require("../e2ehelper");
53
+ const axios_1 = __importDefault(require("axios"));
54
+ const chai_as_promised_1 = __importDefault(require("chai-as-promised"));
55
+ const chai = __importStar(require("chai"));
56
+ const e2ehelper_2 = require("../e2ehelper");
57
+ chai.use(chai_as_promised_1.default);
58
+ let driver;
59
+ const WDIO_PARAMS = {
60
+ connectionRetryCount: 0,
61
+ hostname: e2ehelper_2.hub_config.bindHostOrIp,
62
+ port: e2ehelper_1.HUB_APPIUM_PORT,
63
+ logLevel: 'info',
64
+ path: '/',
65
+ };
66
+ let hubReady = false;
67
+ let nodeReady = false;
68
+ const capabilities = {
69
+ 'appium:automationName': 'UiAutomator2',
70
+ 'appium:app': 'https://prod-mobile-artefacts.lambdatest.com/assets/docs/proverbial_android.apk',
71
+ platformName: 'android',
72
+ 'appium:deviceName': '',
73
+ 'appium:uiautomator2ServerInstallTimeout': 90000,
74
+ };
75
+ const NEW_COMMAND_TIMEOUT_SECS = 10;
76
+ describe('E2E Forward Request', () => {
77
+ console.log('Before all');
78
+ // dump hub config into a file
79
+ const hub_config_file = (0, e2ehelper_1.ensureHubConfig)('android', 'real', 'real', {
80
+ newCommandTimeoutSec: NEW_COMMAND_TIMEOUT_SECS,
81
+ });
82
+ // dump node config into a file
83
+ const node_config_file = (0, e2ehelper_1.ensureNodeConfig)();
84
+ // setup appium home
85
+ const APPIUM_HOME = (0, e2ehelper_1.ensureAppiumHome)('hub', true);
86
+ const APPIUM_HOME_NODE = (0, e2ehelper_1.ensureAppiumHome)('node', true);
87
+ console.log(`Hub config file: ${hub_config_file}`);
88
+ // run hub
89
+ const hubProcess = (0, plugin_harness_1.pluginE2EHarness)({
90
+ before: undefined,
91
+ after: global.after,
92
+ configFile: hub_config_file,
93
+ pluginName: 'xenon',
94
+ host: e2ehelper_2.hub_config.bindHostOrIp,
95
+ port: e2ehelper_1.HUB_APPIUM_PORT,
96
+ driverSource: 'npm',
97
+ driverName: 'uiautomator2',
98
+ driverSpec: 'appium-uiautomator2-driver',
99
+ pluginSource: 'local',
100
+ pluginSpec: e2ehelper_1.PLUGIN_PATH,
101
+ appiumHome: APPIUM_HOME,
102
+ });
103
+ // run node
104
+ const nodeProcess = (0, plugin_harness_1.pluginE2EHarness)({
105
+ before: undefined,
106
+ after: global.after,
107
+ configFile: node_config_file,
108
+ pluginName: 'xenon',
109
+ port: e2ehelper_1.NODE_APPIUM_PORT,
110
+ host: e2ehelper_2.node_config.bindHostOrIp,
111
+ driverSource: 'npm',
112
+ driverName: 'uiautomator2',
113
+ driverSpec: 'appium-uiautomator2-driver',
114
+ pluginSource: 'local',
115
+ pluginSpec: e2ehelper_1.PLUGIN_PATH,
116
+ appiumHome: APPIUM_HOME_NODE,
117
+ });
118
+ function waitForHubAndNode() {
119
+ return __awaiter(this, void 0, void 0, function* () {
120
+ if (!hubReady) {
121
+ console.log('Waiting for hub to be ready');
122
+ yield hubProcess.startPlugin();
123
+ hubReady = true;
124
+ }
125
+ if (!nodeReady) {
126
+ console.log('Waiting for node to be ready');
127
+ yield nodeProcess.startPlugin();
128
+ nodeReady = true;
129
+ }
130
+ });
131
+ }
132
+ it('node can handle appium request on its own (hub still need to run)', () => __awaiter(void 0, void 0, void 0, function* () {
133
+ yield waitForHubAndNode();
134
+ const node_wdio_params = Object.assign({}, WDIO_PARAMS, {
135
+ hostname: e2ehelper_2.node_config.bindHostOrIp,
136
+ port: e2ehelper_1.NODE_APPIUM_PORT,
137
+ });
138
+ console.log(`Node wdio params: ${JSON.stringify(node_wdio_params)}`);
139
+ console.log(`node config: ${JSON.stringify(e2ehelper_2.node_config)}`);
140
+ driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, node_wdio_params), { capabilities }));
141
+ (0, chai_1.expect)(driver).to.be.not.undefined;
142
+ }));
143
+ it('can forward session request to node', () => __awaiter(void 0, void 0, void 0, function* () {
144
+ yield waitForHubAndNode();
145
+ if (e2ehelper_2.hub_config.bindHostOrIp == e2ehelper_2.node_config.bindHostOrIp) {
146
+ it.skip('node and hub should not be using the same host');
147
+ }
148
+ // hub and node should be running
149
+ const hub_url = `http://${e2ehelper_2.hub_config.bindHostOrIp}:${e2ehelper_1.HUB_APPIUM_PORT}`;
150
+ const node_url = `http://${e2ehelper_2.node_config.bindHostOrIp}:${e2ehelper_1.NODE_APPIUM_PORT}`;
151
+ console.log(`Hub url: ${hub_url}`);
152
+ (0, chai_1.expect)(axios_1.default.get(`${hub_url}/xenon`)).to.eventually.equal(200, 'hub should be running');
153
+ (0, chai_1.expect)(axios_1.default.get(`${node_url}/xenon`)).to.eventually.equal(200, 'node should be running');
154
+ // all devices
155
+ const allDevices = (yield axios_1.default.get(`http://${e2ehelper_2.hub_config.bindHostOrIp}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon/api/device`)).data;
156
+ // there should be at least one device
157
+ // expect(allDevices.length).to.be.greaterThan(0);
158
+ // wait until there is at least one device
159
+ // one of the device should come from node
160
+ const nodeDevice = allDevices.filter((device) => { var _a; return (_a = device.host) === null || _a === void 0 ? void 0 : _a.includes(e2ehelper_2.node_config.bindHostOrIp); });
161
+ (0, chai_1.expect)(nodeDevice).to.not.be.undefined;
162
+ console.log(`Node device: ${JSON.stringify(nodeDevice)}`);
163
+ driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities }));
164
+ // busy device should be on the node
165
+ const newAllDevices = (yield axios_1.default.get(`http://${e2ehelper_2.hub_config.bindHostOrIp}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon/api/device`)).data;
166
+ const busyDevice = newAllDevices.filter((device) => device.busy);
167
+ // device should have host as node_config.bindHostOrIp
168
+ (0, chai_1.expect)(busyDevice[0]).to.have.property('host').that.includes(e2ehelper_2.node_config.bindHostOrIp);
169
+ (0, chai_1.expect)(busyDevice[0]).to.have.property('host').that.not.includes(e2ehelper_2.hub_config.bindHostOrIp);
170
+ }));
171
+ it('update lastCmdExecutedAt when forwarding request', () => __awaiter(void 0, void 0, void 0, function* () {
172
+ yield waitForHubAndNode();
173
+ if (e2ehelper_2.hub_config.bindHostOrIp == e2ehelper_2.node_config.bindHostOrIp) {
174
+ it.skip('node and hub should not be using the same host');
175
+ }
176
+ driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities }));
177
+ const allDevices = (yield axios_1.default.get(`http://${e2ehelper_2.hub_config.bindHostOrIp}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon/api/device`)).data;
178
+ const busyDevice = allDevices.filter((device) => device.busy);
179
+ const lastCmdExecutedAt = busyDevice[0].lastCmdExecutedAt;
180
+ // lastCmdExecutedAt should not be empty
181
+ (0, chai_1.expect)(lastCmdExecutedAt).to.not.be.undefined;
182
+ // run a command
183
+ yield driver.getPageSource();
184
+ // check lastCmdExecutedAt
185
+ const newAllDevices = (yield axios_1.default.get(`http://${e2ehelper_2.hub_config.bindHostOrIp}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon/api/device`)).data;
186
+ const newBusyDevice = newAllDevices.filter((device) => device.udid === busyDevice[0].udid && device.host === busyDevice[0].host);
187
+ const newLastCmdExecutedAt = newBusyDevice[0].lastCmdExecutedAt;
188
+ // lastCmdExecutedAt should not be empty
189
+ (0, chai_1.expect)(newLastCmdExecutedAt).to.not.be.undefined;
190
+ // lastCmdExecutedAt should be greater than the previous one
191
+ (0, chai_1.expect)(newLastCmdExecutedAt).to.be.greaterThan(lastCmdExecutedAt);
192
+ // print out the device
193
+ console.log(`Busy device: ${JSON.stringify(newBusyDevice)}`);
194
+ }));
195
+ it.only('does not unblock device when cmd is sent before newCommandTimeoutSec', () => __awaiter(void 0, void 0, void 0, function* () {
196
+ yield waitForHubAndNode();
197
+ if (e2ehelper_2.hub_config.bindHostOrIp == e2ehelper_2.node_config.bindHostOrIp) {
198
+ it.skip('node and hub should not be using the same host');
199
+ }
200
+ driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities }));
201
+ const allDevices = (yield axios_1.default.get(`http://${e2ehelper_2.hub_config.bindHostOrIp}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon/api/device`)).data;
202
+ const busyDevice = allDevices.filter((device) => device.busy);
203
+ // keep sending command every 5 seconds for 20 seconds
204
+ const interval = setInterval(() => __awaiter(void 0, void 0, void 0, function* () {
205
+ yield driver.getPageSource();
206
+ }), 5000);
207
+ // wait for 20 seconds
208
+ yield new Promise((resolve) => setTimeout(resolve, (NEW_COMMAND_TIMEOUT_SECS + 10) * 1000));
209
+ clearInterval(interval);
210
+ // check device status
211
+ const newAllDevices = (yield axios_1.default.get(`http://${e2ehelper_2.hub_config.bindHostOrIp}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon/api/device`)).data;
212
+ const newBusyDevice = newAllDevices.filter((device) => device.udid === busyDevice[0].udid && device.host === busyDevice[0].host);
213
+ // device should be busy
214
+ (0, chai_1.expect)(newBusyDevice[0].busy).to.be.true;
215
+ }));
216
+ afterEach(function () {
217
+ return __awaiter(this, void 0, void 0, function* () {
218
+ if (driver !== undefined) {
219
+ yield driver.deleteSession();
220
+ driver = undefined;
221
+ }
222
+ });
223
+ });
224
+ });
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ const chai_1 = require("chai");
49
+ const ip_1 = __importDefault(require("ip"));
50
+ // we are using custom plugin harness as we want to run two instance of xenon simultaneously
51
+ const plugin_harness_1 = require("../plugin-harness");
52
+ const webdriverio_1 = require("webdriverio");
53
+ const e2ehelper_1 = require("../e2ehelper");
54
+ const axios_1 = __importDefault(require("axios"));
55
+ const chai_as_promised_1 = __importDefault(require("chai-as-promised"));
56
+ const chai = __importStar(require("chai"));
57
+ const mocha_1 = require("mocha");
58
+ chai.use(chai_as_promised_1.default);
59
+ let driver;
60
+ const APPIUM_HOST = ip_1.default.address();
61
+ const APPIUM_PORT = 4723;
62
+ const WDIO_PARAMS = {
63
+ connectionRetryCount: 0,
64
+ hostname: APPIUM_HOST,
65
+ port: APPIUM_PORT,
66
+ logLevel: 'info',
67
+ };
68
+ const capabilities = {
69
+ 'appium:automationName': 'UiAutomator2',
70
+ 'appium:app': 'https://prod-mobile-artefacts.lambdatest.com/assets/docs/proverbial_android.apk',
71
+ platformName: 'android',
72
+ 'appium:deviceName': '',
73
+ 'appium:uiautomator2ServerInstallTimeout': 90000,
74
+ };
75
+ let hubReady = false;
76
+ let nodeReady = false;
77
+ describe('E2E Hub and Node', () => {
78
+ console.log('Before all');
79
+ // dump hub config into a file
80
+ const hub_config_file = (0, e2ehelper_1.ensureHubConfig)('android', 'real', 'simulated');
81
+ // dump node config into a file
82
+ const node_config_file = (0, e2ehelper_1.ensureNodeConfig)();
83
+ // setup appium home
84
+ const APPIUM_HOME = (0, e2ehelper_1.ensureAppiumHome)('hub', true);
85
+ const APPIUM_HOME_NODE = (0, e2ehelper_1.ensureAppiumHome)('node', true);
86
+ console.log(`Hub config file: ${hub_config_file}`);
87
+ // run hub
88
+ const hubProcess = (0, plugin_harness_1.pluginE2EHarness)({
89
+ before: undefined,
90
+ after: global.after,
91
+ configFile: hub_config_file,
92
+ pluginName: 'xenon',
93
+ host: e2ehelper_1.hub_config.bindHostOrIp,
94
+ port: e2ehelper_1.HUB_APPIUM_PORT,
95
+ driverSource: 'npm',
96
+ driverName: 'uiautomator2',
97
+ driverSpec: 'appium-uiautomator2-driver',
98
+ pluginSource: 'local',
99
+ pluginSpec: e2ehelper_1.PLUGIN_PATH,
100
+ appiumHome: APPIUM_HOME,
101
+ });
102
+ // run node
103
+ const nodeProcess = (0, plugin_harness_1.pluginE2EHarness)({
104
+ before: undefined,
105
+ after: global.after,
106
+ configFile: node_config_file,
107
+ pluginName: 'xenon',
108
+ port: e2ehelper_1.NODE_APPIUM_PORT,
109
+ host: e2ehelper_1.node_config.bindHostOrIp,
110
+ driverSource: 'npm',
111
+ driverName: 'uiautomator2',
112
+ driverSpec: 'appium-uiautomator2-driver',
113
+ pluginSource: 'local',
114
+ pluginSpec: e2ehelper_1.PLUGIN_PATH,
115
+ appiumHome: APPIUM_HOME_NODE,
116
+ });
117
+ function waitForHubAndNode() {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ if (!hubReady) {
120
+ console.log('Waiting for hub to be ready');
121
+ yield hubProcess.startPlugin();
122
+ hubReady = true;
123
+ }
124
+ if (!nodeReady) {
125
+ console.log('Waiting for node to be ready');
126
+ yield nodeProcess.startPlugin();
127
+ nodeReady = true;
128
+ }
129
+ });
130
+ }
131
+ it('should have devices on the hub', () => __awaiter(void 0, void 0, void 0, function* () {
132
+ yield waitForHubAndNode();
133
+ // check xenon endpoint using axios
134
+ const res = yield axios_1.default.get(`http://${APPIUM_HOST}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon/api/device`);
135
+ (0, chai_1.expect)(res.status).to.equal(200);
136
+ (0, chai_1.expect)(res.data.length).to.be.greaterThan(0);
137
+ // one of the devices should be an android device from the node
138
+ const androidDevices = res.data.filter((device) => device.platform === 'android');
139
+ (0, chai_1.expect)(androidDevices.length).to.be.greaterThan(0);
140
+ const nodeAndroidDevices = androidDevices.filter((device) => device.host.includes(e2ehelper_1.NODE_APPIUM_PORT.toString()) &&
141
+ device.host.includes(e2ehelper_1.node_config.bindHostOrIp));
142
+ (0, chai_1.expect)(nodeAndroidDevices.length).to.be.greaterThan(0);
143
+ }));
144
+ it('Vertical swipe test', () => __awaiter(void 0, void 0, void 0, function* () {
145
+ yield waitForHubAndNode();
146
+ driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities }));
147
+ console.log(`Device UDID: ${yield driver.capabilities.deviceUDID}`);
148
+ yield driver.performActions([
149
+ {
150
+ type: 'pointer',
151
+ id: 'finger1',
152
+ parameters: { pointerType: 'touch' },
153
+ actions: [
154
+ { type: 'pointerMove', duration: 0, x: 100, y: 100 },
155
+ { type: 'pointerDown', button: 0 },
156
+ { type: 'pause', duration: 500 },
157
+ { type: 'pointerMove', duration: 1000, origin: 'pointer', x: -50, y: 0 },
158
+ { type: 'pointerUp', button: 0 },
159
+ ],
160
+ },
161
+ ]);
162
+ console.log('Successfully swiped');
163
+ }));
164
+ it('serve xenon endpoint when test is still running', () => __awaiter(void 0, void 0, void 0, function* () {
165
+ yield waitForHubAndNode();
166
+ driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities }));
167
+ // check xenon endpoint using axios
168
+ const res = yield axios_1.default.get(`http://${APPIUM_HOST}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon`);
169
+ (0, chai_1.expect)(res.status).to.equal(200);
170
+ }));
171
+ it('Clean pending session when session failed to start', () => __awaiter(void 0, void 0, void 0, function* () {
172
+ yield waitForHubAndNode();
173
+ // ask appium to launch non-existent app package and app activity
174
+ const nonExistentAppCapabilities = {
175
+ 'appium:automationName': 'UiAutomator2',
176
+ 'appium:appPackage': 'com.nonexistent',
177
+ 'appium:appActivity': 'com.nonexistent.MainActivity',
178
+ platformName: 'android',
179
+ 'appium:deviceName': '',
180
+ 'appium:uiautomator2ServerInstallTimeout': 90000,
181
+ };
182
+ yield (0, chai_1.expect)((0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities: nonExistentAppCapabilities }))).to.eventually.be.rejected;
183
+ // check xenon endpoint using axios: /api/queue/length
184
+ const res = yield axios_1.default.get(`http://${APPIUM_HOST}:${e2ehelper_1.HUB_APPIUM_PORT}/xenon/api/queue/length`);
185
+ (0, chai_1.expect)(res.status).to.equal(200);
186
+ (0, chai_1.expect)(res.data).to.equal(0);
187
+ }));
188
+ it('Propagate error when session failed to be created', () => __awaiter(void 0, void 0, void 0, function* () {
189
+ yield waitForHubAndNode();
190
+ // ask appium to launch non-existent app package and app activity
191
+ const nonExistentAppCapabilities = {
192
+ 'appium:automationName': 'UiAutomator2',
193
+ 'appium:appPackage': 'com.nonexistent',
194
+ 'appium:appActivity': 'com.nonexistent.MainActivity',
195
+ platformName: 'android',
196
+ 'appium:deviceName': '',
197
+ 'appium:uiautomator2ServerInstallTimeout': 90000,
198
+ };
199
+ yield (0, chai_1.expect)((0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities: nonExistentAppCapabilities }))).to.eventually.be.rejectedWith("An unknown server-side error occurred while processing the command. Original error: Error: Either provide 'app' option to install 'com.nonexistent' or consider setting 'noReset' to 'true' if 'com.nonexistent' is supposed to be preinstalled.");
200
+ }));
201
+ (0, mocha_1.afterEach)(function () {
202
+ return __awaiter(this, void 0, void 0, function* () {
203
+ try {
204
+ if (driver !== undefined) {
205
+ yield driver.deleteSession();
206
+ driver = undefined;
207
+ }
208
+ }
209
+ catch (err) {
210
+ console.log('Unable to delete driver session');
211
+ }
212
+ });
213
+ });
214
+ });
@@ -0,0 +1,39 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const webdriverio_1 = require("webdriverio");
13
+ const APPIUM_HOST = 'localhost';
14
+ const APPIUM_PORT = 31337;
15
+ const WDIO_PARAMS = {
16
+ hostname: APPIUM_HOST,
17
+ port: APPIUM_PORT,
18
+ path: '/wd/hub/',
19
+ logLevel: 'info',
20
+ };
21
+ const capabilities = {
22
+ platformName: 'iOS',
23
+ 'appium:automationName': 'XCUITest',
24
+ 'appium:iPhoneOnly': true,
25
+ 'appium:app': '/Users/saikrishna/Downloads/git/AppiumTestDistribution/apps/VodQAReactNative.zip',
26
+ 'appium:usePrebuiltWDA': true,
27
+ };
28
+ describe('Plugin1 Test', () => {
29
+ let driver;
30
+ beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
31
+ driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities }));
32
+ }));
33
+ it('iOS veritical swipe', () => __awaiter(void 0, void 0, void 0, function* () {
34
+ yield driver.$('~login').click();
35
+ yield driver.$('~verticalSwipe').click();
36
+ yield driver.pause(2000);
37
+ }));
38
+ afterEach(() => __awaiter(void 0, void 0, void 0, function* () { return yield driver.deleteSession(); }));
39
+ });
@@ -0,0 +1,39 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const webdriverio_1 = require("webdriverio");
13
+ const APPIUM_HOST = 'localhost';
14
+ const APPIUM_PORT = 4723;
15
+ const WDIO_PARAMS = {
16
+ connectionRetryCount: 220000,
17
+ hostname: APPIUM_HOST,
18
+ port: APPIUM_PORT,
19
+ path: '/wd/hub/',
20
+ logLevel: 'info',
21
+ };
22
+ const capabilities = {
23
+ platformName: 'iOS',
24
+ 'appium:automationName': 'XCUITest',
25
+ 'appium:iPhoneOnly': true,
26
+ 'appium:app': 'https://github.com/AppiumTestDistribution/appium-demo/blob/main/vodqa.zip?raw=true',
27
+ };
28
+ describe('Plugin1 Test', () => {
29
+ let driver;
30
+ beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
31
+ driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities }));
32
+ }));
33
+ it('iOS veritical swipe', () => __awaiter(void 0, void 0, void 0, function* () {
34
+ yield driver.$('~login').click();
35
+ yield driver.$('~verticalSwipe').click();
36
+ yield driver.pause(2000);
37
+ }));
38
+ afterEach(() => __awaiter(void 0, void 0, void 0, function* () { return yield driver.deleteSession(); }));
39
+ });