@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.
- package/README.md +446 -0
- package/lib/package.json +207 -0
- package/lib/public/assets/Layouts-7IT8aFLI.js +11 -0
- package/lib/public/assets/Layouts-DPMls9vh.css +1 -0
- package/lib/public/assets/ai-settings-BbnfgdEx.js +11 -0
- package/lib/public/assets/apps-CRMrI4_p.js +16 -0
- package/lib/public/assets/apps-CcM77dgg.css +1 -0
- package/lib/public/assets/badge-B1nKs8zj.css +1 -0
- package/lib/public/assets/badge-CSvl5xIU.js +11 -0
- package/lib/public/assets/button-CJlKn4PZ.css +1 -0
- package/lib/public/assets/button-CvLaGFYj.js +26 -0
- package/lib/public/assets/calendar-6w-D6Oaw.js +6 -0
- package/lib/public/assets/clock-DcdeWBPr.js +6 -0
- package/lib/public/assets/cpu-DiSoXT9n.js +6 -0
- package/lib/public/assets/device-explorer-CajM63OJ.js +193 -0
- package/lib/public/assets/device-explorer-CxdUAoTL.css +1 -0
- package/lib/public/assets/index-ByQwMN5T.js +174 -0
- package/lib/public/assets/index-C1DBaoSh.js +1 -0
- package/lib/public/assets/index-qzCez_kk.css +1 -0
- package/lib/public/assets/lock-B23ibZmo.js +6 -0
- package/lib/public/assets/maintenance-settings-CirzA6yG.js +6 -0
- package/lib/public/assets/mouse-pointer-2-Cz76SHFb.js +6 -0
- package/lib/public/assets/plus-BBwlIevt.js +6 -0
- package/lib/public/assets/session-dashboard-C2k7FFv_.css +1 -0
- package/lib/public/assets/session-dashboard-HPDtwPOZ.js +62 -0
- package/lib/public/assets/settings-DrZsZwdc.js +1 -0
- package/lib/public/assets/trash-2-DQpvzJec.js +6 -0
- package/lib/public/assets/useSocket-Dxsqae2a.js +16 -0
- package/lib/public/assets/webhook-settings-CDPgsgkb.css +1 -0
- package/lib/public/assets/webhook-settings-Cp-B4Nrw.js +1 -0
- package/lib/public/assets/zap-DovP6iow.js +6 -0
- package/lib/public/favicon.ico +0 -0
- package/lib/public/favicon.png +0 -0
- package/lib/public/favicon.svg +9 -0
- package/lib/public/index.html +46 -0
- package/lib/public/logo.svg +17 -0
- package/lib/public/logo192.png +0 -0
- package/lib/public/logo512.png +0 -0
- package/lib/public/manifest.json +25 -0
- package/lib/public/robots.txt +3 -0
- package/lib/schema.json +348 -0
- package/lib/src/InternalHttpClient.js +212 -0
- package/lib/src/PluginContext.js +29 -0
- package/lib/src/XenonCapabilityManager.js +199 -0
- package/lib/src/app/index.js +167 -0
- package/lib/src/app/routers/apps.js +79 -0
- package/lib/src/app/routers/config.js +131 -0
- package/lib/src/app/routers/control.js +835 -0
- package/lib/src/app/routers/dashboard.js +301 -0
- package/lib/src/app/routers/grid.js +352 -0
- package/lib/src/app/routers/reservation.js +190 -0
- package/lib/src/app/routers/webhook.js +83 -0
- package/lib/src/app/swagger-docs.js +203 -0
- package/lib/src/app/swagger.js +366 -0
- package/lib/src/chromeUtils.js +148 -0
- package/lib/src/commands/handle.js +19 -0
- package/lib/src/commands/index.js +8 -0
- package/lib/src/config.js +73 -0
- package/lib/src/dashboard/asset-manager.js +84 -0
- package/lib/src/dashboard/commands.js +284 -0
- package/lib/src/dashboard/event-manager.js +699 -0
- package/lib/src/dashboard/services/app-service.js +134 -0
- package/lib/src/dashboard/services/failure-analysis-service.js +173 -0
- package/lib/src/dashboard/services/session-service.js +113 -0
- package/lib/src/data-service/CircuitBreaker.js +83 -0
- package/lib/src/data-service/config-service.js +155 -0
- package/lib/src/data-service/db.js +122 -0
- package/lib/src/data-service/device-service.js +320 -0
- package/lib/src/data-service/device-store.interface.js +2 -0
- package/lib/src/data-service/device-store.js +345 -0
- package/lib/src/data-service/pending-sessions-service.js +25 -0
- package/lib/src/data-service/pluginArgs.js +25 -0
- package/lib/src/data-service/prisma-service.js +31 -0
- package/lib/src/data-service/prisma-store.js +385 -0
- package/lib/src/data-service/queue-service.js +150 -0
- package/lib/src/data-service/web-config-service.js +130 -0
- package/lib/src/device-managers/AndroidDeviceManager.js +1155 -0
- package/lib/src/device-managers/ChromeDriverManager.js +68 -0
- package/lib/src/device-managers/HealthMonitorService.js +325 -0
- package/lib/src/device-managers/IOSDeviceManager.js +351 -0
- package/lib/src/device-managers/NodeDevices.js +82 -0
- package/lib/src/device-managers/android/AndroidStreamService.js +370 -0
- package/lib/src/device-managers/android/DeviceLockManager.js +45 -0
- package/lib/src/device-managers/cloud/CapabilityManager.js +26 -0
- package/lib/src/device-managers/cloud/Devices.js +86 -0
- package/lib/src/device-managers/iOSTracker.js +44 -0
- package/lib/src/device-managers/index.js +89 -0
- package/lib/src/device-managers/ios/IOSDiscoveryService.js +268 -0
- package/lib/src/device-managers/ios/IOSStreamService.js +893 -0
- package/lib/src/device-managers/ios/WDAClient.js +866 -0
- package/lib/src/device-utils.js +663 -0
- package/lib/src/enums/Capabilities.js +8 -0
- package/lib/src/enums/Cloud.js +11 -0
- package/lib/src/enums/Platform.js +9 -0
- package/lib/src/enums/SessionType.js +9 -0
- package/lib/src/enums/SocketEvents.js +15 -0
- package/lib/src/helpers/UniversalMjpegProxy.js +273 -0
- package/lib/src/helpers/index.js +229 -0
- package/lib/src/index.js +95 -0
- package/lib/src/interceptors/CommandInterceptor.js +524 -0
- package/lib/src/interfaces/ICloudManager.js +2 -0
- package/lib/src/interfaces/IDevice.js +2 -0
- package/lib/src/interfaces/IDeviceFilterOptions.js +2 -0
- package/lib/src/interfaces/IDeviceManager.js +2 -0
- package/lib/src/interfaces/IOptions.js +2 -0
- package/lib/src/interfaces/IPluginArgs.js +55 -0
- package/lib/src/interfaces/ISessionCapability.js +2 -0
- package/lib/src/logger.js +225 -0
- package/lib/src/plugin.js +244 -0
- package/lib/src/prisma.js +12 -0
- package/lib/src/profiling/AndroidAppProfiler.js +213 -0
- package/lib/src/proxy/wd-command-proxy.js +221 -0
- package/lib/src/scripts/generate-database-migration.js +59 -0
- package/lib/src/scripts/initialize-database.js +55 -0
- package/lib/src/scripts/install-go-ios.js +66 -0
- package/lib/src/scripts/prepare-prisma.js +89 -0
- package/lib/src/services/AICommandService.js +143 -0
- package/lib/src/services/AIService.js +466 -0
- package/lib/src/services/CleanupService.js +141 -0
- package/lib/src/services/EventBus.js +74 -0
- package/lib/src/services/InspectorService.js +395 -0
- package/lib/src/services/MetricsService.js +134 -0
- package/lib/src/services/NetworkConditioningService.js +173 -0
- package/lib/src/services/NotificationService.js +163 -0
- package/lib/src/services/RequestLogService.js +252 -0
- package/lib/src/services/ResourceIsolationService.js +122 -0
- package/lib/src/services/SecurityService.js +120 -0
- package/lib/src/services/ServerManager.js +284 -0
- package/lib/src/services/SessionHeartbeatService.js +158 -0
- package/lib/src/services/SessionLifecycleService.js +572 -0
- package/lib/src/services/SocketClient.js +71 -0
- package/lib/src/services/SocketServer.js +87 -0
- package/lib/src/services/TracingService.js +132 -0
- package/lib/src/services/VideoPipelineService.js +220 -0
- package/lib/src/services/healing/FuzzyXmlHealingProvider.js +333 -0
- package/lib/src/services/healing/HealEtalonService.js +98 -0
- package/lib/src/services/healing/HealedLocatorGenerator.js +132 -0
- package/lib/src/services/healing/HealingOrchestrator.js +165 -0
- package/lib/src/services/healing/LlmHealingProvider.js +77 -0
- package/lib/src/services/healing/OcrHealingProvider.js +119 -0
- package/lib/src/services/healing/ResilioTreeHealingProvider.js +100 -0
- package/lib/src/services/healing/VisualAiHealingProvider.js +90 -0
- package/lib/src/services/healing/types.js +12 -0
- package/lib/src/services/omni-vision/OmniVisionService.js +718 -0
- package/lib/src/services/omni-vision/VisionAssertionService.js +68 -0
- package/lib/src/sessions/CloudSession.js +42 -0
- package/lib/src/sessions/LocalSession.js +313 -0
- package/lib/src/sessions/RemoteSession.js +287 -0
- package/lib/src/sessions/SessionManager.js +238 -0
- package/lib/src/sessions/XenonSession.js +44 -0
- package/lib/src/types/CLIArgs.js +2 -0
- package/lib/src/types/CloudArgs.js +2 -0
- package/lib/src/types/CloudSchema.js +131 -0
- package/lib/src/types/DeviceType.js +2 -0
- package/lib/src/types/DeviceUpdate.js +2 -0
- package/lib/src/types/IOSDevice.js +2 -0
- package/lib/src/types/Platform.js +2 -0
- package/lib/src/types/SessionStatus.js +11 -0
- package/lib/src/validators/CapabilityValidator.js +93 -0
- package/lib/test/e2e/android/conf.spec.js +43 -0
- package/lib/test/e2e/android/conf2.spec.js +44 -0
- package/lib/test/e2e/android/conf3.spec.js +44 -0
- package/lib/test/e2e/e2ehelper.js +113 -0
- package/lib/test/e2e/hubnode/forward-request.spec.js +224 -0
- package/lib/test/e2e/hubnode/hubnode.spec.js +214 -0
- package/lib/test/e2e/ios/conf1.spec.js +39 -0
- package/lib/test/e2e/ios/conf2.spec.js +39 -0
- package/lib/test/e2e/plugin-harness.js +236 -0
- package/lib/test/e2e/plugin.spec.js +97 -0
- package/lib/test/e2e/telemetry_verification.spec.js +83 -0
- package/lib/test/e2e/video-recording-test.spec.js +63 -0
- package/lib/test/helpers/test-container.js +112 -0
- package/lib/test/integration/androidDevices.spec.js +137 -0
- package/lib/test/integration/cliArgs.js +73 -0
- package/lib/test/integration/ios/01iOSSimulator.spec.js +291 -0
- package/lib/test/integration/ios/02iOSDevices.spec.js +75 -0
- package/lib/test/integration/testHelpers.js +74 -0
- package/lib/test/unit/AndroidDeviceManager.spec.js +178 -0
- package/lib/test/unit/ChromeDriverManager.spec.js +26 -0
- package/lib/test/unit/CleanupService.spec.js +21 -0
- package/lib/test/unit/DeviceModel.spec.js +157 -0
- package/lib/test/unit/FuzzyXmlHealingProvider.test.js +294 -0
- package/lib/test/unit/GetAdbOriginal.js +42 -0
- package/lib/test/unit/HealingCascade.test.js +128 -0
- package/lib/test/unit/IOSDeviceManager.spec.js +261 -0
- package/lib/test/unit/RemoteIOs.spec.js +78 -0
- package/lib/test/unit/ResilioTreeHealingProvider.test.js +96 -0
- package/lib/test/unit/commands.spec.js +27 -0
- package/lib/test/unit/config.spec.js +27 -0
- package/lib/test/unit/device-service.spec.js +307 -0
- package/lib/test/unit/device-utils.spec.js +313 -0
- package/lib/test/unit/fixtures/device.config.js +4 -0
- package/lib/test/unit/fixtures/devices.js +89 -0
- package/lib/test/unit/helpers.spec.js +62 -0
- package/lib/test/unit/omni-vision.spec.js +100 -0
- package/lib/test/unit/plugin.spec.js +133 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +207 -0
- package/prisma/data.db +0 -0
- package/prisma/dev.db +0 -0
- package/prisma/dev.db-journal +0 -0
- package/prisma/migrations/20231011074725_initial_tables/migration.sql +47 -0
- package/prisma/migrations/20231226115334_update_session_log/migration.sql +2 -0
- package/prisma/migrations/20251204113710_add_video_recording_enabled/migration.sql +29 -0
- package/prisma/migrations/20251204132449_add_log_table/migration.sql +11 -0
- package/prisma/migrations/20251205050111_add_profiling_support/migration.sql +47 -0
- package/prisma/migrations/20251205050947_add_is_error_field/migration.sql +24 -0
- package/prisma/migrations/20260126201337_add_app_model/migration.sql +18 -0
- package/prisma/migrations/20260130115722_add_performance_trace_and_xenon_sync/migration.sql +2 -0
- package/prisma/migrations/20260130135114_add_device_models/migration.sql +57 -0
- package/prisma/migrations/20260130140655_make_systemport_optional/migration.sql +45 -0
- package/prisma/migrations/20260130140932_make_device_fields_optional/migration.sql +45 -0
- package/prisma/migrations/20260130141040_final_schema_fix/migration.sql +45 -0
- package/prisma/migrations/20260130143234_add_device_health_fields/migration.sql +4 -0
- package/prisma/migrations/20260130144921_add_failure_category/migration.sql +2 -0
- package/prisma/migrations/20260131151456_add_webhook_config/migration.sql +10 -0
- package/prisma/migrations/20260201094507_add_device_tags/migration.sql +11 -0
- package/prisma/migrations/20260201103410_add_managed_process/migration.sql +15 -0
- package/prisma/migrations/20260201140637_add_web_config/migration.sql +22 -0
- package/prisma/migrations/20260201162232_add_session_progress/migration.sql +2 -0
- package/prisma/migrations/20260201174231_add_total_healed_count/migration.sql +2 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +210 -0
- package/schema.json +348 -0
- package/scripts/build-xenon.sh +32 -0
- package/scripts/dev/debug-gemini.ts +44 -0
- package/scripts/generate-types-from-schema.js +86 -0
- package/scripts/install-compatible-driver.js +39 -0
|
@@ -0,0 +1,236 @@
|
|
|
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.pluginE2EHarness = pluginE2EHarness;
|
|
16
|
+
const support_1 = require("appium/support");
|
|
17
|
+
const get_port_1 = __importDefault(require("get-port"));
|
|
18
|
+
const log_symbols_1 = require("log-symbols");
|
|
19
|
+
const teen_process_1 = require("teen_process");
|
|
20
|
+
const path_1 = __importDefault(require("path"));
|
|
21
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
22
|
+
const fs_1 = __importDefault(require("fs"));
|
|
23
|
+
const ip_1 = __importDefault(require("ip"));
|
|
24
|
+
const axios_1 = __importDefault(require("axios"));
|
|
25
|
+
/**
|
|
26
|
+
* Creates hooks to install a driver and a plugin and starts an Appium server w/ the given extensions.
|
|
27
|
+
* @param {E2ESetupOpts} opts
|
|
28
|
+
* @returns {void}
|
|
29
|
+
*/
|
|
30
|
+
function pluginE2EHarness(opts) {
|
|
31
|
+
let { port } = opts;
|
|
32
|
+
const { appiumHome, before, after, configFile, driverSource, driverPackage, driverName, driverSpec, pluginSource, pluginPackage, pluginSpec, pluginName, host, enableGoIos, } = opts;
|
|
33
|
+
let server;
|
|
34
|
+
function goIosPath() {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const appium_path = path_1.default.dirname(require.resolve('appium'));
|
|
37
|
+
console.log(`${log_symbols_1.info} appium_path: ${appium_path}`);
|
|
38
|
+
const node_modules_root = (yield (0, teen_process_1.exec)('npm', ['root', '-g'])).stdout.trim();
|
|
39
|
+
console.log(`${log_symbols_1.info} node_modules_root: ${node_modules_root}`);
|
|
40
|
+
const platform_name = process.platform;
|
|
41
|
+
const arch_name = process.arch;
|
|
42
|
+
const go_ios_dir = path_1.default.join(node_modules_root, 'go-ios');
|
|
43
|
+
// find ios binary matching platform name
|
|
44
|
+
let go_ios_bin = fs_1.default.readdirSync(go_ios_dir, { recursive: true }).find((item) => {
|
|
45
|
+
console.log(`${log_symbols_1.info} item: ${item}`);
|
|
46
|
+
return item.includes(platform_name);
|
|
47
|
+
});
|
|
48
|
+
console.log(`${log_symbols_1.info} platform: ${platform_name} arch: ${arch_name} go_ios_bin: ${go_ios_bin}`);
|
|
49
|
+
if (!go_ios_bin) {
|
|
50
|
+
// throw new Error(`go-ios binary not found for platform ${platform_name}`);
|
|
51
|
+
go_ios_bin = '';
|
|
52
|
+
console.log(`${log_symbols_1.warning} go-ios binary not found for platform ${platform_name}`);
|
|
53
|
+
}
|
|
54
|
+
const full_path = path_1.default.join(go_ios_dir, go_ios_bin.toString(), 'ios');
|
|
55
|
+
return full_path;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// return appium binary path based on APPIUM_HOME
|
|
59
|
+
function getAppiumBin() {
|
|
60
|
+
return require.resolve('appium');
|
|
61
|
+
}
|
|
62
|
+
function startPlugin() {
|
|
63
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
64
|
+
const setupAppiumHome = () => __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
/**
|
|
66
|
+
* @type {AppiumEnv}
|
|
67
|
+
*/
|
|
68
|
+
const env = Object.assign({}, process.env);
|
|
69
|
+
if (appiumHome) {
|
|
70
|
+
env.APPIUM_HOME = appiumHome;
|
|
71
|
+
//env.HOME = appiumHome;
|
|
72
|
+
yield support_1.fs.mkdirp(appiumHome);
|
|
73
|
+
console.log(`${log_symbols_1.info} Set \`APPIUM_HOME\` to ${appiumHome}`);
|
|
74
|
+
}
|
|
75
|
+
// find go_ios from npm
|
|
76
|
+
if (enableGoIos)
|
|
77
|
+
env.GO_IOS = yield goIosPath();
|
|
78
|
+
return env;
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
*
|
|
82
|
+
* @param {AppiumEnv} env
|
|
83
|
+
*/
|
|
84
|
+
const installDriver = (env) => __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
var _a;
|
|
86
|
+
const APPIUM_BIN = getAppiumBin();
|
|
87
|
+
console.log(`${log_symbols_1.info} Checking if driver "${driverName}" is installed...`);
|
|
88
|
+
const driverListArgs = [APPIUM_BIN, 'driver', 'list', '--json'];
|
|
89
|
+
console.log(`${log_symbols_1.info} Running: ${process.execPath} ${driverListArgs.join(' ')}`);
|
|
90
|
+
const { stdout: driverListJson } = yield (0, teen_process_1.exec)(process.execPath, driverListArgs, {
|
|
91
|
+
env,
|
|
92
|
+
});
|
|
93
|
+
const installedDrivers = JSON.parse(driverListJson);
|
|
94
|
+
if (!((_a = installedDrivers[driverName]) === null || _a === void 0 ? void 0 : _a.installed)) {
|
|
95
|
+
console.log(`${log_symbols_1.warning} Driver "${driverName}" not installed; installing...`);
|
|
96
|
+
const driverArgs = [APPIUM_BIN, 'driver', 'install', '--source', driverSource, driverSpec];
|
|
97
|
+
if (driverPackage) {
|
|
98
|
+
driverArgs.push('--package', driverPackage);
|
|
99
|
+
}
|
|
100
|
+
console.log(`${log_symbols_1.info} Running: ${process.execPath} ${driverArgs.join(' ')}`);
|
|
101
|
+
yield (0, teen_process_1.exec)(process.execPath, driverArgs, {
|
|
102
|
+
env,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
console.log(`${log_symbols_1.success} Installed driver "${driverName}"`);
|
|
106
|
+
});
|
|
107
|
+
function removePluginFromExtensionsYaml(env) {
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
const extensionsYaml = path_1.default.join(env.APPIUM_HOME, 'node_modules', '.cache', 'appium', 'extensions.yaml');
|
|
110
|
+
console.log(`${log_symbols_1.info} Removing plugin "${pluginName}" from ${extensionsYaml}`);
|
|
111
|
+
const extensions = js_yaml_1.default.load(fs_1.default.readFileSync(extensionsYaml, 'utf8'));
|
|
112
|
+
delete extensions.plugins[pluginName];
|
|
113
|
+
console.log(`${log_symbols_1.info} Writing back to ${extensionsYaml}`);
|
|
114
|
+
fs_1.default.writeFileSync(extensionsYaml, js_yaml_1.default.dump(extensions));
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
*
|
|
119
|
+
* @param {AppiumEnv} env
|
|
120
|
+
*/
|
|
121
|
+
const installPlugin = (env) => __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
/*const availablePlugins = await installedPluginsByAppiumCommands(env);
|
|
123
|
+
console.log(`${info} Available plugins: ${JSON.stringify(Object.keys(availablePlugins), null, 2)}`);
|
|
124
|
+
const installedPlugins = Object.keys(availablePlugins).map((item) => availablePlugins[item]).filter((p: any) => p.installed);
|
|
125
|
+
console.log(`${info} Installed plugin: ${JSON.stringify(installedPlugins, null, 2)}`);
|
|
126
|
+
*/
|
|
127
|
+
// same plugin maybe installed via different source: npm or local
|
|
128
|
+
// we don't care, just remove it and write it back to the file
|
|
129
|
+
yield removePluginFromExtensionsYaml(env);
|
|
130
|
+
// installing our version of plugin
|
|
131
|
+
const pluginArgs = [
|
|
132
|
+
getAppiumBin(),
|
|
133
|
+
'plugin',
|
|
134
|
+
'install',
|
|
135
|
+
'--source',
|
|
136
|
+
pluginSource,
|
|
137
|
+
pluginSpec,
|
|
138
|
+
];
|
|
139
|
+
// only aplicable for npm
|
|
140
|
+
if (pluginPackage) {
|
|
141
|
+
pluginArgs.push('--package', pluginPackage);
|
|
142
|
+
}
|
|
143
|
+
console.log(`${log_symbols_1.info} Installing plugin: ${process.execPath} ${pluginArgs.join(' ')}`);
|
|
144
|
+
yield (0, teen_process_1.exec)(process.execPath, pluginArgs, { env });
|
|
145
|
+
console.log(`${log_symbols_1.success} Installed plugin "${pluginName}"`);
|
|
146
|
+
});
|
|
147
|
+
const createServer = () => __awaiter(this, void 0, void 0, function* () {
|
|
148
|
+
if (!port) {
|
|
149
|
+
port = yield (0, get_port_1.default)();
|
|
150
|
+
}
|
|
151
|
+
console.log(`${log_symbols_1.info} Will use port ${port} for Appium server`);
|
|
152
|
+
// here we are using CLI (instead of AppiumServer) to prevent schema conflicts
|
|
153
|
+
yield runAppiumServerFromCli(env, [pluginName], [driverName], configFile);
|
|
154
|
+
// use axios to wait until port is returning 200 OK
|
|
155
|
+
console.log(`${log_symbols_1.info} Waiting for Appium server to be ready...`);
|
|
156
|
+
});
|
|
157
|
+
function runAppiumServerFromCli(env_1) {
|
|
158
|
+
return __awaiter(this, arguments, void 0, function* (env, usePlugins = [], useDrivers = [], configFile = '') {
|
|
159
|
+
/**
|
|
160
|
+
example:
|
|
161
|
+
appium server -ka 800 \
|
|
162
|
+
--use-plugins=xenon,appium-dashboard \
|
|
163
|
+
--relaxed-security \
|
|
164
|
+
--allow-insecure chromedriver_autodownload,execute_driver_script,adb_shell \
|
|
165
|
+
--config ./hub-config.json \
|
|
166
|
+
-pa /wd/hub
|
|
167
|
+
*/
|
|
168
|
+
const APPIUM_BIN = getAppiumBin();
|
|
169
|
+
const serverArgs = [APPIUM_BIN, 'server', '-ka', '800'];
|
|
170
|
+
if (usePlugins.length > 0) {
|
|
171
|
+
serverArgs.push(`--use-plugins=${usePlugins.join(',')}`);
|
|
172
|
+
}
|
|
173
|
+
if (useDrivers.length > 0) {
|
|
174
|
+
serverArgs.push(`--use-drivers=${useDrivers.join(',')}`);
|
|
175
|
+
}
|
|
176
|
+
if (configFile) {
|
|
177
|
+
serverArgs.push(`--config=${configFile}`);
|
|
178
|
+
}
|
|
179
|
+
console.log(`APPIUM_HOME=${env.APPIUM_HOME} GO_IOS=${env.GO_IOS}`);
|
|
180
|
+
console.log(`${log_symbols_1.info} Running: ${process.execPath} ${serverArgs.join(' ')}`);
|
|
181
|
+
(0, teen_process_1.exec)(process.execPath, serverArgs, {
|
|
182
|
+
env,
|
|
183
|
+
});
|
|
184
|
+
return waitServer(host !== null && host !== void 0 ? host : ip_1.default.address(), port !== null && port !== void 0 ? port : 4723, 60);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// Use axios to hit appium endpoint until it returns 200 OK
|
|
188
|
+
function waitServer(host, port, timeoutSeconds) {
|
|
189
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
190
|
+
// const basePath = serverArgs.basePath || '';
|
|
191
|
+
const url = `http://${host}:${port}/status`;
|
|
192
|
+
const timeout = timeoutSeconds * 1000;
|
|
193
|
+
const start = Date.now();
|
|
194
|
+
while (Date.now() - start < timeout) {
|
|
195
|
+
try {
|
|
196
|
+
yield axios_1.default.get(url);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
catch (ign) {
|
|
200
|
+
// ignore
|
|
201
|
+
console.log(`${log_symbols_1.info} url: ${url} error: ${ign.message}`);
|
|
202
|
+
}
|
|
203
|
+
yield new Promise((resolve) => setTimeout(resolve, 1000));
|
|
204
|
+
}
|
|
205
|
+
throw new Error(`Appium server did not start after ${timeoutSeconds} seconds`);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
const env = yield setupAppiumHome();
|
|
209
|
+
yield installDriver(env);
|
|
210
|
+
yield installPlugin(env);
|
|
211
|
+
yield createServer();
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function stopPlugin() {
|
|
215
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
216
|
+
if (server) {
|
|
217
|
+
yield server.close();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// clean it after test
|
|
222
|
+
after(stopPlugin);
|
|
223
|
+
// have an option to start the plugin before the test manually
|
|
224
|
+
// this is useful to start multiple plugins in a single test
|
|
225
|
+
if (before) {
|
|
226
|
+
console.log("Adding plugin startup into mocha's before hook");
|
|
227
|
+
before(startPlugin);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.log(`Please start plugin ${pluginName} manually using "startPlugin()" function`);
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
startPlugin,
|
|
234
|
+
stopPlugin,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16
|
+
const NodeDevices_1 = __importDefault(require("../../src/device-managers/NodeDevices"));
|
|
17
|
+
const chai_1 = __importDefault(require("chai"));
|
|
18
|
+
chai_1.default.should();
|
|
19
|
+
const chai_2 = require("chai");
|
|
20
|
+
const axios_1 = __importDefault(require("axios"));
|
|
21
|
+
const e2ehelper_1 = require("./e2ehelper");
|
|
22
|
+
const plugin_test_support_1 = require("@appium/plugin-test-support");
|
|
23
|
+
const ip_1 = __importDefault(require("ip"));
|
|
24
|
+
const db_1 = require("../../src/data-service/db");
|
|
25
|
+
describe('Basic Plugin Test', () => {
|
|
26
|
+
// dump hub config into a file
|
|
27
|
+
const hub_config_file = (0, e2ehelper_1.ensureHubConfig)();
|
|
28
|
+
// dump node config into a file
|
|
29
|
+
(0, e2ehelper_1.ensureNodeConfig)();
|
|
30
|
+
// setup appium home
|
|
31
|
+
const APPIUM_HOME = (0, e2ehelper_1.ensureAppiumHome)();
|
|
32
|
+
// run hub
|
|
33
|
+
(0, plugin_test_support_1.pluginE2EHarness)({
|
|
34
|
+
before: global.before,
|
|
35
|
+
after: global.after,
|
|
36
|
+
serverArgs: {
|
|
37
|
+
subcommand: 'server',
|
|
38
|
+
configFile: hub_config_file,
|
|
39
|
+
},
|
|
40
|
+
pluginName: 'xenon',
|
|
41
|
+
port: e2ehelper_1.HUB_APPIUM_PORT,
|
|
42
|
+
driverSource: 'npm',
|
|
43
|
+
driverName: 'uiautomator2',
|
|
44
|
+
driverSpec: 'appium-uiautomator2-driver',
|
|
45
|
+
pluginSource: 'local',
|
|
46
|
+
pluginSpec: e2ehelper_1.PLUGIN_PATH,
|
|
47
|
+
appiumHome: APPIUM_HOME,
|
|
48
|
+
});
|
|
49
|
+
const hub_url = `http://${ip_1.default.address()}:${e2ehelper_1.HUB_APPIUM_PORT}`;
|
|
50
|
+
it('Basic Plugin test', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
51
|
+
(yield axios_1.default.get(`${hub_url}/xenon`)).status.should.eql(200);
|
|
52
|
+
}));
|
|
53
|
+
it('Basic Plugin API test', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
(yield axios_1.default.get(`${hub_url}/xenon/api/device`)).status.should.eql(200);
|
|
55
|
+
}));
|
|
56
|
+
it('Add Android devices from node to hub', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
|
+
(yield db_1.XenonDatabase.DeviceModel).removeDataOnly();
|
|
58
|
+
const nodeAndroidDevice = [
|
|
59
|
+
{
|
|
60
|
+
adbRemoteHost: null,
|
|
61
|
+
adbPort: 5039,
|
|
62
|
+
systemPort: 58296,
|
|
63
|
+
sdk: '13',
|
|
64
|
+
realDevice: false,
|
|
65
|
+
name: 'sdk_gphone64_arm64',
|
|
66
|
+
busy: false,
|
|
67
|
+
state: 'device',
|
|
68
|
+
udid: 'emulator-5551',
|
|
69
|
+
platform: 'android',
|
|
70
|
+
deviceType: 'emulator',
|
|
71
|
+
host: 'http://127.2.1.41:4723',
|
|
72
|
+
totalUtilizationTimeMilliSec: 7023014,
|
|
73
|
+
sessionStartTime: 0,
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
const nodeDevices = new NodeDevices_1.default(hub_url);
|
|
77
|
+
yield nodeDevices.postDevicesToHub(nodeAndroidDevice, 'add');
|
|
78
|
+
const devices = (yield axios_1.default.get(`${hub_url}/xenon/api/device`)).data;
|
|
79
|
+
devices.find((d) => d.udid === 'emulator-5551').should.to.be.an('object');
|
|
80
|
+
nodeAndroidDevice[0].udid = 'emulator-5552';
|
|
81
|
+
yield nodeDevices.postDevicesToHub(nodeAndroidDevice, 'add');
|
|
82
|
+
const updatedDeviceList = (yield axios_1.default.get(`${hub_url}/xenon/api/device`)).data;
|
|
83
|
+
//updatedDeviceList.should.have.lengthOf(2);
|
|
84
|
+
updatedDeviceList.find((d) => d.udid === 'emulator-5552').should.to.be.an('object');
|
|
85
|
+
}));
|
|
86
|
+
it('Remove Android devices from node to hub', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
87
|
+
const nodeDevices = new NodeDevices_1.default(hub_url);
|
|
88
|
+
const devices = (yield axios_1.default.get(`${hub_url}/xenon/api/device`)).data;
|
|
89
|
+
const exptectedDevice = devices.find((d) => d.udid === 'emulator-5551');
|
|
90
|
+
devices.find((d) => d.udid === 'emulator-5551').should.to.be.an('object');
|
|
91
|
+
console.log('devices', exptectedDevice);
|
|
92
|
+
yield nodeDevices.postDevicesToHub([{ udid: 'emulator-5551', host: '127.2.1.41' }], 'remove');
|
|
93
|
+
const updatedDeviceList = (yield axios_1.default.get(`${hub_url}/xenon/api/device`)).data;
|
|
94
|
+
const find = updatedDeviceList.find((d) => d.udid === 'emulator-5551');
|
|
95
|
+
(0, chai_2.expect)(find).to.be.undefined;
|
|
96
|
+
}));
|
|
97
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
const chai_1 = __importDefault(require("chai"));
|
|
16
|
+
chai_1.default.should();
|
|
17
|
+
const chai_2 = require("chai");
|
|
18
|
+
const e2ehelper_1 = require("./e2ehelper");
|
|
19
|
+
const plugin_test_support_1 = require("@appium/plugin-test-support");
|
|
20
|
+
const ip_1 = __importDefault(require("ip"));
|
|
21
|
+
const webdriverio_1 = require("webdriverio");
|
|
22
|
+
const prisma_1 = require("../../src/prisma");
|
|
23
|
+
describe('Performance Telemetry Verification', () => {
|
|
24
|
+
const APPIUM_HOME = (0, e2ehelper_1.ensureAppiumHome)('telemetry-verify', true);
|
|
25
|
+
const hub_config_file = (0, e2ehelper_1.ensureHubConfig)('android', 'both', 'both', {
|
|
26
|
+
enableDashboard: true,
|
|
27
|
+
});
|
|
28
|
+
(0, plugin_test_support_1.pluginE2EHarness)({
|
|
29
|
+
before: global.before,
|
|
30
|
+
after: global.after,
|
|
31
|
+
serverArgs: {
|
|
32
|
+
subcommand: 'server',
|
|
33
|
+
configFile: hub_config_file,
|
|
34
|
+
},
|
|
35
|
+
pluginName: 'xenon',
|
|
36
|
+
port: e2ehelper_1.HUB_APPIUM_PORT,
|
|
37
|
+
driverSource: 'npm',
|
|
38
|
+
driverName: 'fake',
|
|
39
|
+
driverSpec: 'appium-fake-driver',
|
|
40
|
+
pluginSource: 'local',
|
|
41
|
+
pluginSpec: e2ehelper_1.PLUGIN_PATH,
|
|
42
|
+
appiumHome: APPIUM_HOME,
|
|
43
|
+
});
|
|
44
|
+
const hub_url = `http://${ip_1.default.address()}:${e2ehelper_1.HUB_APPIUM_PORT}`;
|
|
45
|
+
it('Should capture duration and OTel metadata for commands', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
46
|
+
const driver = yield (0, webdriverio_1.remote)({
|
|
47
|
+
protocol: 'http',
|
|
48
|
+
hostname: ip_1.default.address(),
|
|
49
|
+
port: e2ehelper_1.HUB_APPIUM_PORT,
|
|
50
|
+
path: '/wd/hub',
|
|
51
|
+
capabilities: {
|
|
52
|
+
platformName: 'Android',
|
|
53
|
+
'appium:automationName': 'fake',
|
|
54
|
+
'appium:deviceName': 'FakeDevice',
|
|
55
|
+
'xe:enable_dashboard': true,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
try {
|
|
59
|
+
yield driver.getTitle();
|
|
60
|
+
yield driver.$('//button').click();
|
|
61
|
+
// Give some time for background persistence
|
|
62
|
+
yield new Promise((r) => setTimeout(r, 1000));
|
|
63
|
+
const sessionLogs = yield prisma_1.prisma.sessionLog.findMany({
|
|
64
|
+
where: { session_id: driver.sessionId },
|
|
65
|
+
orderBy: { createdAt: 'desc' },
|
|
66
|
+
});
|
|
67
|
+
console.log(`Found ${sessionLogs.length} logs for session ${driver.sessionId}`);
|
|
68
|
+
sessionLogs.forEach((log) => {
|
|
69
|
+
console.log(`Command: ${log.command_name}, Duration: ${log.duration}ms, SpanID: ${log.span_id}`);
|
|
70
|
+
// Assertions
|
|
71
|
+
(0, chai_2.expect)(log.span_id).to.not.be.null;
|
|
72
|
+
(0, chai_2.expect)(log.trace_id).to.not.be.null;
|
|
73
|
+
if (log.command_name !== 'createSession' && log.command_name !== 'deleteSession') {
|
|
74
|
+
(0, chai_2.expect)(log.duration).to.be.a('number').and.at.least(0);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
(0, chai_2.expect)(sessionLogs.length).to.be.at.least(2);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
yield driver.deleteSession();
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
/**
|
|
14
|
+
* Test to verify video recording functionality
|
|
15
|
+
* This test enables video recording and verifies that the video_recording field is populated in the database
|
|
16
|
+
*/
|
|
17
|
+
const APPIUM_HOST = 'localhost';
|
|
18
|
+
const APPIUM_PORT = 4723;
|
|
19
|
+
const WDIO_PARAMS = {
|
|
20
|
+
connectionRetryCount: 5,
|
|
21
|
+
hostname: APPIUM_HOST,
|
|
22
|
+
port: APPIUM_PORT,
|
|
23
|
+
path: '/wd/hub/',
|
|
24
|
+
logLevel: 'info',
|
|
25
|
+
};
|
|
26
|
+
const capabilities = {
|
|
27
|
+
platformName: 'iOS',
|
|
28
|
+
'appium:automationName': 'XCUITest',
|
|
29
|
+
'appium:iPhoneOnly': true,
|
|
30
|
+
'appium:app': 'https://github.com/AppiumTestDistribution/appium-demo/blob/main/vodqa.zip?raw=true',
|
|
31
|
+
// Note: record_video is now enabled by default, can be set to false to disable
|
|
32
|
+
// record_video: false, // Uncomment to disable video recording
|
|
33
|
+
video_resolution: '1280x720',
|
|
34
|
+
name: 'Video Recording Test',
|
|
35
|
+
build: 'video-recording-test-build',
|
|
36
|
+
};
|
|
37
|
+
describe('Video Recording Test', () => {
|
|
38
|
+
let driver;
|
|
39
|
+
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
+
driver = yield (0, webdriverio_1.remote)(Object.assign(Object.assign({}, WDIO_PARAMS), { capabilities }));
|
|
41
|
+
}));
|
|
42
|
+
it('should record video during test', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
+
console.log('✅ Session created - video recording should be enabled');
|
|
44
|
+
// Perform some basic interactions
|
|
45
|
+
try {
|
|
46
|
+
yield driver.$('~login').click();
|
|
47
|
+
console.log('✅ Clicked login button');
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
console.log('ℹ️ Could not click login button (element may not exist in test environment)');
|
|
51
|
+
}
|
|
52
|
+
// Wait a bit to ensure video is being recorded
|
|
53
|
+
yield driver.pause(2000);
|
|
54
|
+
console.log('✅ Test paused for 2 seconds - video should be recording');
|
|
55
|
+
}));
|
|
56
|
+
afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
|
+
console.log('🟢 Deleting session - video should be stopped and saved');
|
|
58
|
+
yield driver.deleteSession();
|
|
59
|
+
console.log('✅ Session deleted');
|
|
60
|
+
// Give the server a moment to process the video
|
|
61
|
+
yield new Promise((resolve) => setTimeout(resolve, 1000));
|
|
62
|
+
}));
|
|
63
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
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.setupTestContainer = setupTestContainer;
|
|
16
|
+
exports.createTestAndroidManager = createTestAndroidManager;
|
|
17
|
+
exports.createTestIOSManager = createTestIOSManager;
|
|
18
|
+
exports.createTestXenonManager = createTestXenonManager;
|
|
19
|
+
exports.resetTestContainer = resetTestContainer;
|
|
20
|
+
require("reflect-metadata");
|
|
21
|
+
const typedi_1 = require("typedi");
|
|
22
|
+
const PluginContext_1 = require("../../src/PluginContext");
|
|
23
|
+
const IPluginArgs_1 = require("../../src/interfaces/IPluginArgs");
|
|
24
|
+
const uuid_1 = require("uuid");
|
|
25
|
+
const db_1 = require("../../src/data-service/db");
|
|
26
|
+
const device_store_1 = require("../../src/data-service/device-store");
|
|
27
|
+
const AndroidDeviceManager_1 = __importDefault(require("../../src/device-managers/AndroidDeviceManager"));
|
|
28
|
+
const IOSDeviceManager_1 = __importDefault(require("../../src/device-managers/IOSDeviceManager"));
|
|
29
|
+
const device_managers_1 = require("../../src/device-managers");
|
|
30
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
31
|
+
const IOSDiscoveryService_1 = require("../../src/device-managers/ios/IOSDiscoveryService");
|
|
32
|
+
const sandbox = sinon_1.default.createSandbox();
|
|
33
|
+
/**
|
|
34
|
+
* Test utility to initialize the TypeDI Container with a PluginContext
|
|
35
|
+
* configured for testing purposes.
|
|
36
|
+
*/
|
|
37
|
+
function setupTestContainer(overrides) {
|
|
38
|
+
// Reset container to ensure clean state between tests
|
|
39
|
+
typedi_1.Container.reset();
|
|
40
|
+
const nodeId = (0, uuid_1.v4)();
|
|
41
|
+
const port = 4723;
|
|
42
|
+
const pluginArgs = Object.assign({}, IPluginArgs_1.DefaultPluginArgs, overrides || {});
|
|
43
|
+
// Mock LocalStorage
|
|
44
|
+
typedi_1.Container.set('LocalStorage', {
|
|
45
|
+
getItem: (key) => null,
|
|
46
|
+
setItem: (key, value) => { },
|
|
47
|
+
removeItem: (key) => { },
|
|
48
|
+
});
|
|
49
|
+
// Initialize PluginContext
|
|
50
|
+
const context = typedi_1.Container.get(PluginContext_1.PluginContext);
|
|
51
|
+
context.setContext(pluginArgs, port, nodeId, '');
|
|
52
|
+
// Get managers (they'll use the PluginContext via DI)
|
|
53
|
+
const androidManager = typedi_1.Container.get(AndroidDeviceManager_1.default);
|
|
54
|
+
const iosManager = typedi_1.Container.get(IOSDeviceManager_1.default);
|
|
55
|
+
const xenonManager = typedi_1.Container.get(device_managers_1.XenonManager);
|
|
56
|
+
return {
|
|
57
|
+
context,
|
|
58
|
+
androidManager,
|
|
59
|
+
iosManager,
|
|
60
|
+
xenonManager,
|
|
61
|
+
nodeId,
|
|
62
|
+
port,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Creates a mock PluginContext and AndroidDeviceManager for unit testing.
|
|
67
|
+
* Use this when you need to stub/spy on the manager.
|
|
68
|
+
*/
|
|
69
|
+
function createTestAndroidManager(pluginArgs) {
|
|
70
|
+
const { androidManager } = setupTestContainer(pluginArgs);
|
|
71
|
+
return androidManager;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Creates a mock PluginContext and IOSDeviceManager for unit testing.
|
|
75
|
+
*/
|
|
76
|
+
function createTestIOSManager(pluginArgs) {
|
|
77
|
+
const { iosManager } = setupTestContainer(pluginArgs);
|
|
78
|
+
return iosManager;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Creates a XenonManager configured for testing.
|
|
82
|
+
*/
|
|
83
|
+
function createTestXenonManager(pluginArgs) {
|
|
84
|
+
const { xenonManager } = setupTestContainer(pluginArgs);
|
|
85
|
+
xenonManager.init();
|
|
86
|
+
return xenonManager;
|
|
87
|
+
}
|
|
88
|
+
function resetTestContainer() {
|
|
89
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
sandbox.restore();
|
|
91
|
+
typedi_1.Container.reset();
|
|
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;
|
|
110
|
+
yield db_1.XenonDatabase.reset();
|
|
111
|
+
});
|
|
112
|
+
}
|