Skip to content

Commit 3a8500a

Browse files
feature: implement mobile-devices-controller-server
* feature: expose server * feat: implement interaction with mobile-devices-controller-server * fix(appium-server): should not trying to subscribe for device if alredy has * fix(service-context): url encoding * fix: stopDevice * chore: merge * fix: kill on process exit appium server and driver * refactor(device-controlelr) : update subscribe. * fix: retry for starting server and driver
1 parent 9964551 commit 3a8500a

21 files changed

+331
-85
lines changed

index.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { IDeviceManager } from "./lib/interfaces/device-manager";
12
export { AppiumDriver } from "./lib/appium-driver";
3+
export { AppiumServer } from "./lib/appium-server";
24
export { ElementHelper } from "./lib/element-helper";
35
export { UIElement } from "./lib/ui-element";
46
export { Point } from "./lib/point";
@@ -7,6 +9,7 @@ export { Locator } from "./lib/locators";
79
export { Direction } from "./lib/direction";
810
export { DeviceManger } from "./lib/device-controller";
911
export { IRectangle } from "./lib/interfaces/rectangle";
10-
export declare function startServer(port?: number): Promise<void>;
12+
export { IDeviceManager } from "./lib/interfaces/device-manager";
13+
export declare function startServer(port?: number, deviceManager?: IDeviceManager): Promise<void>;
1114
export declare function stopServer(): Promise<void>;
1215
export declare function createDriver(): Promise<any>;

index.ts

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import * as portastic from "portastic";
21
import { AppiumServer } from "./lib/appium-server";
32
import { AppiumDriver } from "./lib/appium-driver";
43
import { ElementHelper } from "./lib/element-helper";
54
import { NsCapabilities } from "./lib/ns-capabilities";
6-
import { shutdown } from "./lib/utils";
5+
import { IDeviceManager } from "./lib/interfaces/device-manager";
6+
import { shutdown, findFreePort } from "./lib/utils";
77

88
export { AppiumDriver } from "./lib/appium-driver";
9+
export { AppiumServer } from "./lib/appium-server";
910
export { ElementHelper } from "./lib/element-helper";
1011
export { UIElement } from "./lib/ui-element";
1112
export { Point } from "./lib/point";
@@ -14,52 +15,27 @@ export { Locator } from "./lib/locators";
1415
export { Direction } from "./lib/direction";
1516
export { DeviceManger } from "./lib/device-controller";
1617
export { IRectangle } from "./lib/interfaces/rectangle";
18+
export { IDeviceManager } from "./lib/interfaces/device-manager";
1719

1820
const nsCapabilities = new NsCapabilities();
1921
const appiumServer = new AppiumServer(nsCapabilities);
2022

