"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var devices_service_1 = require("../../../mobile/mobile-core/devices-service");
var yok_1 = require("../../../yok");
var Future = require("fibers/future");
var events_1 = require("events");
var chai_1 = require("chai");
var stubs_1 = require("../stubs");
var messages_1 = require("../../../messages/messages");
var constants = require("../../../constants");
var device_platforms_constants_1 = require("../../../mobile/device-platforms-constants");
var IOSDeviceDiscoveryStub = (function (_super) {
    __extends(IOSDeviceDiscoveryStub, _super);
    function IOSDeviceDiscoveryStub() {
        _super.apply(this, arguments);
    }
    IOSDeviceDiscoveryStub.prototype.startLookingForDevices = function () {
        return Future.fromResult();
    };
    IOSDeviceDiscoveryStub.prototype.checkForDevices = function () {
        return Future.fromResult();
    };
    return IOSDeviceDiscoveryStub;
}(events_1.EventEmitter));
var AndroidDeviceDiscoveryStub = (function (_super) {
    __extends(AndroidDeviceDiscoveryStub, _super);
    function AndroidDeviceDiscoveryStub() {
        _super.apply(this, arguments);
    }
    AndroidDeviceDiscoveryStub.prototype.startLookingForDevices = function () {
        return Future.fromResult();
    };
    AndroidDeviceDiscoveryStub.prototype.checkForDevices = function () {
        return Future.fromResult();
    };
    return AndroidDeviceDiscoveryStub;
}(events_1.EventEmitter));
var IOSSimulatorDiscoveryStub = (function (_super) {
    __extends(IOSSimulatorDiscoveryStub, _super);
    function IOSSimulatorDiscoveryStub() {
        _super.apply(this, arguments);
    }
    IOSSimulatorDiscoveryStub.prototype.startLookingForDevices = function () {
        return Future.fromResult();
    };
    IOSSimulatorDiscoveryStub.prototype.checkForDevices = function () {
        return Future.fromResult();
    };
    return IOSSimulatorDiscoveryStub;
}(events_1.EventEmitter));
var androidDeviceDiscovery, iOSDeviceDiscovery, iOSSimulatorDiscovery, androidEmulatorDevice = { deviceInfo: { identifier: "androidEmulatorDevice", platform: "android" }, isEmulator: true }, iOSSimulator = {
    deviceInfo: {
        identifier: "ios-simulator-device",
        platform: "ios"
    },
    applicationManager: {
        getInstalledApplications: function () { return Future.fromResult(["com.telerik.unitTest1", "com.telerik.unitTest2"]); },
        canStartApplication: function () { return true; },
        startApplication: function (packageName, framework) { return Future.fromResult(); },
        tryStartApplication: function (packageName, framework) { return Future.fromResult(); },
        reinstallApplication: function (packageName, packageFile) { return Future.fromResult(); },
        isApplicationInstalled: function (packageName) { return Future.fromResult(_.includes(["com.telerik.unitTest1", "com.telerik.unitTest2"], packageName)); },
        isLiveSyncSupported: function (appIdentifier) { return Future.fromResult(_.includes(["com.telerik.unitTest1", "com.telerik.unitTest2"], appIdentifier)); }
    },
    deploy: function (packageFile, packageName) { return Future.fromResult(); },
    isEmulator: true
};
var AndroidEmulatorServices = (function () {
    function AndroidEmulatorServices() {
        this.isStartEmulatorCalled = false;
    }
    AndroidEmulatorServices.prototype.startEmulator = function () {
        this.isStartEmulatorCalled = true;
        androidDeviceDiscovery.emit("deviceFound", androidEmulatorDevice);
        return Future.fromResult();
    };
    return AndroidEmulatorServices;
}());
var IOSEmulatorServices = (function () {
    function IOSEmulatorServices() {
        this.isStartEmulatorCalled = false;
    }
    IOSEmulatorServices.prototype.startEmulator = function () {
        if (!this.isStartEmulatorCalled) {
            this.isStartEmulatorCalled = true;
            iOSSimulatorDiscovery.emit("deviceFound", iOSSimulator);
        }
        return Future.fromResult();
    };
    return IOSEmulatorServices;
}());
function createTestInjector() {
    var testInjector = new yok_1.Yok();
    testInjector.register("logger", stubs_1.CommonLoggerStub);
    testInjector.register("errors", stubs_1.ErrorsStub);
    testInjector.register("iOSDeviceDiscovery", IOSDeviceDiscoveryStub);
    testInjector.register("iOSSimulatorDiscovery", IOSSimulatorDiscoveryStub);
    testInjector.register("androidDeviceDiscovery", AndroidDeviceDiscoveryStub);
    testInjector.register("staticConfig", { CLIENT_NAME: "unit-tests" });
    testInjector.register("devicePlatformsConstants", device_platforms_constants_1.DevicePlatformsConstants);
    testInjector.register("androidEmulatorServices", AndroidEmulatorServices);
    testInjector.register("iOSEmulatorServices", IOSEmulatorServices);
    testInjector.register("messages", messages_1.Messages);
    testInjector.register("companionAppsService", {});
    testInjector.register("processService", {
        attachToProcessExitSignals: function (context, callback) { }
    });
    testInjector.register("mobileHelper", {
        platformNames: ["ios", "android"],
        validatePlatformName: function (platform) { return platform.toLowerCase(); },
        getPlatformCapabilities: function (platform) { return { cableDeploy: true }; },
        isiOSPlatform: function (platform) { return !!(platform && platform.toLowerCase() === "ios"); },
        isAndroidPlatform: function (platform) { return !!(platform && platform.toLowerCase() === "android"); }
    });
    testInjector.register("deviceLogProvider", {
        setLogLevel: function (logLevel, deviceIdentifier) { }
    });
    testInjector.register("devicesService", devices_service_1.DevicesService);
    testInjector.register("hostInfo", {
        isDarwin: false
    });
    testInjector.register("options", {
        emulator: false
    });
    testInjector.register("androidProcessService", {});
    return testInjector;
}
function mockIsAppInstalled(devices, expectedResult) {
    _.each(devices, function (device, index) { return device.applicationManager.isApplicationInstalled = function (packageName) { return Future.fromResult(expectedResult[index]); }; });
}
function throwErrorFuture() {
    return (function () {
        throw new Error("error");
    }).future()();
}
var intervalId = 1;
var nodeJsTimer = {
    ref: function () { },
    unref: function () { return intervalId++; }
};
var originalSetInterval = setInterval;
function mockSetInterval(options) {
    global.setInterval = function (callback, ms) {
        var args = [];
        for (var _i = 2; _i < arguments.length; _i++) {
            args[_i - 2] = arguments[_i];
        }
        if (options.shouldExecuteCallback) {
            callback();
        }
        if (options.testCaseCallback) {
            options.testCaseCallback();
        }
        return nodeJsTimer;
    };
}
function resetDefaultSetInterval() {
    global.setInterval = originalSetInterval;
}
describe("devicesService", function () {
    var counter = 0, iOSDevice = {
        deviceInfo: {
            identifier: "ios-device",
            platform: "ios"
        },
        applicationManager: {
            getInstalledApplications: function () { return Future.fromResult(["com.telerik.unitTest1", "com.telerik.unitTest2"]); },
            canStartApplication: function () { return true; },
            startApplication: function (packageName, framework) { return Future.fromResult(); },
            tryStartApplication: function (packageName, framework) { return Future.fromResult(); },
            reinstallApplication: function (packageName, packageFile) { return Future.fromResult(); },
            isApplicationInstalled: function (packageName) { return Future.fromResult(_.includes(["com.telerik.unitTest1", "com.telerik.unitTest2"], packageName)); },
            isLiveSyncSupported: function (appIdentifier) { return Future.fromResult(_.includes(["com.telerik.unitTest1", "com.telerik.unitTest2"], appIdentifier)); },
            checkForApplicationUpdates: function () { return Future.fromResult(); },
            getDebuggableApps: function () { return Future.fromResult(null); },
            getDebuggableAppViews: function (appIdentifiers) { return Future.fromResult(null); }
        },
        deploy: function (packageFile, packageName) { return Future.fromResult(); }
    }, androidDevice = {
        deviceInfo: {
            identifier: "android-device",
            platform: "android"
        },
        applicationManager: {
            getInstalledApplications: function () { return Future.fromResult(["com.telerik.unitTest1", "com.telerik.unitTest2", "com.telerik.unitTest3"]); },
            canStartApplication: function () { return true; },
            startApplication: function (packageName, framework) { return Future.fromResult(); },
            tryStartApplication: function (packageName, framework) { return Future.fromResult(); },
            reinstallApplication: function (packageName, packageFile) { return Future.fromResult(); },
            isApplicationInstalled: function (packageName) { return Future.fromResult(_.includes(["com.telerik.unitTest1", "com.telerik.unitTest2", "com.telerik.unitTest3"], packageName)); },
            isLiveSyncSupported: function (appIdentifier) { return Future.fromResult(_.includes(["com.telerik.unitTest1", "com.telerik.unitTest2", "com.telerik.unitTest3"], appIdentifier)); },
            checkForApplicationUpdates: function () { return Future.fromResult(); },
            getDebuggableApps: function () { return Future.fromResult(null); },
            getDebuggableAppViews: function (appIdentifiers) { return Future.fromResult(null); }
        },
        deploy: function (packageFile, packageName) { return Future.fromResult(); }
    }, testInjector, devicesService, androidEmulatorServices, logger, assertAndroidEmulatorIsStarted = function () {
        chai_1.assert.isFalse(androidEmulatorServices.isStartEmulatorCalled);
        devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
        chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device.");
        chai_1.assert.isTrue(androidEmulatorServices.isStartEmulatorCalled);
        androidDeviceDiscovery.emit("deviceLost", androidEmulatorDevice);
        androidEmulatorServices.isStartEmulatorCalled = false;
    };
    beforeEach(function () {
        testInjector = createTestInjector();
        devicesService = testInjector.resolve("devicesService");
        iOSDeviceDiscovery = testInjector.resolve("iOSDeviceDiscovery");
        iOSSimulatorDiscovery = testInjector.resolve("iOSSimulatorDiscovery");
        androidDeviceDiscovery = testInjector.resolve("androidDeviceDiscovery");
        androidEmulatorServices = testInjector.resolve("androidEmulatorServices");
        logger = testInjector.resolve("logger");
        counter = 0;
    });
    it("attaches to events when a new DevicesService is instantiated", function () {
        iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
        androidDeviceDiscovery.emit("deviceFound", androidDevice);
        var devices = devicesService.getDeviceInstances();
        chai_1.assert.isTrue(devicesService.hasDevices, "After emitting two devices, hasDevices must be true");
        chai_1.assert.deepEqual(devices[0], iOSDevice);
        chai_1.assert.deepEqual(devices[1], androidDevice);
    });
    describe("hasDevices", function () {
        it("is true when device is found", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            chai_1.assert.isTrue(devicesService.hasDevices, "After emitting, hasDevices must be true");
        });
        it("is false when device is found and lost after that", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            androidDeviceDiscovery.emit("deviceLost", androidDevice);
            chai_1.assert.isFalse(devicesService.hasDevices, "After losing all devices, hasDevices must be false.");
        });
        it("is true when two devices are found and one of them is lost after that", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
            androidDeviceDiscovery.emit("deviceLost", androidDevice);
            chai_1.assert.isTrue(devicesService.hasDevices, "After losing only one of two devices, hasDevices must be true.");
        });
    });
    describe("getDeviceInstances and getDevices", function () {
        it("returns one android device, when only one device is attached", function () {
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [], "Initially getDevicesInstances must return empty array.");
            chai_1.assert.deepEqual(devicesService.getDevices(), [], "Initially getDevices must return empty array.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice]);
            chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo]);
        });
        it("does not return any devices, when only one device is attached and it is removed after that", function () {
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [], "Initially getDevicesInstances must return empty array.");
            chai_1.assert.deepEqual(devicesService.getDevices(), [], "Initially getDevices must return empty array.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice]);
            chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo]);
            androidDeviceDiscovery.emit("deviceLost", androidDevice);
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [], "When all devices are lost, getDevicesInstances must return empty array.");
            chai_1.assert.deepEqual(devicesService.getDevices(), [], "When all devices are lost, getDevices must return empty array.");
        });
        it("returns one android device, when two devices are attached and one of them is removed", function () {
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [], "Initially getDevicesInstances must return empty array.");
            chai_1.assert.deepEqual(devicesService.getDevices(), [], "Initially getDevices must return empty array.");
            var tempDevice = { deviceInfo: { identifier: "temp-device" } };
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            androidDeviceDiscovery.emit("deviceFound", tempDevice);
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice, tempDevice]);
            chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo, tempDevice.deviceInfo]);
            androidDeviceDiscovery.emit("deviceLost", tempDevice);
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice]);
            chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo]);
        });
    });
    describe("isAppInstalledOnDevices", function () {
        beforeEach(function () {
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
        });
        it("returns true for each device on which the app is installed", function () {
            var deviceIdentifiers = [androidDevice.deviceInfo.identifier, iOSDevice.deviceInfo.identifier], appId = "com.telerik.unitTest1";
            var results = devicesService.isAppInstalledOnDevices(deviceIdentifiers, appId, "cordova");
            chai_1.assert.isTrue(results.length > 0);
            _.each(results, function (futurizedResult, index) {
                var realResult = futurizedResult.wait();
                chai_1.assert.isTrue(realResult.isInstalled);
                chai_1.assert.deepEqual(realResult.appIdentifier, appId);
                chai_1.assert.deepEqual(realResult.deviceIdentifier, deviceIdentifiers[index]);
                chai_1.assert.deepEqual(realResult.isLiveSyncSupported, true);
            });
        });
        it("returns false for each device on which the app is not installed", function () {
            var results = devicesService.isAppInstalledOnDevices([androidDevice.deviceInfo.identifier, iOSDevice.deviceInfo.identifier], "com.telerik.unitTest3", "cordova");
            chai_1.assert.isTrue(results.length > 0);
            Future.wait(results);
            chai_1.assert.deepEqual(results.map(function (r) { return r.get().isInstalled; }), [true, false]);
        });
        it("throws error when invalid identifier is passed", function () {
            var results = devicesService.isAppInstalledOnDevices(["invalidDeviceId", iOSDevice.deviceInfo.identifier], "com.telerik.unitTest1", "cordova");
            chai_1.assert.throws(function () { return Future.wait(results); });
            _.each(results, function (futurizedResult) {
                var error = futurizedResult.error;
                if (error) {
                    chai_1.assert.isTrue(error.message.indexOf("invalidDeviceId") !== -1, "The message must contain the id of the invalid device.");
                }
                else {
                    chai_1.assert.isTrue(futurizedResult.get().isInstalled, "The app is installed on iOS Device, so we must return true.");
                }
            });
        });
    });
    describe("initialize and other methods behavior after initialze work correctly", function () {
        var tempDevice = {
            deviceInfo: {
                identifier: "temp-device",
                platform: "android"
            },
            applicationManager: {
                getInstalledApplications: function () { return Future.fromResult(["com.telerik.unitTest1", "com.telerik.unitTest2", "com.telerik.unitTest3"]); },
                isApplicationInstalled: function (packageName) { return Future.fromResult(_.includes(["com.telerik.unitTest1", "com.telerik.unitTest2", "com.telerik.unitTest3"], packageName)); },
                isLiveSyncSupported: function (appIdentifier) { return Future.fromResult(_.includes(["com.telerik.unitTest1", "com.telerik.unitTest2", "com.telerik.unitTest3"], appIdentifier)); }
            }
        };
        describe("when initialize is called with platform and deviceId and device's platform is the same as passed one", function () {
            var assertAllMethodsResults = function (deviceId) {
                chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
                androidDeviceDiscovery.emit("deviceFound", androidDevice);
                devicesService.initialize({ platform: "android", deviceId: deviceId }).wait();
                chai_1.assert.deepEqual(devicesService.platform, "android");
                chai_1.assert.deepEqual(devicesService.deviceCount, 1);
                androidDeviceDiscovery.emit("deviceFound", tempDevice);
                chai_1.assert.isTrue(devicesService.hasDevices, "After emitting and initializing, hasDevices must be true");
                chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice, tempDevice]);
                chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo, tempDevice.deviceInfo]);
                chai_1.assert.deepEqual(devicesService.deviceCount, 1);
                devicesService.execute(function () { counter++; return Future.fromResult(); }).wait();
                chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device.");
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return false; }).wait();
                chai_1.assert.deepEqual(counter, 0, "The action must not be executed when canExecute returns false.");
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
                chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device.");
                androidDeviceDiscovery.emit("deviceLost", androidDevice);
                androidDeviceDiscovery.emit("deviceLost", tempDevice);
                counter = 0;
                assertAndroidEmulatorIsStarted();
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }, { allowNoDevices: true }).wait();
                chai_1.assert.deepEqual(counter, 0, "The action must not be executed when there are no devices.");
                chai_1.assert.isTrue(logger.output.indexOf(constants.ERROR_NO_DEVICES) !== -1);
            };
            it("when deviceId is deviceIdentifier", function () {
                assertAllMethodsResults(androidDevice.deviceInfo.identifier);
            });
            it("when deviceId is index", function () {
                assertAllMethodsResults("1");
            });
            it("fails when deviceId is invalid index (less than 0)", function () {
                chai_1.assert.throws(function () { return devicesService.initialize({ platform: "android", deviceId: "-1" }).wait(); });
            });
            it("fails when deviceId is invalid index (more than currently connected devices)", function () {
                chai_1.assert.throws(function () { return devicesService.initialize({ platform: "android", deviceId: "100" }).wait(); });
            });
            it("does not fail when iOSDeviceDiscovery startLookingForDevices fails", function () {
                iOSDeviceDiscovery.startLookingForDevices = function () { throw new Error("my error"); };
                assertAllMethodsResults("1");
                chai_1.assert.isTrue(logger.traceOutput.indexOf("my error") !== -1);
            });
            it("does not fail when androidDeviceDiscovery startLookingForDevices fails", function () {
                androidDeviceDiscovery.startLookingForDevices = function () { throw new Error("my error"); };
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                devicesService.initialize({ platform: "ios", deviceId: iOSDevice.deviceInfo.identifier }).wait();
                chai_1.assert.isTrue(logger.traceOutput.indexOf("my error") !== -1);
            });
            it("does not fail when iosSimulatorDiscovery startLookingForDevices fails", function () {
                var hostInfo = testInjector.resolve("hostInfo");
                hostInfo.isDarwin = true;
                iOSSimulatorDiscovery.startLookingForDevices = function () { throw new Error("my error"); };
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                devicesService.initialize({ platform: "ios", deviceId: iOSDevice.deviceInfo.identifier }).wait();
                chai_1.assert.isTrue(logger.traceOutput.indexOf("my error") !== -1);
            });
        });
        it("when initialize is called with platform and deviceId and such device cannot be found", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            chai_1.assert.throws(function () { return devicesService.initialize({ platform: "android", deviceId: androidDevice.deviceInfo.identifier }).wait(); });
        });
        it("when initialize is called with deviceId and invalid platform", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            chai_1.assert.throws(function () { return devicesService.initialize({ platform: "invalidPlatform", deviceId: androidDevice.deviceInfo.identifier }).wait(); });
        });
        it("when initialize is called with platform and deviceId and device's platform is different", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            chai_1.assert.throws(function () { return devicesService.initialize({ platform: "ios", deviceId: androidDevice.deviceInfo.identifier }).wait(); });
        });
        describe("when only deviceIdentifier is passed", function () {
            var assertAllMethodsResults = function (deviceId) {
                chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
                androidDeviceDiscovery.emit("deviceFound", androidDevice);
                devicesService.initialize({ deviceId: deviceId }).wait();
                chai_1.assert.deepEqual(devicesService.platform, "android");
                chai_1.assert.deepEqual(devicesService.deviceCount, 1);
                androidDeviceDiscovery.emit("deviceFound", tempDevice);
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                chai_1.assert.isTrue(devicesService.hasDevices, "After emitting and initializing, hasDevices must be true");
                chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice, tempDevice, iOSDevice]);
                chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo, tempDevice.deviceInfo, iOSDevice.deviceInfo]);
                chai_1.assert.deepEqual(devicesService.deviceCount, 1);
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }).wait();
                chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device.");
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return false; }).wait();
                chai_1.assert.deepEqual(counter, 0, "The action must not be executed when canExecute returns false.");
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
                chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device.");
                androidDeviceDiscovery.emit("deviceLost", androidDevice);
                androidDeviceDiscovery.emit("deviceLost", tempDevice);
                iOSDeviceDiscovery.emit("deviceLost", iOSDevice);
                counter = 0;
                assertAndroidEmulatorIsStarted();
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }, { allowNoDevices: true }).wait();
                chai_1.assert.deepEqual(counter, 0, "The action must not be executed when there are no devices.");
                chai_1.assert.isTrue(logger.output.indexOf(constants.ERROR_NO_DEVICES) !== -1);
            };
            it("when deviceId is deviceIdentifier", function () {
                assertAllMethodsResults(androidDevice.deviceInfo.identifier);
            });
            it("when deviceId is index", function () {
                assertAllMethodsResults("1");
            });
            it("fails when deviceId is invalid index (less than 0)", function () {
                chai_1.assert.throws(function () { return devicesService.initialize({ deviceId: "-1" }).wait(); });
            });
            it("fails when deviceId is invalid index (more than currently connected devices)", function () {
                chai_1.assert.throws(function () { return devicesService.initialize({ deviceId: "100" }).wait(); });
            });
            it("does not fail when iOSDeviceDiscovery startLookingForDevices fails", function () {
                iOSDeviceDiscovery.startLookingForDevices = function () { throw new Error("my error"); };
                assertAllMethodsResults("1");
                chai_1.assert.isTrue(logger.traceOutput.indexOf("my error") !== -1);
            });
            it("does not fail when androidDeviceDiscovery startLookingForDevices fails", function () {
                androidDeviceDiscovery.startLookingForDevices = function () { throw new Error("my error"); };
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                devicesService.initialize({ deviceId: iOSDevice.deviceInfo.identifier }).wait();
                chai_1.assert.isTrue(logger.traceOutput.indexOf("my error") !== -1);
            });
        });
        describe("when only platform is passed", function () {
            it("execute fails when platform is iOS on non-Darwin platform and there are no devices attached when --emulator is passed", function () {
                testInjector.resolve("hostInfo").isDarwin = false;
                devicesService.initialize({ platform: "ios" }).wait();
                testInjector.resolve("options").emulator = true;
                chai_1.assert.throws(function () { return devicesService.execute(function () { counter++; return Future.fromResult(); }).wait(); }, "Cannot find connected devices. Reconnect any connected devices");
            });
            it("execute fails when platform is iOS on non-Darwin platform and there are no devices attached", function () {
                testInjector.resolve("hostInfo").isDarwin = false;
                devicesService.initialize({ platform: "ios" }).wait();
                chai_1.assert.isFalse(devicesService.hasDevices, "MUST BE FALSE!!!");
                chai_1.assert.throws(function () { return devicesService.execute(function () { counter++; return Future.fromResult(); }).wait(); }, "Cannot find connected devices. Reconnect any connected devices");
            });
            it("executes action only on iOS Simulator when iOS device is found and --emulator is passed", function () {
                testInjector.resolve("options").emulator = true;
                testInjector.resolve("hostInfo").isDarwin = true;
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                devicesService.initialize({ platform: "ios" }).wait();
                var deviceIdentifier;
                counter = 0;
                devicesService.execute(function (d) { deviceIdentifier = d.deviceInfo.identifier; counter++; return Future.fromResult(); }).wait();
                chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device. ASAAS");
                chai_1.assert.deepEqual(deviceIdentifier, iOSSimulator.deviceInfo.identifier);
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return false; }).wait();
                chai_1.assert.deepEqual(counter, 0, "The action must not be executed when canExecute returns false.");
                counter = 0;
                iOSDeviceDiscovery.emit("deviceLost", iOSDevice);
                deviceIdentifier = null;
                devicesService.execute(function (d) { deviceIdentifier = d.deviceInfo.identifier; counter++; return Future.fromResult(); }).wait();
                chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device.");
                chai_1.assert.deepEqual(deviceIdentifier, iOSSimulator.deviceInfo.identifier);
                counter = 0;
                deviceIdentifier = null;
                devicesService.execute(function (d) { deviceIdentifier = d.deviceInfo.identifier; counter++; return Future.fromResult(); }, function () { return false; }).wait();
                chai_1.assert.deepEqual(counter, 0, "The action must not be executed when canExecute returns false.");
                chai_1.assert.deepEqual(deviceIdentifier, null);
            });
            it("all methods work as expected", function () {
                chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
                androidDeviceDiscovery.emit("deviceFound", androidDevice);
                devicesService.initialize({ platform: "android" }).wait();
                chai_1.assert.deepEqual(devicesService.platform, "android");
                chai_1.assert.deepEqual(devicesService.deviceCount, 1);
                androidDeviceDiscovery.emit("deviceFound", tempDevice);
                chai_1.assert.isTrue(devicesService.hasDevices, "After emitting and initializing, hasDevices must be true");
                chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice, tempDevice]);
                chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo, tempDevice.deviceInfo]);
                chai_1.assert.deepEqual(devicesService.deviceCount, 2);
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }).wait();
                chai_1.assert.deepEqual(counter, 2, "The action must be executed on two devices.");
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return false; }).wait();
                chai_1.assert.deepEqual(counter, 0, "The action must not be executed when canExecute returns false.");
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
                chai_1.assert.deepEqual(counter, 2, "The action must be executed on two devices.");
                androidDeviceDiscovery.emit("deviceLost", androidDevice);
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
                chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device.");
                androidDeviceDiscovery.emit("deviceLost", tempDevice);
                counter = 0;
                assertAndroidEmulatorIsStarted();
                counter = 0;
                devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }, { allowNoDevices: true }).wait();
                chai_1.assert.deepEqual(counter, 0, "The action must not be executed when there are no devices.");
                chai_1.assert.isTrue(logger.output.indexOf(constants.ERROR_NO_DEVICES) !== -1);
                chai_1.assert.isFalse(androidEmulatorServices.isStartEmulatorCalled);
            });
        });
        it("when only skipInferPlatform is passed (true)", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
            devicesService.initialize({ skipInferPlatform: true }).wait();
            chai_1.assert.deepEqual(devicesService.platform, undefined);
            chai_1.assert.deepEqual(devicesService.deviceCount, 2);
            androidDeviceDiscovery.emit("deviceFound", tempDevice);
            chai_1.assert.isTrue(devicesService.hasDevices, "After emitting and initializing, hasDevices must be true");
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice, iOSDevice, tempDevice]);
            chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo, iOSDevice.deviceInfo, tempDevice.deviceInfo]);
            chai_1.assert.deepEqual(devicesService.deviceCount, 3);
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }).wait();
            chai_1.assert.deepEqual(counter, 3, "The action must be executed on two devices.");
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return false; }).wait();
            chai_1.assert.deepEqual(counter, 0, "The action must not be executed when canExecute returns false.");
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
            chai_1.assert.deepEqual(counter, 3, "The action must be executed on three devices.");
            androidDeviceDiscovery.emit("deviceLost", androidDevice);
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
            chai_1.assert.deepEqual(counter, 2, "The action must be executed on two devices.");
            androidDeviceDiscovery.emit("deviceLost", tempDevice);
            iOSDeviceDiscovery.emit("deviceLost", iOSDevice);
            counter = 0;
            chai_1.assert.throws(function () { return devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait(); });
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }, { allowNoDevices: true }).wait();
            chai_1.assert.deepEqual(counter, 0, "The action must not be executed when there are no devices.");
            chai_1.assert.isTrue(logger.output.indexOf(constants.ERROR_NO_DEVICES) !== -1);
        });
        it("when parameters are not passed and devices with same platform are detected", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            devicesService.initialize().wait();
            chai_1.assert.deepEqual(devicesService.platform, "android");
            chai_1.assert.deepEqual(devicesService.deviceCount, 1);
            androidDeviceDiscovery.emit("deviceFound", tempDevice);
            chai_1.assert.isTrue(devicesService.hasDevices, "After emitting and initializing, hasDevices must be true");
            chai_1.assert.deepEqual(devicesService.getDeviceInstances(), [androidDevice, tempDevice]);
            chai_1.assert.deepEqual(devicesService.getDevices(), [androidDevice.deviceInfo, tempDevice.deviceInfo]);
            chai_1.assert.deepEqual(devicesService.deviceCount, 2);
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }).wait();
            chai_1.assert.deepEqual(counter, 2, "The action must be executed on two devices.");
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return false; }).wait();
            chai_1.assert.deepEqual(counter, 0, "The action must not be executed when canExecute returns false.");
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
            chai_1.assert.deepEqual(counter, 2, "The action must be executed on two devices.");
            androidDeviceDiscovery.emit("deviceLost", androidDevice);
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }).wait();
            chai_1.assert.deepEqual(counter, 1, "The action must be executed on only one device.");
            androidDeviceDiscovery.emit("deviceLost", tempDevice);
            counter = 0;
            assertAndroidEmulatorIsStarted();
            counter = 0;
            devicesService.execute(function () { counter++; return Future.fromResult(); }, function () { return true; }, { allowNoDevices: true }).wait();
            chai_1.assert.deepEqual(counter, 0, "The action must not be executed when there are no devices.");
            chai_1.assert.isTrue(logger.output.indexOf(constants.ERROR_NO_DEVICES) !== -1);
        });
        it("when parameters are not passed and devices with different platforms are detected initialize should throw", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
            chai_1.assert.throws(function () { return devicesService.initialize().wait(); });
        });
        it("when parameters are not passed and devices with invalid platforms are detected initialize should work with correct devices only", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            iOSDeviceDiscovery.emit("deviceFound", {
                deviceInfo: {
                    identifier: "invalid-platform-device",
                    platform: "invalid-platform"
                }
            });
            devicesService.initialize().wait();
            chai_1.assert.isTrue(logger.output.indexOf("is not supported") !== -1);
        });
        it("when parameters are not passed and only devices with invalid platforms are detected, initialize should throw", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            iOSDeviceDiscovery.emit("deviceFound", {
                deviceInfo: {
                    identifier: "invalid-platform-device",
                    platform: "invalid-platform"
                }
            });
            chai_1.assert.throws(function () { return devicesService.initialize().wait(); });
            chai_1.assert.isTrue(logger.output.indexOf("is not supported") !== -1);
        });
        it("caches execution result and does not execute next time when called", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            devicesService.initialize({ platform: "android" }).wait();
            chai_1.assert.deepEqual(devicesService.platform, "android");
            chai_1.assert.deepEqual(devicesService.deviceCount, 1);
            devicesService.initialize({ platform: "ios" }).wait();
            chai_1.assert.deepEqual(devicesService.platform, "android");
        });
        describe("when options.emulator is true on non-Darwin OS", function () {
            beforeEach(function () {
                var options = testInjector.resolve("options");
                options.emulator = true;
                var hostInfo = testInjector.resolve("hostInfo");
                hostInfo.isDarwin = false;
            });
            it("throws when iOS platform is specified and iOS device identifier is passed", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                chai_1.assert.throws(function () { return devicesService.initialize({ platform: "ios", deviceId: iOSDevice.deviceInfo.identifier }).wait(); }, "You can use iOS simulator only on OS X.");
            });
            it("throws when iOS device identifier is passed", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                chai_1.assert.throws(function () { return devicesService.initialize({ deviceId: iOSDevice.deviceInfo.identifier }).wait(); }, "You can use iOS simulator only on OS X.");
            });
            it("throws when iOS platform is specified", function () {
                chai_1.assert.throws(function () { return devicesService.initialize({ platform: "ios" }).wait(); }, "You can use iOS simulator only on OS X.");
            });
            it("throws when paramaters are not passed, but iOS device is detected", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                chai_1.assert.throws(function () { return devicesService.initialize().wait(); }, "You can use iOS simulator only on OS X.");
            });
            it("does not throw when only skipInferPlatform is passed", function () {
                devicesService.initialize({ skipInferPlatform: true }).wait();
            });
            it("does not throw when Android platform is specified and Android device identifier is passed", function () {
                androidDeviceDiscovery.emit("deviceFound", androidDevice);
                devicesService.initialize({ platform: "android", deviceId: androidDevice.deviceInfo.identifier }).wait();
            });
        });
        describe("does not fail on Darwin when trying to use iOS simulator", function () {
            beforeEach(function () {
                var options = testInjector.resolve("options");
                options.emulator = true;
                var hostInfo = testInjector.resolve("hostInfo");
                hostInfo.isDarwin = true;
            });
            it("when iOS platform is specified and iOS device identifier is passed", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                devicesService.initialize({ platform: "ios", deviceId: iOSDevice.deviceInfo.identifier }).wait();
            });
            it("when iOS device identifier is passed", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                devicesService.initialize({ deviceId: iOSDevice.deviceInfo.identifier }).wait();
            });
            it("when iOS platform is specified", function () {
                devicesService.initialize({ platform: "ios" }).wait();
            });
            it("when paramaters are not passed, but iOS device is detected", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
                devicesService.initialize().wait();
            });
            it("when only skipInferPlatform is passed", function () {
                devicesService.initialize({ skipInferPlatform: true }).wait();
            });
            it("when iOS platform is specified and iOS simulator device identifier is passed", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSSimulator);
                devicesService.initialize({ platform: "ios", deviceId: iOSSimulator.deviceInfo.identifier }).wait();
            });
            it("when iOS simulator identifier is passed", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSSimulator);
                devicesService.initialize({ deviceId: iOSSimulator.deviceInfo.identifier }).wait();
            });
            it("when paramaters are not passed, but iOS simulator is detected", function () {
                iOSDeviceDiscovery.emit("deviceFound", iOSSimulator);
                devicesService.initialize().wait();
            });
        });
    });
    describe("setLogLevel", function () {
        it("calls deviceLogProvider's setLogLevel with correct arguments", function () {
            var deviceLogProvider = testInjector.resolve("deviceLogProvider");
            var actualLogLevel = null, actualDeviceIdentifier = null;
            deviceLogProvider.setLogLevel = function (logLevel, deviceIdentifier) {
                actualLogLevel = logLevel;
                actualDeviceIdentifier = deviceIdentifier;
            };
            var expectedLogLevel = "expectedLogLevel", expectedDeviceId = "expcetedDeviceId";
            devicesService.setLogLevel(expectedLogLevel, expectedDeviceId);
            chai_1.assert.deepEqual(actualLogLevel, expectedLogLevel);
            chai_1.assert.deepEqual(actualDeviceIdentifier, expectedDeviceId);
            devicesService.setLogLevel(expectedLogLevel);
            chai_1.assert.deepEqual(actualLogLevel, expectedLogLevel);
            chai_1.assert.deepEqual(actualDeviceIdentifier, undefined);
        });
    });
    describe("deployOnDevices", function () {
        beforeEach(function () {
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
        });
        it("returns undefined for each device on which the app is installed", function () {
            var results = devicesService.deployOnDevices([androidDevice.deviceInfo.identifier, iOSDevice.deviceInfo.identifier], "path", "packageName", "cordova");
            chai_1.assert.isTrue(results.length > 0);
            _.each(results, function (futurizedResult) {
                var realResult = futurizedResult.wait();
                chai_1.assert.isTrue(realResult === undefined, "On success, undefined should be returned.");
            });
        });
        it("does not call startApplication when canStartApplication returns false", function () {
            iOSDevice.applicationManager.canStartApplication = function () { return false; };
            iOSDevice.applicationManager.startApplication = function () {
                throw new Error("Start application must not be called for iOSDevice when canStartApplication returns false.");
            };
            var results = devicesService.deployOnDevices([androidDevice.deviceInfo.identifier, iOSDevice.deviceInfo.identifier], "path", "packageName", "cordova");
            chai_1.assert.isTrue(results.length > 0);
            Future.wait(results);
            chai_1.assert.deepEqual(results.map(function (r) { return r.get(); }), [undefined, undefined]);
        });
        it("throws error when invalid identifier is passed", function () {
            var results = devicesService.deployOnDevices(["invalidDeviceId", iOSDevice.deviceInfo.identifier], "path", "packageName", "cordova");
            chai_1.assert.throws(function () { return Future.wait(results); });
            _.each(results, function (futurizedResult) {
                var error = futurizedResult.error;
                if (error) {
                    chai_1.assert.isTrue(error.message.indexOf("invalidDeviceId") !== -1, "The message must contain the id of the invalid device.");
                }
                else {
                    chai_1.assert.isTrue(futurizedResult.get() === undefined, "On success, undefined should be returned.");
                }
            });
        });
    });
    describe("getDevicesForPlatform", function () {
        it("returns empty array when there are no devices", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("android"), []);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("ios"), []);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("invalid platform"), []);
        });
        it("returns correct results when devices with different platforms are detected", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
            var tempDeviceInstance = { deviceInfo: { identifier: "temp-device", platform: "android" } };
            androidDeviceDiscovery.emit("deviceFound", tempDeviceInstance);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("android"), [androidDevice, tempDeviceInstance]);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("ios"), [iOSDevice]);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("invalid platform"), []);
        });
        it("returns correct results when devices with different platforms are detected, assert case insensitivity", function () {
            chai_1.assert.isFalse(devicesService.hasDevices, "Initially devicesService hasDevices must be false.");
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
            var tempDeviceInstance = { deviceInfo: { identifier: "temp-device", platform: "AndroId" } };
            androidDeviceDiscovery.emit("deviceFound", tempDeviceInstance);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("android"), [androidDevice, tempDeviceInstance]);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("ios"), [iOSDevice]);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("invalid platform"), []);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("AnDroID"), [androidDevice, tempDeviceInstance]);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("Ios"), [iOSDevice]);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("inValid PlatForm"), []);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("ANDROID"), [androidDevice, tempDeviceInstance]);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("IOS"), [iOSDevice]);
            chai_1.assert.deepEqual(devicesService.getDevicesForPlatform("INVALID PLATFORM"), []);
        });
    });
    describe("isAndroidDevice", function () {
        it("returns true when android device is passed", function () {
            chai_1.assert.isTrue(devicesService.isAndroidDevice(androidDevice));
        });
        it("returns true when android emulator is passed", function () {
            chai_1.assert.isTrue(devicesService.isAndroidDevice({ deviceInfo: { platform: "android" }, isEmulator: true }));
        });
        it("returns true when android device is passed, assert case insensitivity", function () {
            chai_1.assert.isTrue(devicesService.isAndroidDevice({ deviceInfo: { platform: "aNdRoId" } }));
            chai_1.assert.isTrue(devicesService.isAndroidDevice({ deviceInfo: { platform: "ANDROID" } }));
        });
        it("returns false when iOS device is passed", function () {
            chai_1.assert.isFalse(devicesService.isAndroidDevice(iOSDevice));
            chai_1.assert.isFalse(devicesService.isAndroidDevice(iOSSimulator));
        });
        it("returns false when device with invalid platform is passed", function () {
            chai_1.assert.isFalse(devicesService.isAndroidDevice({ deviceInfo: { platform: "invalid platform" } }));
        });
    });
    describe("isiOSDevice", function () {
        it("returns true when iOS device is passed", function () {
            chai_1.assert.isTrue(devicesService.isiOSDevice(iOSDevice));
        });
        it("returns true when iOS device is passed, assert case insensitivity", function () {
            chai_1.assert.isTrue(devicesService.isiOSDevice({ deviceInfo: { platform: "iOs" } }));
            chai_1.assert.isTrue(devicesService.isiOSDevice({ deviceInfo: { platform: "IOS" } }));
        });
        it("returns false when android device is passed", function () {
            chai_1.assert.isFalse(devicesService.isiOSDevice(androidDevice));
        });
        it("returns false when device with invalid platform is passed", function () {
            chai_1.assert.isFalse(devicesService.isiOSDevice({ deviceInfo: { platform: "invalid platform" } }));
        });
        it("returns false when iOS emulator is passed", function () {
            chai_1.assert.isFalse(devicesService.isiOSDevice(iOSSimulator));
        });
    });
    describe("isiOSSimulator", function () {
        it("returns true when iOS simulator is passed", function () {
            chai_1.assert.isTrue(devicesService.isiOSSimulator(iOSSimulator));
        });
        it("returns true when iOS simulator is passed, assert case insensitivity", function () {
            chai_1.assert.isTrue(devicesService.isiOSSimulator({ deviceInfo: { platform: "iOs" }, isEmulator: true }));
            chai_1.assert.isTrue(devicesService.isiOSSimulator({ deviceInfo: { platform: "IOS" }, isEmulator: true }));
        });
        it("returns false when iOS device is passed", function () {
            chai_1.assert.isFalse(devicesService.isiOSSimulator(iOSDevice));
        });
        it("returns false when Androd device or Android Emulator is passed", function () {
            chai_1.assert.isFalse(devicesService.isiOSSimulator(androidDevice));
            chai_1.assert.isFalse(devicesService.isiOSSimulator({ deviceInfo: { platform: "android" }, isEmulator: true }));
        });
        it("returns false when device with invalid platform is passed", function () {
            chai_1.assert.isFalse(devicesService.isiOSSimulator({ deviceInfo: { platform: "invalid platform" } }));
        });
    });
    describe("getDeviceByDeviceOption", function () {
        it("returns undefined when devicesService is not initialized", function () {
            chai_1.assert.deepEqual(devicesService.getDeviceByDeviceOption(), undefined);
        });
        it("returns undefined when devicesService is initialized with platform only", function () {
            devicesService.initialize({ platform: "android" }).wait();
            chai_1.assert.deepEqual(devicesService.getDeviceByDeviceOption(), undefined);
        });
        it("returns undefined when devicesService is initialized with skipInferPlatform only", function () {
            devicesService.initialize({ skipInferPlatform: true }).wait();
            chai_1.assert.deepEqual(devicesService.getDeviceByDeviceOption(), undefined);
        });
        it("returns deviceIdentifier when devicesService is initialized with deviceId only", function () {
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            devicesService.initialize({ deviceId: androidDevice.deviceInfo.identifier }).wait();
            chai_1.assert.deepEqual(devicesService.getDeviceByDeviceOption(), androidDevice);
        });
        it("returns deviceIdentifier when devicesService is initialized with deviceId (passed as number)", function () {
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            devicesService.initialize({ deviceId: "1" }).wait();
            chai_1.assert.deepEqual(devicesService.getDeviceByDeviceOption(), androidDevice);
        });
        it("returns deviceIdentifier when devicesService is initialized with deviceId and platform", function () {
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            devicesService.initialize({ deviceId: androidDevice.deviceInfo.identifier, platform: "android" }).wait();
            chai_1.assert.deepEqual(devicesService.getDeviceByDeviceOption(), androidDevice);
        });
        it("returns deviceIdentifier when devicesService is initialized with deviceId (passed as number) and platform", function () {
            androidDeviceDiscovery.emit("deviceFound", androidDevice);
            devicesService.initialize({ deviceId: "1", platform: "android" }).wait();
            chai_1.assert.deepEqual(devicesService.getDeviceByDeviceOption(), androidDevice);
        });
    });
    describe("isCompanionAppInstalledOnAllDevices", function () {
        var $companionAppsService;
        var deviceIdentifiers = [iOSDevice.deviceInfo.identifier, androidDevice.deviceInfo.identifier];
        beforeEach(function () {
            $companionAppsService = testInjector.resolve("companionAppsService");
            $companionAppsService.getCompanionAppIdentifier = function (framework, platform) { return "companion-app"; };
            devicesService.getDeviceByIdentifier = function (identifier) {
                return (identifier === androidDevice.deviceInfo.identifier ? androidDevice : iOSDevice);
            };
        });
        it("should return correct result for all of the devices passed as argument.", function () {
            var expectedResult = [true, true];
            mockIsAppInstalled([iOSDevice, androidDevice], expectedResult);
            var result = _.map(devicesService.isCompanionAppInstalledOnDevices(deviceIdentifiers, constants.TARGET_FRAMEWORK_IDENTIFIERS.Cordova), function (future) { return future.wait().isInstalled; });
            chai_1.assert.deepEqual(result.length, deviceIdentifiers.length);
        });
        it("should return correct result when all of the devices have the companion app installed.", function () {
            var expectedResult = [true, true];
            mockIsAppInstalled([iOSDevice, androidDevice], expectedResult);
            var result = _.map(devicesService.isCompanionAppInstalledOnDevices(deviceIdentifiers, constants.TARGET_FRAMEWORK_IDENTIFIERS.Cordova), function (future) { return future.wait().isInstalled; });
            chai_1.assert.deepEqual(result, expectedResult);
        });
        it("should return correct result when some of the devices have the companion app installed.", function () {
            var expectedResult = [true, false];
            mockIsAppInstalled([iOSDevice, androidDevice], expectedResult);
            var result = _.map(devicesService.isCompanionAppInstalledOnDevices(deviceIdentifiers, constants.TARGET_FRAMEWORK_IDENTIFIERS.Cordova), function (future) { return future.wait().isInstalled; });
            chai_1.assert.deepEqual(result, expectedResult);
        });
        it("should return correct result when none of the devices have the companion app installed.", function () {
            var expectedResult = [false, false];
            mockIsAppInstalled([iOSDevice, androidDevice], expectedResult);
            var result = _.map(devicesService.isCompanionAppInstalledOnDevices(deviceIdentifiers, constants.TARGET_FRAMEWORK_IDENTIFIERS.Cordova), function (future) { return future.wait().isInstalled; });
            chai_1.assert.deepEqual(result, expectedResult);
        });
    });
    describe("startDeviceDetectionInterval", function () {
        var setIntervalsCalledCount;
        beforeEach(function () {
            setIntervalsCalledCount = 0;
            mockSetInterval({ shouldExecuteCallback: true });
        });
        afterEach(function () {
            devicesService.stopDeviceDetectionInterval().wait();
            resetDefaultSetInterval();
        });
        it("should start device detection interval.", function () {
            var hasStartedDeviceDetectionInterval = false;
            mockSetInterval({
                shouldExecuteCallback: false,
                testCaseCallback: function () {
                    hasStartedDeviceDetectionInterval = true;
                }
            });
            devicesService.startDeviceDetectionInterval();
            chai_1.assert.isTrue(hasStartedDeviceDetectionInterval);
        });
        it("should not start device detection interval if there is one running.", function () {
            global.setInterval = function (callback, ms) {
                var args = [];
                for (var _i = 2; _i < arguments.length; _i++) {
                    args[_i - 2] = arguments[_i];
                }
                callback();
                return {
                    ref: function () { },
                    unref: function () {
                        setIntervalsCalledCount++;
                        return intervalId++;
                    }
                };
            };
            devicesService.startDeviceDetectionInterval();
            devicesService.startDeviceDetectionInterval();
            devicesService.startDeviceDetectionInterval();
            chai_1.assert.deepEqual(setIntervalsCalledCount, 1);
        });
        describe("ios devices check", function () {
            var $iOSDeviceDiscovery;
            beforeEach(function () {
                $iOSDeviceDiscovery = testInjector.resolve("iOSDeviceDiscovery");
            });
            it("should check for ios devices.", function () {
                var hasCheckedForIosDevices = false;
                $iOSDeviceDiscovery.checkForDevices = function () {
                    return (function () {
                        hasCheckedForIosDevices = true;
                    }).future()();
                };
                devicesService.startDeviceDetectionInterval();
                chai_1.assert.isTrue(hasCheckedForIosDevices);
            });
            it("should not throw if ios device check fails throws an exception.", function () {
                $iOSDeviceDiscovery.checkForDevices = throwErrorFuture;
                var callback = function () {
                    devicesService.startDeviceDetectionInterval.call(devicesService);
                };
                chai_1.assert.doesNotThrow(callback);
            });
        });
        describe("android devices check", function () {
            var $androidDeviceDiscovery;
            beforeEach(function () {
                $androidDeviceDiscovery = testInjector.resolve("androidDeviceDiscovery");
            });
            it("should check for android devices.", function () {
                var hasCheckedForAndroidDevices = false;
                $androidDeviceDiscovery.startLookingForDevices = function () {
                    return (function () {
                        hasCheckedForAndroidDevices = true;
                    }).future()();
                };
                devicesService.startDeviceDetectionInterval();
                chai_1.assert.isTrue(hasCheckedForAndroidDevices);
            });
            it("should not throw if android device check fails throws an exception.", function () {
                $androidDeviceDiscovery.startLookingForDevices = throwErrorFuture;
                var callback = function () {
                    devicesService.startDeviceDetectionInterval.call(devicesService);
                };
                chai_1.assert.doesNotThrow(callback);
            });
        });
        describe("ios simulator check", function () {
            var $iOSSimulatorDiscovery;
            var $hostInfo;
            var hasCheckedForIosSimulator;
            beforeEach(function () {
                $iOSSimulatorDiscovery = testInjector.resolve("iOSSimulatorDiscovery");
                $iOSSimulatorDiscovery.checkForDevices = function () {
                    return (function () {
                        hasCheckedForIosSimulator = true;
                    }).future()();
                };
                $hostInfo = testInjector.resolve("hostInfo");
                $hostInfo.isDarwin = true;
                hasCheckedForIosSimulator = false;
            });
            it("should check for ios simulator if the host is Darwin.", function () {
                devicesService.startDeviceDetectionInterval();
                chai_1.assert.isTrue(hasCheckedForIosSimulator);
            });
            it("should not check for ios simulator if the host is not Darwin.", function () {
                $hostInfo.isDarwin = false;
                devicesService.startDeviceDetectionInterval();
                chai_1.assert.isFalse(hasCheckedForIosSimulator);
            });
            it("should not throw if ios simulator check fails throws an exception.", function () {
                $iOSSimulatorDiscovery.checkForDevices = throwErrorFuture;
                var callback = function () {
                    devicesService.startDeviceDetectionInterval.call(devicesService);
                };
                chai_1.assert.doesNotThrow(callback);
            });
        });
        describe("check for application updates", function () {
            var $iOSDeviceDiscovery;
            var $androidDeviceDiscovery;
            var hasCheckedForAndroidAppUpdates;
            var hasCheckedForIosAppUpdates;
            beforeEach(function () {
                hasCheckedForAndroidAppUpdates = false;
                hasCheckedForIosAppUpdates = false;
                $iOSDeviceDiscovery = testInjector.resolve("iOSDeviceDiscovery");
                $androidDeviceDiscovery = testInjector.resolve("androidDeviceDiscovery");
                androidDevice.applicationManager.checkForApplicationUpdates = function () {
                    return (function () {
                        hasCheckedForAndroidAppUpdates = true;
                    }).future()();
                };
                iOSDevice.applicationManager.checkForApplicationUpdates = function () {
                    return (function () {
                        hasCheckedForIosAppUpdates = true;
                    }).future()();
                };
                $androidDeviceDiscovery.emit("deviceFound", androidDevice);
                $iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
            });
            it("should check for application updates for all connected devices.", function () {
                devicesService.startDeviceDetectionInterval();
                chai_1.assert.isTrue(hasCheckedForAndroidAppUpdates);
                chai_1.assert.isTrue(hasCheckedForIosAppUpdates);
            });
            it("should check for application updates if the check on one device throws an exception.", function () {
                iOSDevice.applicationManager.checkForApplicationUpdates = throwErrorFuture;
                var callback = function () {
                    devicesService.startDeviceDetectionInterval.call(devicesService);
                };
                chai_1.assert.doesNotThrow(callback);
                chai_1.assert.isTrue(hasCheckedForAndroidAppUpdates);
            });
            it("should not throw if all checks for application updates on all devices throw exceptions.", function () {
                iOSDevice.applicationManager.checkForApplicationUpdates = throwErrorFuture;
                androidDevice.applicationManager.checkForApplicationUpdates = throwErrorFuture;
                var callback = function () {
                    devicesService.startDeviceDetectionInterval.call(devicesService);
                };
                chai_1.assert.doesNotThrow(callback);
            });
        });
    });
    describe("stopDeviceDetectionInterval", function () {
        beforeEach(function () {
            mockSetInterval({ shouldExecuteCallback: true });
        });
        afterEach(function () {
            devicesService.stopDeviceDetectionInterval().wait();
            resetDefaultSetInterval();
        });
        it("should stop the device detection interval.", function () {
            var setIntervalStartedCount = 0;
            mockSetInterval({
                shouldExecuteCallback: false,
                testCaseCallback: function () {
                    setIntervalStartedCount++;
                }
            });
            devicesService.startDeviceDetectionInterval();
            devicesService.stopDeviceDetectionInterval().wait();
            devicesService.startDeviceDetectionInterval();
            chai_1.assert.deepEqual(setIntervalStartedCount, 2);
        });
        it("should not stop the device detection interval if there is not interval running.", function () {
            var clearIntervalCount = 0;
            global.clearInterval = function () {
                clearIntervalCount++;
            };
            devicesService.startDeviceDetectionInterval();
            devicesService.stopDeviceDetectionInterval().wait();
            devicesService.stopDeviceDetectionInterval().wait();
            chai_1.assert.deepEqual(clearIntervalCount, 1);
        });
    });
    describe("detectCurrentlyAttachedDevices", function () {
        var $androidDeviceDiscovery;
        var $iOSDeviceDiscovery;
        var $iOSSimulatorDiscovery;
        var $hostInfo;
        beforeEach(function () {
            $hostInfo = testInjector.resolve("hostInfo");
            $androidDeviceDiscovery = testInjector.resolve("androidDeviceDiscovery");
            $iOSDeviceDiscovery = testInjector.resolve("iOSDeviceDiscovery");
            $iOSSimulatorDiscovery = testInjector.resolve("iOSSimulatorDiscovery");
        });
        it("should start looking for android devices.", function () {
            var hasStartedLookingForAndroidDevices = false;
            $androidDeviceDiscovery.startLookingForDevices = function () {
                return (function () {
                    hasStartedLookingForAndroidDevices = true;
                }).future()();
            };
            devicesService.detectCurrentlyAttachedDevices().wait();
            chai_1.assert.isTrue(hasStartedLookingForAndroidDevices);
        });
        it("should start looking for ios devices.", function () {
            var hasStartedLookingForIosDevices = false;
            $iOSDeviceDiscovery.startLookingForDevices = function () {
                return (function () {
                    hasStartedLookingForIosDevices = true;
                }).future()();
            };
            devicesService.detectCurrentlyAttachedDevices().wait();
            chai_1.assert.isTrue(hasStartedLookingForIosDevices);
        });
        it("should start looking for ios simulator if the host is Darwin.", function () {
            var hasStartedLookingForIosSimulator = false;
            $hostInfo.isDarwin = true;
            $iOSSimulatorDiscovery.startLookingForDevices = function () {
                return (function () {
                    hasStartedLookingForIosSimulator = true;
                }).future()();
            };
            devicesService.detectCurrentlyAttachedDevices().wait();
            chai_1.assert.isTrue(hasStartedLookingForIosSimulator);
        });
        it("should not start looking for ios simulator if the host is not Darwin.", function () {
            var hasStartedLookingForIosSimulator = false;
            $hostInfo.isDarwin = false;
            $iOSSimulatorDiscovery.startLookingForDevices = function () {
                return (function () {
                    hasStartedLookingForIosSimulator = true;
                }).future()();
            };
            devicesService.detectCurrentlyAttachedDevices().wait();
            chai_1.assert.isFalse(hasStartedLookingForIosSimulator);
        });
        it("should not throw if any of the device discovery services throws an exception.", function () {
            $iOSDeviceDiscovery.startLookingForDevices = throwErrorFuture;
            var callback = function () {
                devicesService.detectCurrentlyAttachedDevices.call(devicesService).wait();
            };
            chai_1.assert.doesNotThrow(callback);
        });
    });
    it("should call mapAbstractToTcpPort of android process service with the same parameters.", function () {
        var expectedDeviceIdentifier = "123456789";
        var expectedAppIdentifier = "com.telerik.myapp";
        var expectedFramework = constants.TARGET_FRAMEWORK_IDENTIFIERS.Cordova;
        var actualDeviceIdentifier;
        var actualAppIdentifier;
        var actualFramework;
        var $androidProcessService = testInjector.resolve("androidProcessService");
        $androidProcessService.mapAbstractToTcpPort = function (deviceIdentifier, appIdentifier, framework) {
            return (function () {
                actualDeviceIdentifier = deviceIdentifier;
                actualAppIdentifier = appIdentifier;
                actualFramework = framework;
                return "";
            }).future()();
        };
        devicesService.mapAbstractToTcpPort(expectedDeviceIdentifier, expectedAppIdentifier, expectedFramework).wait();
        chai_1.assert.deepEqual(actualDeviceIdentifier, expectedDeviceIdentifier);
        chai_1.assert.deepEqual(actualAppIdentifier, expectedAppIdentifier);
        chai_1.assert.deepEqual(actualFramework, expectedFramework);
    });
    it("should get debuggable apps correctly for multiple devices.", function () {
        var $iOSDeviceDiscovery = testInjector.resolve("iOSDeviceDiscovery");
        var $androidDeviceDiscovery = testInjector.resolve("androidDeviceDiscovery");
        $androidDeviceDiscovery.emit("deviceFound", androidDevice);
        $iOSDeviceDiscovery.emit("deviceFound", iOSDevice);
        var androidDebuggableApps = [{
                appIdentifier: "com.telerik.myapp",
                deviceIdentifier: androidDevice.deviceInfo.identifier,
                framework: constants.TARGET_FRAMEWORK_IDENTIFIERS.Cordova
            }, {
                appIdentifier: "com.telerik.myapp1",
                deviceIdentifier: androidDevice.deviceInfo.identifier,
                framework: constants.TARGET_FRAMEWORK_IDENTIFIERS.NativeScript
            }];
        var iosDebuggableApps = [{
                appIdentifier: "com.telerik.myapp2",
                deviceIdentifier: iOSDevice.deviceInfo.identifier,
                framework: constants.TARGET_FRAMEWORK_IDENTIFIERS.Cordova
            }, {
                appIdentifier: "com.telerik.myapp3",
                deviceIdentifier: iOSDevice.deviceInfo.identifier,
                framework: constants.TARGET_FRAMEWORK_IDENTIFIERS.NativeScript
            }];
        androidDevice.applicationManager.getDebuggableApps = function () {
            return (function () {
                return androidDebuggableApps;
            }).future()();
        };
        iOSDevice.applicationManager.getDebuggableApps = function () {
            return (function () {
                return iosDebuggableApps;
            }).future()();
        };
        var futures = devicesService.getDebuggableApps([androidDevice.deviceInfo.identifier, iOSDevice.deviceInfo.identifier]);
        var debuggableApps = _(futures)
            .map(function (future) { return future.wait(); })
            .flatten()
            .value();
        chai_1.assert.deepEqual(debuggableApps, _.concat(androidDebuggableApps, iosDebuggableApps));
    });
    describe("getDebuggableViews", function () {
        var $androidDeviceDiscovery;
        var debuggableViews = [{
                description: "descrition",
                webSocketDebuggerUrl: "debugurl",
                url: "url",
                type: "type",
                title: "title",
                id: "id1",
                devtoolsFrontendUrl: "frontenturl"
            }, {
                description: "descrition1",
                webSocketDebuggerUrl: "debugurl1",
                url: "url1",
                type: "type1",
                title: "title1",
                id: "id2",
                devtoolsFrontendUrl: "frontenturl1"
            }];
        beforeEach(function () {
            $androidDeviceDiscovery = testInjector.resolve("androidDeviceDiscovery");
            $androidDeviceDiscovery.emit("deviceFound", androidDevice);
        });
        it("should get the correct debuggable views.", function () {
            androidDevice.applicationManager.getDebuggableAppViews = function (appIdentifiers) {
                return (function () {
                    var result = {};
                    result[appIdentifiers[0]] = debuggableViews;
                    return result;
                }).future()();
            };
            var actualDebuggableViews = devicesService.getDebuggableViews(androidDevice.deviceInfo.identifier, "com.telerik.myapp").wait();
            chai_1.assert.deepEqual(actualDebuggableViews, debuggableViews);
        });
        it("should return undefined if debuggable views are found for otheer app but not for the specified.", function () {
            androidDevice.applicationManager.getDebuggableAppViews = function (appIdentifiers) {
                return (function () {
                    var result = {};
                    result["com.telerik.otherApp"] = debuggableViews;
                    return result;
                }).future()();
            };
            var actualDebuggableViews = devicesService.getDebuggableViews(androidDevice.deviceInfo.identifier, "com.telerik.myapp").wait();
            chai_1.assert.deepEqual(actualDebuggableViews, undefined);
        });
    });
});