2123
let appiumDriver = null;
22-
export async function startServer(port?: number) {
23-
appiumServer.port = port || nsCapabilities.port;
24-
let retry = false;
25-
if (!appiumServer.port) {
26-
appiumServer.port = (await portastic.find({ min: 8600, max: 9080 }))[0];
27-
retry = true;
28-
}
29-
30-
let hasStarted = await appiumServer.start();
31-
let retryCount = 0;
32-
while (retry && !hasStarted && retryCount < 10) {
33-
let tempPort = appiumServer.port + 10;
34-
tempPort = (await portastic.find({ min: tempPort, max: 9180 }))[0];
35-
console.log("Trying to use port: ", tempPort);
36-
appiumServer.port = tempPort;
37-
hasStarted = await appiumServer.start();
38-
retryCount++;
39-
}
40-
41-
if (!hasStarted) {
42-
throw new Error("Appium driver failed to start!!! Run with --verbose option for more info!");
43-
}
44-
45-
appiumServer.hasStarted = hasStarted;
46-
47-
process.on("uncaughtException", () => shutdown(appiumServer.server, nsCapabilities.verbose));
48-
process.on("exit", () => shutdown(appiumServer.server, nsCapabilities.verbose));
49-
process.on("SIGINT", () => shutdown(appiumServer.server, nsCapabilities.verbose));
24+
export async function startServer(port?: number, deviceManager?: IDeviceManager) {
25+
await appiumServer.start(port || 8300, deviceManager);
5026
};
5127

5228
export async function stopServer() {
5329
if (appiumDriver !== null && appiumDriver.isAlive) {
5430
await appiumDriver.quit();
5531
}
56-
if (appiumServer !== null && appiumServer.hasStarted) {
32+
if (appiumServer !== null && appiumServer.server && !appiumServer.server.killed) {
5733
await appiumServer.stop();
5834
}
5935
};
6036

6137
export async function createDriver() {
62-
if (!appiumServer.hasStarted) {
38+
if (!appiumServer.server) {
6339
throw new Error("Server is not available!");
6440
}
6541
if (!nsCapabilities.appiumCapsLocation) {
@@ -79,3 +55,15 @@ export async function createDriver() {
7955

8056
return appiumDriver;
8157
}
58+
59+
const killProcesses = async (code) => {
60+
if (appiumServer) {
61+
return await stopServer();
62+
}
63+
}
64+
65+
process.on("exit", async (code) => await killProcesses(code));
66+
process.on("close", async (code) => await killProcesses(code));
67+
process.on("SIGINT", async (code) => await killProcesses(code));
68+
process.on("error", async (code) => await killProcesses(code));
69+
process.on("uncaughtException", () => async (code) => await killProcesses(code));

lib/appium-driver.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export declare class AppiumDriver {
131131
logScreenshot(fileName: string): Promise<string>;
132132
logPageSource(fileName: string): Promise<void>;
133133
static createAppiumDriver(port: number, args: INsCapabilities): Promise<AppiumDriver>;
134+
private static applyAdditionalSettings(args);
134135
resetApp(): Promise<void>;
135136
init(): Promise<void>;
136137
quit(): Promise<void>;

lib/appium-driver.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { SearchOptions } from "./search-options";
1010
import { UIElement } from "./ui-element";
1111
import { Direction } from "./direction";
1212
import { Locator } from "./locators";
13+
import { Platform } from "mobile-devices-controller";
14+
import { ServiceContext } from "./service/service-context";
1315
import {
1416
addExt,
1517
log,
@@ -21,6 +23,7 @@ import {
2123
getReportPath,
2224
calculateOffset,
2325
scroll,
26+
findFreePort
2427
} from "./utils";
2528
import { INsCapabilities } from "./interfaces/ns-capabilities";
2629
import { IRectangle } from "./interfaces/rectangle";
@@ -410,6 +413,8 @@ export class AppiumDriver {
410413

411414
log("Creating driver!", args.verbose);
412415

416+
args.appiumCaps['udid'] = args.appiumCaps['udid'] || args.device.token;
417+
await AppiumDriver.applyAdditionalSettings(args);
413418
const _webio = webdriverio.remote({
414419
baseUrl: driverConfig.host,
415420
port: driverConfig.port,
@@ -419,10 +424,41 @@ export class AppiumDriver {
419424

420425
const driver = await wd.promiseChainRemote(driverConfig);
421426
AppiumDriver.configureLogging(driver, args.verbose);
422-
await driver.init(args.appiumCaps);
427+
let hasStarted = false;
428+
let retries = 10;
429+
while (retries > 0 && !hasStarted) {
430+
try {
431+
const test = await driver.init(args.appiumCaps);
432+
hasStarted = true;
433+
} catch (error) {
434+
if (error && error.message && error.message.includes("WebDriverAgent")) {
435+
let freePort = await findFreePort(10, args.appiumCaps.port, args);
436+
console.log(" args.appiumCaps['wdaLocalPort']", freePort)
437+
args.appiumCaps["wdaLocalPort"] = freePort;
438+
}
439+
console.log(error);
440+
}
441+
retries--;
442+
}
443+
423444
return new AppiumDriver(driver, wd, _webio, driverConfig, args);
424445
}
425446

447+
private static async applyAdditionalSettings(args) {
448+
if (args.appiumCaps.platformName.toLowerCase() === Platform.IOS) {
449+
args.appiumCaps["useNewWDA"] = false;
450+
args.appiumCaps["wdaStartupRetries"] = 5;
451+
args.appiumCaps["shouldUseSingletonTestManager"] = false;
452+
453+
// It looks we need it for XCTest (iOS 10+ automation)
454+
if (args.appiumCaps.platformVersion >= 10) {
455+
let freePort = await findFreePort(10, (parseInt(args.appiumCaps["wdaLocalPort"])) || 8400, args);
456+
console.log(" args.appiumCaps['wdaLocalPort']", freePort)
457+
args.appiumCaps["wdaLocalPort"] = freePort;
458+
}
459+
}
460+
}
461+
426462
public async resetApp() {
427463
await this._driver.resetApp();
428464
}

lib/appium-server.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
/// <reference types="node" />
22
import * as child_process from "child_process";
33
import { INsCapabilities } from "./interfaces/ns-capabilities";
4+
import { IDeviceManager } from "./interfaces/device-manager";
45
export declare class AppiumServer {
56
private _args;
67
private _server;
78
private _appium;
89
private _port;
910
private _runType;
1011
private _hasStarted;
12+
private _deviceManager;
1113
constructor(_args: INsCapabilities);
1214
port: number;
1315
runType: string;
1416
readonly server: child_process.ChildProcess;
1517
hasStarted: boolean;
16-
start(): Promise<boolean>;
18+
start(port: any, deviceManager?: IDeviceManager): Promise<boolean>;
1719
stop(): Promise<{}>;
1820
private resolveAppiumDependency();
1921
}

lib/appium-server.ts

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
import * as child_process from "child_process";
2-
import { log, resolve, waitForOutput, shutdown, fileExists, isWin, executeCommand } from "./utils";
2+
import {
3+
log,
4+
resolve,
5+
waitForOutput,
6+
shutdown,
7+
fileExists,
8+
isWin,
9+
executeCommand,
10+
findFreePort
11+
} from "./utils";
312
import { INsCapabilities } from "./interfaces/ns-capabilities";
13+
import { IDeviceManager } from "./interfaces/device-manager";
414
import { DeviceManger } from "./device-controller";
15+
import { ServiceContext } from "../lib/service/service-context";
516

617
export class AppiumServer {
718
private _server: child_process.ChildProcess;
819
private _appium;
920
private _port: number;
1021
private _runType: string;
1122
private _hasStarted: boolean;
23+
private _deviceManager: IDeviceManager;
1224

1325
constructor(private _args: INsCapabilities) {
1426
this._runType = this._args.runType;
@@ -44,23 +56,38 @@ export class AppiumServer {
4456
this._hasStarted = hasStarted;
4557
}
4658

47-
public async start() {
48-
const device = await DeviceManger.startDevice(this._args);
59+
public async start(port, deviceManager: IDeviceManager = new DeviceManger(port)) {
60+
this._deviceManager = deviceManager;
61+
if (!this._args.device) {
62+
const device = await this._deviceManager.startDevice(this._args);
63+
this._args.device = device;
64+
}
4965
log("Starting server...", this._args.verbose);
50-
this._args.device = device;
5166
const logLevel = this._args.verbose === true ? "debug" : "info";
52-
this._server = child_process.spawn(this._appium, ["-p", this.port.toString(), "--log-level", logLevel], {
53-
shell: true,
54-
detached: false
55-
});
56-
57-
const response: boolean = await waitForOutput(this._server, /listener started/, /Error: listen/, 60000, this._args.verbose);
67+
this.port = port || this._args.port;
68+
let retry = false;
69+
70+
let response: boolean = false;
71+
let retries = 11;
72+
while (retries > 0 && !response) {
73+
retries--;
74+
this.port = (await findFreePort(100, this.port, this._args));
75+
76+
this._server = child_process.spawn(this._appium, ["-p", this.port.toString(), "--log-level", logLevel], {
77+
shell: true,
78+
detached: false
79+
});
80+
response = await waitForOutput(this._server, /listener started/, /Error: listen/, 60000, this._args.verbose);
81+
if (!response) {
82+
this.port += 10;
83+
}
84+
}
5885

5986
return response;
6087
}
6188

6289
public async stop() {
63-
await DeviceManger.stop(this._args);
90+
await this._deviceManager.stopDevice(this._args);
6491
return new Promise((resolve, reject) => {
6592
this._server.on("close", (code, signal) => {
6693
log(`Appium terminated due signal: ${signal} and code: ${code}`, this._args.verbose);

lib/device-controller.d.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { INsCapabilities } from "./interfaces/ns-capabilities";
2+
import { IDeviceManager } from "./interfaces/device-manager";
3+
import { ServiceContext } from "./service/service-context";
24
import { IDevice } from "mobile-devices-controller";
3-
export declare class DeviceManger {
5+
export declare class DeviceManger implements IDeviceManager {
6+
private _serveiceContext;
47
private static _emulators;
5-
static startDevice(args: INsCapabilities): Promise<IDevice>;
6-
static stop(args: INsCapabilities): Promise<void>;
8+
constructor(port: any, _serveiceContext?: ServiceContext);
9+
startDevice(args: INsCapabilities): Promise<IDevice>;
10+
stopDevice(args: INsCapabilities): Promise<void>;
711
static kill(device: IDevice): Promise<void>;
812
private static getDefaultDevice(args);
9-
private static device(runType);
1013
}

0 commit comments

Comments
 (0)