You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ja...@apache.org on 2018/12/03 17:22:08 UTC

[cordova-paramedic] 01/01: ParamedicSauceLabs: all Sauce Labs only methods extracted from paramedic.js

This is an automated email from the ASF dual-hosted git repository.

janpio pushed a commit to branch janpio-split_saucelabs
in repository https://gitbox.apache.org/repos/asf/cordova-paramedic.git

commit 36dc1900c10425ed7dd5dbd7d021ea223607a73a
Author: Jan Piotrowski <pi...@gmail.com>
AuthorDate: Mon Dec 3 18:21:53 2018 +0100

    ParamedicSauceLabs: all Sauce Labs only methods extracted from paramedic.js
---
 lib/{paramedic.js => ParamedicSauceLabs.js} | 519 ++--------------------------
 lib/paramedic.js                            | 501 +--------------------------
 2 files changed, 35 insertions(+), 985 deletions(-)

diff --git a/lib/paramedic.js b/lib/ParamedicSauceLabs.js
similarity index 51%
copy from lib/paramedic.js
copy to lib/ParamedicSauceLabs.js
index 9f01fcb..15f9a1d 100644
--- a/lib/paramedic.js
+++ b/lib/ParamedicSauceLabs.js
@@ -1,3 +1,5 @@
+#!/usr/bin/env node
+
 /**
     Licensed to the Apache Software Foundation (ASF) under one
     or more contributor license agreements.  See the NOTICE file
@@ -16,467 +18,26 @@
     specific language governing permissions and limitations
     under the License.
 */
-
-var cp              = require('child_process');
-var exec            = require('./utils').exec;
-var execPromise     = require('./utils').execPromise;
-var shell           = require('shelljs');
-var Server          = require('./LocalServer');
 var path            = require('path');
+var cp              = require('child_process');
 var Q               = require('q');
+var shell           = require('shelljs');
+var randomstring    = require('randomstring');
 var fs              = require('fs');
 var wd              = require('wd');
 var SauceLabs       = require('saucelabs');
-var randomstring    = require('randomstring');
 var sauceConnectLauncher    = require('sauce-connect-launcher');
 
+var exec            = require('./utils').exec;
+var execPromise     = require('./utils').execPromise;
 var logger          = require('./utils').logger;
 var util            = require('./utils').utilities;
-var Reporters       = require('./Reporters');
-var ParamedicKill   = require('./ParamedicKill');
-var AppiumRunner    = require('./appium/AppiumRunner');
 var appPatcher      = require('./appium/helpers/appPatcher');
-var ParamedicLogCollector   = require('./ParamedicLogCollector');
-var ParamediciOSPermissions = require('./ParamediciOSPermissions');
-var ParamedicTargetChooser  = require('./ParamedicTargetChooser');
-var ParamedicAppUninstall   = require('./ParamedicAppUninstall');
-var ParamedicApp   = require('./ParamedicApp');
-
-//this will add custom promise chain methods to the driver prototype
-require('./appium/helpers/wdHelper');
 
-// Time to wait for initial device connection.
-// If device has not connected within this interval the tests are stopped.
-var INITIAL_CONNECTION_TIMEOUT = 540000; // 9mins
-
-
-function ParamedicRunner(config, _callback) {
-    this.tempFolder = null;
-
-    this.config = config;
-    this.targetObj = undefined;
-
-    exec.setVerboseLevel(config.isVerbose());
+function ParamedicSauceLabs() {
 }
 
-ParamedicRunner.prototype.run = function () {
-    var self = this;
-    var isTestPassed = false;
-
-    self.checkConfig();
-
-    return Q().then(function () {
-        // create project and prepare (install plugins, setup test startpage, install platform, check platform requirements)
-        var paramedicApp = new ParamedicApp(self.config, self.storedCWD);
-        self.tempFolder = paramedicApp.createTempProject();
-        shell.pushd(self.tempFolder.name);
-        return paramedicApp.prepareProjectToRunTests();
-    })
-    .then(function () {
-        if (self.config.runMainTests()) {
-            // start server
-            var noListener = (self.config.getPlatformId() === util.BROWSER) && self.config.shouldUseSauce();
-            return Server.startServer(self.config.getPorts(), self.config.getExternalServerUrl(), self.config.getUseTunnel(), noListener);
-        }
-    })
-    .then(function (server) {
-        if (self.config.runMainTests()) {
-            // configure server usage
-            self.server = server;
-
-            self.injectReporters();
-            self.subcribeForEvents();
-
-            var logUrl = self.server.getConnectionUrl(self.config.getPlatformId());
-            self.writeMedicJson(logUrl);
-
-            logger.normal('Start running tests at ' + (new Date()).toLocaleTimeString());
-        }
-        // run tests
-        return self.runTests();
-    })
-    .timeout(self.config.getTimeout(), 'Timed out after waiting for ' + self.config.getTimeout() + ' ms.')
-    .fin(function (result) {
-        isTestPassed = result;
-        logger.normal('Completed tests at ' + (new Date()).toLocaleTimeString());
-        // if we do --justbuild  or run on sauce,
-        // we should NOT do actions below
-        if (self.config.getAction() !== 'build' && !self.config.shouldUseSauce()) {
-            // collect logs and uninstall app
-            self.collectDeviceLogs();
-            return self.uninstallApp()
-                .fail(function () {
-                    // do not fail if uninstall fails
-                })
-                .fin(function () {
-                    self.killEmulatorProcess();
-                });
-        }
-        return self.displaySauceDetails(self.sauceBuildName);
-    })
-    .fin(function () {
-        self.cleanUpProject();
-    });
-};
-
-ParamedicRunner.prototype.checkConfig = function () {
-    this.checkSauceRequirements();
-    if (!this.config.runMainTests() && !this.config.runAppiumTests()) {
-        throw new Error('No tests to run: both --skipAppiumTests and --skipMainTests are used');
-    }
-    checkCli: {
-        if (this.config.getCli() !== 'cordova' && this.config.getCli() !== 'phonegap') {
-            if (path.isAbsolute(this.config.getCli())) {
-                break checkCli;
-            }
-            var cliAbsolutePath = path.resolve(this.config.getCli());
-            this.config.setCli(cliAbsolutePath);
-        }
-    }
-    logger.info('cordova-paramedic: Will use the following cli: ' + this.config.getCli());
-};
-
-ParamedicRunner.prototype.setPermissions = function () {
-    var applicationsToGrantPermission = [
-        'kTCCServiceAddressBook'
-    ];
-    if(this.config.getPlatformId() === util.IOS) {
-        logger.info('cordova-paramedic: Setting required permissions.');
-        var tccDb = this.config.getTccDb();
-        if(tccDb) {
-            var appName                 = util.PARAMEDIC_DEFAULT_APP_NAME;
-            var paramediciOSPermissions = new ParamediciOSPermissions(appName, tccDb, this.targetObj);
-            paramediciOSPermissions.updatePermissions(applicationsToGrantPermission);
-        }
-    }
-};
-
-ParamedicRunner.prototype.injectReporters = function () {
-    var self = this;
-    var reporters = Reporters.getReporters(self.config.getOutputDir());
-
-    ['jasmineStarted', 'specStarted', 'specDone',
-    'suiteStarted', 'suiteDone', 'jasmineDone'].forEach(function(route) {
-        reporters.forEach(function(reporter) {
-            if (reporter[route] instanceof Function)
-                self.server.on(route, reporter[route].bind(reporter));
-        });
-    });
-};
-
-ParamedicRunner.prototype.subcribeForEvents = function () {
-    this.server.on('deviceLog', function (data) {
-        logger.verbose('device|console.' + data.type + ': '  + data.msg[0]);
-    });
-
-    this.server.on('deviceInfo', function (data) {
-        logger.normal('cordova-paramedic: Device info: ' + JSON.stringify(data));
-    });
-};
-
-ParamedicRunner.prototype.writeMedicJson = function(logUrl) {
-    logger.normal('cordova-paramedic: writing medic log url to project ' + logUrl);
-
-    fs.writeFileSync(path.join('www','medic.json'), JSON.stringify({logurl:logUrl}));
-};
-
-ParamedicRunner.prototype.buildApp = function () {
-    var self = this;
-    var command = this.getCommandForBuilding();
-
-    logger.normal('cordova-paramedic: running command ' + command);
-
-    return execPromise(command)
-    .then(function(output) {
-        if (output.indexOf ('BUILD FAILED') >= 0) {
-            throw new Error('Unable to build the project.');
-        }
-    }, function(output) {
-        // this trace is automatically available in verbose mode
-        // so we check for this flag to not trace twice
-        if (!self.config.verbose) {
-            logger.normal(output);
-        }
-        throw new Error('Unable to build the project.');
-    });
-};
-
-ParamedicRunner.prototype.maybeRunFileTransferServer = function () {
-    var self = this;
-    return Q().then(function () {
-        var plugins = self.config.getPlugins();
-        for (var i = 0; i < plugins.length; i++) {
-            if (plugins[i].indexOf('cordova-plugin-file-transfer') >= 0 && !self.config.getFileTransferServer() && !self.config.isCI()) {
-                return self.server.startFileTransferServer(self.tempFolder.name);
-            }
-        }
-    });
-};
-
-ParamedicRunner.prototype.runLocalTests = function () {
-    var self = this;
-    var runProcess = null;
-
-    // checking for Android platform here because in this case we still need to start an emulator
-    // will check again a bit lower
-    if (!self.config.runMainTests() && self.config.getPlatformId() !== util.ANDROID) {
-        logger.normal('Skipping main tests...');
-        return Q(util.TEST_PASSED);
-    }
-
-    logger.info('cordova-paramedic: running tests locally');
-
-    return Q().then(function () {
-        return self.maybeRunFileTransferServer();
-    })
-    .then(function () {
-        return self.getCommandForStartingTests();
-    })
-    .then(function(command) {
-        self.setPermissions();
-        logger.normal('cordova-paramedic: running command ' + command);
-
-        if (self.config.getPlatformId() !== util.BROWSER) {
-            return execPromise(command);
-        }
-        console.log('$ ' + command);
-        runProcess = cp.exec(command, function () {
-            // a precaution not to try to kill some other process
-            runProcess = null;
-        });
-    })
-    .then(function() {
-        // skipping here and not at the beginning because we need to
-        // start up the Android emulator for Appium tests to run on
-        if (!self.config.runMainTests()) {
-            logger.normal('Skipping main tests...');
-            return util.TEST_PASSED;
-        }
-
-        // skip tests if it was just build
-        if (self.shouldWaitForTestResult()) {
-            return Q.promise(function(resolve, reject) {
-                // reject if timed out
-                self.waitForConnection().catch(reject);
-                // resolve if got results
-                self.waitForTests().then(resolve);
-            });
-        }
-
-        return util.TEST_PASSED; // if we're not waiting for a test result, just report tests as passed
-    })
-    .fin(function (result) {
-        if (runProcess) {
-            return Q.Promise(function (resolve, reject) {
-                util.killProcess(runProcess.pid, function () {
-                    resolve(result);
-                });
-            });
-        }
-        return result;
-    });
-};
-
-ParamedicRunner.prototype.runAppiumTests = function (useSauce) {
-    var platform = this.config.getPlatformId();
-    var self = this;
-    logger.normal('Start running Appium tests...');
-
-    if (self.config.getAction() === 'build') {
-        logger.normal('Skipping Appium tests: action = build ...');
-        return Q(util.TEST_PASSED);
-    }
-    if (!self.config.runAppiumTests()) {
-        logger.normal('Skipping Appium tests: not configured to run ...');
-        return Q(util.TEST_PASSED);
-    }
-    if (platform !== util.ANDROID && platform !== util.IOS) {
-        logger.warn('Unsupported platform for Appium test run: ' + platform);
-        // just skip Appium tests
-        return Q(util.TEST_PASSED);
-    }
-    if (!useSauce && (!self.targetObj || !self.targetObj.target)) {
-        throw new Error('Cannot determine local device name for Appium');
-    }
-
-    logger.normal('Running Appium tests ' + (useSauce ? 'on Sauce Labs' : 'locally'));
-
-    var options = {
-        platform: self.config.getPlatformId(),
-        appPath: self.tempFolder.name,
-        pluginRepos: self.config.getPlugins().map(function (plugin) {
-            return path.join(self.tempFolder.name, 'plugins', path.basename(plugin));
-        }),
-        appiumDeviceName: self.targetObj && self.targetObj.target,
-        appiumPlatformVersion: null,
-        screenshotPath: path.join(process.cwd(), 'appium_screenshots'),
-        output: self.config.getOutputDir(),
-        verbose: self.config.isVerbose(),
-        sauce: useSauce,
-        browserify: self.config.isBrowserify,
-        cli: self.config.getCli(),
-    };
-    if (useSauce) {
-        options.sauceAppPath = 'sauce-storage:' + this.getAppName();
-        options.sauceUser = this.config.getSauceUser();
-        options.sauceKey = this.config.getSauceKey();
-        options.sauceCaps = this.getSauceCaps();
-        options.sauceCaps.name += '_Appium';
-    }
-
-    var appiumRunner = new AppiumRunner(options);
-    if (appiumRunner.options.testPaths && appiumRunner.options.testPaths.length === 0) {
-        logger.warn('Couldn\'t find Appium tests, skipping...');
-        return Q(util.TEST_PASSED);
-    }
-    return Q()
-    .then(function () {
-        return appiumRunner.prepareApp();
-    })
-    .then(function () {
-        if (useSauce) {
-            return self.packageApp()
-            .then(self.uploadApp.bind(self));
-        }
-    })
-    .then(function () {
-        return appiumRunner.runTests(useSauce);
-    });
-};
-
-ParamedicRunner.prototype.runTests = function () {
-    var isTestPassed = false;
-    var self = this;
-    // Sauce Labs
-    if (this.config.shouldUseSauce()) {
-        return this.runSauceTests()
-        .then(function (result) {
-            isTestPassed = result;
-            return self.runAppiumTests(true);
-        })
-        .then(function (isAppiumTestPassed) {
-            return isTestPassed == util.TEST_PASSED && isAppiumTestPassed == util.TEST_PASSED;
-        });
-    // Not Sauce Labs
-    } else {
-        return this.runLocalTests()
-        .then(function (result) {
-            isTestPassed = result;
-        })
-        .then(self.runAppiumTests.bind(this))
-        .then(function (isAppiumTestPassed) {
-            return isTestPassed == util.TEST_PASSED && isAppiumTestPassed == util.TEST_PASSED;
-        });
-    }
-};
-
-ParamedicRunner.prototype.waitForTests = function () {
-    var self = this;
-    logger.info('cordova-paramedic: waiting for test results');
-    return Q.promise(function(resolve, reject) {
-
-        // time out if connection takes too long
-        var ERR_MSG = 'waitForTests: Seems like device not connected to local server in ' + INITIAL_CONNECTION_TIMEOUT / 1000 + ' secs';
-        setTimeout(function() {
-            if (!self.server.isDeviceConnected()) {
-                reject(new Error(ERR_MSG));
-            }
-        }, INITIAL_CONNECTION_TIMEOUT);
-
-        self.server.on('jasmineDone', function (data) {
-            logger.info('cordova-paramedic: tests have been completed');
-
-            var isTestPassed = (data.specResults.specFailed === 0);
-
-            resolve(isTestPassed);
-        });
-
-        self.server.on('disconnect', function () {
-            reject(new Error('device is disconnected before passing the tests'));
-        });
-    });
-};
-
-ParamedicRunner.prototype.getCommandForStartingTests = function () {
-    var self = this;
-    var cmd  = self.config.getCli() + ' ' + this.config.getAction() + ' ' + this.config.getPlatformId() + util.PARAMEDIC_COMMON_CLI_ARGS;
-
-    function addConfigArgs(cmd) {
-        if (self.config.getArgs()) {
-            cmd += ' ' + self.config.getArgs();
-        }
-        return cmd;
-    }
-
-    if (self.config.getPlatformId() === util.BROWSER) {
-        return addConfigArgs(cmd);
-    }
-
-    var paramedicTargetChooser = new ParamedicTargetChooser(this.tempFolder.name, this.config);
-
-    if (self.config.getAction() === 'build' || (self.config.getPlatformId() === util.WINDOWS && self.config.getArgs().indexOf('appx=8.1-phone') < 0)) {
-        //The app is to be run as a store app or just build. So no need to choose a target.
-        return Q(addConfigArgs(cmd));
-    }
-
-    // For now we always trying to run test app on emulator
-    return Q().then(function () {
-        var configTarget = self.config.getTarget();
-        return paramedicTargetChooser.chooseTarget(/*useEmulator=*/true, /*preferredTarget=*/configTarget);
-    })
-    .then(function(targetObj){
-        self.targetObj = targetObj;
-        cmd += ' --target ' + self.targetObj.target;
-
-        // CB-11472 In case of iOS provide additional '--emulator' flag, otherwise
-        // 'cordova run ios --target' would hang waiting for device with name
-        // as specified in 'target' in case if any device is physically connected
-        if (self.config.getPlatformId() === util.IOS) {
-            cmd += ' --emulator';
-        }
-
-        return addConfigArgs(cmd);
-    });
-};
-
-ParamedicRunner.prototype.getCommandForBuilding = function () {
-    var browserifyArg = this.config.isBrowserify() ? ' --browserify' : '';
-    var cmd = this.config.getCli() + ' build ' + this.config.getPlatformId() + browserifyArg + util.PARAMEDIC_COMMON_CLI_ARGS;
-
-    return cmd;
-};
-
-ParamedicRunner.prototype.shouldWaitForTestResult = function () {
-    var action = this.config.getAction();
-    return (action.indexOf('run') === 0) || (action.indexOf('emulate') === 0);
-};
-
-ParamedicRunner.prototype.waitForConnection = function () {
-    var self = this;
-
-    var ERR_MSG = 'waitForConnection: Seems like device not connected to local server in ' + INITIAL_CONNECTION_TIMEOUT / 1000 + ' secs';
-
-    return Q.promise(function(resolve, reject) {
-        setTimeout(function () {
-            if (!self.server.isDeviceConnected()) {
-                reject(new Error(ERR_MSG));
-            } else {
-                resolve();
-            }
-        }, INITIAL_CONNECTION_TIMEOUT);
-    });
-};
-
-ParamedicRunner.prototype.cleanUpProject = function () {
-    this.server && this.server.cleanUp();
-    if (this.config.shouldCleanUpAfterRun()) {
-        logger.info('cordova-paramedic: Deleting the application: ' + this.tempFolder.name);
-        shell.popd();
-        shell.rm('-rf', this.tempFolder.name);
-    }
-};
-
-ParamedicRunner.prototype.checkSauceRequirements = function () {
+ParamedicSauceLabs.prototype.checkSauceRequirements = function () {
     if (this.config.shouldUseSauce()) {
         var platformId = this.config.getPlatformId();
         if (platformId !== util.ANDROID && platformId !== util.IOS && platformId !== util.BROWSER) {
@@ -495,7 +56,7 @@ ParamedicRunner.prototype.checkSauceRequirements = function () {
     }
 };
 
-ParamedicRunner.prototype.packageApp = function () {
+ParamedicSauceLabs.prototype.packageApp = function () {
     var self = this;
     switch (this.config.getPlatformId()) {
         case util.IOS: {
@@ -524,7 +85,7 @@ ParamedicRunner.prototype.packageApp = function () {
     return Q.resolve();
 };
 
-ParamedicRunner.prototype.uploadApp = function () {
+ParamedicSauceLabs.prototype.uploadApp = function () {
     logger.normal('cordova-paramedic: uploading ' + this.getAppName() + ' to Sauce Storage');
 
     var sauceUser = this.config.getSauceUser();
@@ -540,33 +101,11 @@ ParamedicRunner.prototype.uploadApp = function () {
     return execPromise(uploadCommand);
 };
 
-ParamedicRunner.prototype.getPackagedPath = function () {
+ParamedicSauceLabs.prototype.getPackagedPath = function () {
     return path.join(this.getPackageFolder(), this.getPackageName());
 };
 
-ParamedicRunner.prototype.killEmulatorProcess = function () {
-    if(this.config.shouldCleanUpAfterRun()){
-        logger.info('cordova-paramedic: Killing the emulator process.');
-        var paramedicKill = new ParamedicKill(this.config.getPlatformId());
-        paramedicKill.kill();
-    }
-};
-
-ParamedicRunner.prototype.collectDeviceLogs = function () {
-    logger.info('Collecting logs for the devices.');
-    var outputDir    = this.config.getOutputDir()? this.config.getOutputDir(): this.tempFolder.name;
-    var logMins      = this.config.getLogMins()? this.config.getLogMins(): util.DEFAULT_LOG_TIME;
-    var paramedicLogCollector = new ParamedicLogCollector(this.config.getPlatformId(), this.tempFolder.name, outputDir, this.targetObj);
-    paramedicLogCollector.collectLogs(logMins);
-};
-
-ParamedicRunner.prototype.uninstallApp = function () {
-    logger.info('Uninstalling the app.');
-    var paramedicAppUninstall = new ParamedicAppUninstall(this.tempFolder.name, this.config.getPlatformId());
-    return paramedicAppUninstall.uninstallApp(this.targetObj,util.PARAMEDIC_DEFAULT_APP_NAME);
-};
-
-ParamedicRunner.prototype.getPackageFolder = function () {
+ParamedicSauceLabs.prototype.getPackageFolder = function () {
     var packageDirs = this.getPackageFolders();
     var foundDir = null;
     packageDirs.forEach (function (dir) {
@@ -581,7 +120,7 @@ ParamedicRunner.prototype.getPackageFolder = function () {
     throw new Error ('Couldn\'t locate a built app directory. Looked here: ' + packageDirs);
 };
 
-ParamedicRunner.prototype.getPackageFolders = function () {
+ParamedicSauceLabs.prototype.getPackageFolders = function () {
     var packageFolders;
     switch (this.config.getPlatformId()) {
         case util.ANDROID:
@@ -597,7 +136,7 @@ ParamedicRunner.prototype.getPackageFolders = function () {
     return packageFolders;
 };
 
-ParamedicRunner.prototype.getPackageName = function () {
+ParamedicSauceLabs.prototype.getPackageName = function () {
     var packageName;
     switch (this.config.getPlatformId()) {
         case util.IOS:
@@ -612,7 +151,7 @@ ParamedicRunner.prototype.getPackageName = function () {
     return packageName;
 };
 
-ParamedicRunner.prototype.getBinaryName = function () {
+ParamedicSauceLabs.prototype.getBinaryName = function () {
     var binaryName;
     switch (this.config.getPlatformId()) {
         case util.ANDROID:
@@ -642,7 +181,7 @@ ParamedicRunner.prototype.getBinaryName = function () {
 };
 
 // Returns a name of the file at the SauceLabs storage
-ParamedicRunner.prototype.getAppName = function () {
+ParamedicSauceLabs.prototype.getAppName = function () {
     if (this.appName) {
         return this.appName;
     }
@@ -661,7 +200,7 @@ ParamedicRunner.prototype.getAppName = function () {
     return appName;
 };
 
-ParamedicRunner.prototype.displaySauceDetails = function (buildName) {
+ParamedicSauceLabs.prototype.displaySauceDetails = function (buildName) {
     if (!this.config.shouldUseSauce()) {
         return Q();
     }
@@ -707,7 +246,7 @@ ParamedicRunner.prototype.displaySauceDetails = function (buildName) {
     return d.promise;
 };
 
-ParamedicRunner.prototype.getSauceCaps = function () {
+ParamedicSauceLabs.prototype.getSauceCaps = function () {
     this.sauceBuildName = this.sauceBuildName || this.config.getBuildName();
     var caps = {
         name: this.sauceBuildName,
@@ -758,7 +297,7 @@ ParamedicRunner.prototype.getSauceCaps = function () {
     return caps;
 };
 
-ParamedicRunner.prototype.connectWebdriver = function () {
+ParamedicSauceLabs.prototype.connectWebdriver = function () {
     var user = this.config.getSauceUser();
     var key = this.config.getSauceKey();
     var caps = this.getSauceCaps();
@@ -787,7 +326,7 @@ ParamedicRunner.prototype.connectWebdriver = function () {
         });
 };
 
-ParamedicRunner.prototype.connectSauceConnect = function () {
+ParamedicSauceLabs.prototype.connectSauceConnect = function () {
     var self = this;
     var isBrowser = self.config.getPlatformId() === util.BROWSER;
 
@@ -821,7 +360,7 @@ ParamedicRunner.prototype.connectSauceConnect = function () {
     });
 };
 
-ParamedicRunner.prototype.runSauceTests = function () {
+ParamedicSauceLabs.prototype.runSauceTests = function () {
     var self = this;
     var isTestPassed = false;
     var pollForResults;
@@ -979,16 +518,4 @@ ParamedicRunner.prototype.runSauceTests = function () {
     .then(function () {
         return isTestPassed;
     });
-};
-
-var storedCWD = null;
-
-exports.run = function(paramedicConfig) {
-
-    storedCWD = storedCWD || process.cwd();
-
-    var runner = new ParamedicRunner(paramedicConfig, null);
-    runner.storedCWD = storedCWD;
-
-    return runner.run();
-};
+};
\ No newline at end of file
diff --git a/lib/paramedic.js b/lib/paramedic.js
index 9f01fcb..d9f369b 100644
--- a/lib/paramedic.js
+++ b/lib/paramedic.js
@@ -25,17 +25,12 @@ var Server          = require('./LocalServer');
 var path            = require('path');
 var Q               = require('q');
 var fs              = require('fs');
-var wd              = require('wd');
-var SauceLabs       = require('saucelabs');
-var randomstring    = require('randomstring');
-var sauceConnectLauncher    = require('sauce-connect-launcher');
 
 var logger          = require('./utils').logger;
 var util            = require('./utils').utilities;
 var Reporters       = require('./Reporters');
 var ParamedicKill   = require('./ParamedicKill');
 var AppiumRunner    = require('./appium/AppiumRunner');
-var appPatcher      = require('./appium/helpers/appPatcher');
 var ParamedicLogCollector   = require('./ParamedicLogCollector');
 var ParamediciOSPermissions = require('./ParamediciOSPermissions');
 var ParamedicTargetChooser  = require('./ParamedicTargetChooser');
@@ -49,6 +44,7 @@ require('./appium/helpers/wdHelper');
 // If device has not connected within this interval the tests are stopped.
 var INITIAL_CONNECTION_TIMEOUT = 540000; // 9mins
 
+Q.longStackSupport = true;
 
 function ParamedicRunner(config, _callback) {
     this.tempFolder = null;
@@ -57,6 +53,8 @@ function ParamedicRunner(config, _callback) {
     this.targetObj = undefined;
 
     exec.setVerboseLevel(config.isVerbose());
+
+    this.paramedicSauceLabs = null;
 }
 
 ParamedicRunner.prototype.run = function () {
@@ -96,6 +94,11 @@ ParamedicRunner.prototype.run = function () {
         return self.runTests();
     })
     .timeout(self.config.getTimeout(), 'Timed out after waiting for ' + self.config.getTimeout() + ' ms.')
+    .catch(function (error) {
+        logger.error(error);
+        console.log(error.stack);
+        throw new Error(error);
+    })
     .fin(function (result) {
         isTestPassed = result;
         logger.normal('Completed tests at ' + (new Date()).toLocaleTimeString());
@@ -120,7 +123,10 @@ ParamedicRunner.prototype.run = function () {
 };
 
 ParamedicRunner.prototype.checkConfig = function () {
-    this.checkSauceRequirements();
+    if (this.config.shouldUseSauce()) {
+        this.paramedicSauceLabs = new ParamedicSauceLabs();
+        this.paramedicSauceLabs.checkSauceRequirements();
+    }
     if (!this.config.runMainTests() && !this.config.runAppiumTests()) {
         throw new Error('No tests to run: both --skipAppiumTests and --skipMainTests are used');
     }
@@ -476,74 +482,6 @@ ParamedicRunner.prototype.cleanUpProject = function () {
     }
 };
 
-ParamedicRunner.prototype.checkSauceRequirements = function () {
-    if (this.config.shouldUseSauce()) {
-        var platformId = this.config.getPlatformId();
-        if (platformId !== util.ANDROID && platformId !== util.IOS && platformId !== util.BROWSER) {
-            logger.warn('Saucelabs only supports Android and iOS (and browser), falling back to testing locally.');
-            this.config.setShouldUseSauce(false);
-        } else if (!this.config.getSauceKey()) {
-            throw new Error('Saucelabs key not set. Please set it via environmental variable ' +
-                util.SAUCE_KEY_ENV_VAR + ' or pass it with the --sauceKey parameter.');
-        } else if (!this.config.getSauceUser()) {
-            throw new Error('Saucelabs user not set. Please set it via environmental variable ' +
-                util.SAUCE_USER_ENV_VAR + ' or pass it with the --sauceUser parameter.');
-        } else if (!this.shouldWaitForTestResult()) {
-            // don't throw, just silently disable Sauce
-            this.config.setShouldUseSauce(false);
-        }
-    }
-};
-
-ParamedicRunner.prototype.packageApp = function () {
-    var self = this;
-    switch (this.config.getPlatformId()) {
-        case util.IOS: {
-            return Q.Promise(function (resolve, reject) {
-                var zipCommand = 'zip -r ' + self.getPackageName() + ' ' + self.getBinaryName();
-                shell.pushd(self.getPackageFolder());
-                shell.rm('-rf', self.getPackageName());
-                console.log('Running command: ' + zipCommand + ' in dir: ' + shell.pwd());
-                exec(zipCommand, { silent: !self.config.isVerbose() }, function (code, stdout, stderr) {
-                    shell.popd();
-                    if (code) {
-                        reject('zip command returned with error code ' + code);
-                    } else {
-                        resolve();
-                    }
-                });
-            });
-        }
-        case util.ANDROID:
-            break; // don't need to zip the app for Android
-        case util.BROWSER:
-            break; // don't need to bundle the app on Browser platform at all
-        default:
-            throw new Error('Don\'t know how to package the app for platform: ' + this.config.getPlatformId());
-    }
-    return Q.resolve();
-};
-
-ParamedicRunner.prototype.uploadApp = function () {
-    logger.normal('cordova-paramedic: uploading ' + this.getAppName() + ' to Sauce Storage');
-
-    var sauceUser = this.config.getSauceUser();
-    var key       = this.config.getSauceKey();
-
-    var uploadURI     = encodeURI('https://saucelabs.com/rest/v1/storage/' + sauceUser + '/' + this.getAppName() + '?overwrite=true');
-    var filePath      = this.getPackagedPath();
-    var uploadCommand =
-        'curl -u ' + sauceUser + ':' + key +
-        ' -X POST -H "Content-Type: application/octet-stream" ' +
-        uploadURI + ' --data-binary "@' + filePath + '"';
-
-    return execPromise(uploadCommand);
-};
-
-ParamedicRunner.prototype.getPackagedPath = function () {
-    return path.join(this.getPackageFolder(), this.getPackageName());
-};
-
 ParamedicRunner.prototype.killEmulatorProcess = function () {
     if(this.config.shouldCleanUpAfterRun()){
         logger.info('cordova-paramedic: Killing the emulator process.');
@@ -566,421 +504,6 @@ ParamedicRunner.prototype.uninstallApp = function () {
     return paramedicAppUninstall.uninstallApp(this.targetObj,util.PARAMEDIC_DEFAULT_APP_NAME);
 };
 
-ParamedicRunner.prototype.getPackageFolder = function () {
-    var packageDirs = this.getPackageFolders();
-    var foundDir = null;
-    packageDirs.forEach (function (dir) {
-        if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
-            foundDir = dir;
-            return;
-        }
-    });
-    if (foundDir != null) {
-        return foundDir;
-    }
-    throw new Error ('Couldn\'t locate a built app directory. Looked here: ' + packageDirs);
-};
-
-ParamedicRunner.prototype.getPackageFolders = function () {
-    var packageFolders;
-    switch (this.config.getPlatformId()) {
-        case util.ANDROID:
-            packageFolders =  [ path.join(this.tempFolder.name, 'platforms/android/app/build/outputs/apk/debug'),
-                                path.join(this.tempFolder.name, 'platforms/android/build/outputs/apk') ];
-            break;
-        case util.IOS:
-            packageFolders = [ path.join(this.tempFolder.name, 'platforms/ios/build/emulator') ];
-            break;
-        default:
-            throw new Error('Don\t know where the package foler is for platform: ' + this.config.getPlatformId());
-    }
-    return packageFolders;
-};
-
-ParamedicRunner.prototype.getPackageName = function () {
-    var packageName;
-    switch (this.config.getPlatformId()) {
-        case util.IOS:
-            packageName = 'HelloCordova.zip';
-            break;
-        case util.ANDROID:
-            packageName = this.getBinaryName();
-            break;
-        default:
-            throw new Error('Don\'t know what the package name is for platform: ' + this.config.getPlatformId());
-    }
-    return packageName;
-};
-
-ParamedicRunner.prototype.getBinaryName = function () {
-    var binaryName;
-    switch (this.config.getPlatformId()) {
-        case util.ANDROID:
-            shell.pushd(this.getPackageFolder());
-            var apks = shell.ls('*debug.apk');
-            if (apks.length > 0) {
-                binaryName = apks.reduce(function (previous, current) {
-                    // if there is any apk for x86, take it
-                    if (current.indexOf('x86') >= 0) {
-                        return current;
-                    }
-                    // if not, just take the first one
-                    return previous;
-                });
-            } else {
-                throw new Error('Couldn\'t locate built apk');
-            }
-            shell.popd();
-            break;
-        case util.IOS:
-            binaryName = 'HelloCordova.app';
-            break;
-        default:
-            throw new Error('Don\'t know the binary name for platform: ' + this.config.getPlatformId());
-    }
-    return binaryName;
-};
-
-// Returns a name of the file at the SauceLabs storage
-ParamedicRunner.prototype.getAppName = function () {
-    if (this.appName) {
-        return this.appName;
-    }
-    var appName = randomstring.generate();
-    switch (this.config.getPlatformId()) {
-        case util.ANDROID:
-            appName += '.apk';
-            break;
-        case util.IOS:
-            appName += '.zip';
-            break;
-        default:
-            throw new Error('Don\'t know the app name for platform: ' + this.config.getPlatformId());
-    }
-    this.appName = appName;
-    return appName;
-};
-
-ParamedicRunner.prototype.displaySauceDetails = function (buildName) {
-    if (!this.config.shouldUseSauce()) {
-        return Q();
-    }
-    if (!buildName) {
-        buildName = this.config.getBuildName();
-    }
-
-    var self = this;
-    var d = Q.defer();
-
-    logger.normal('Getting saucelabs jobs details...\n');
-
-    var sauce = new SauceLabs({
-        username: self.config.getSauceUser(),
-        password: self.config.getSauceKey()
-    });
-
-    sauce.getJobs(function (err, jobs) {
-        var found = false;
-        for (var job in jobs) {
-            if (jobs.hasOwnProperty(job) && jobs[job].name && jobs[job].name.indexOf(buildName) === 0) {
-                var jobUrl = 'https://saucelabs.com/beta/tests/' + jobs[job].id;
-                logger.normal('============================================================================================');
-                logger.normal('Job name: ' + jobs[job].name);
-                logger.normal('Job ID: ' + jobs[job].id);
-                logger.normal('Job URL: ' + jobUrl);
-                logger.normal('Video: ' + jobs[job].video_url);
-                logger.normal('Appium logs: ' + jobs[job].log_url);
-                if (self.config.getPlatformId() === util.ANDROID) {
-                    logger.normal('Logcat logs: ' + 'https://saucelabs.com/jobs/' + jobs[job].id + '/logcat.log');
-                }
-                logger.normal('============================================================================================');
-                logger.normal('');
-                found = true;
-            }
-        }
-
-        if (!found) {
-            logger.warn('Can not find saucelabs job. Logs and video will be unavailable.');
-        }
-        d.resolve();
-    });
-    return d.promise;
-};
-
-ParamedicRunner.prototype.getSauceCaps = function () {
-    this.sauceBuildName = this.sauceBuildName || this.config.getBuildName();
-    var caps = {
-        name: this.sauceBuildName,
-        idleTimeout: '100', // in seconds
-        maxDuration: util.SAUCE_MAX_DURATION,
-        tunnelIdentifier: this.config.getSauceTunnelId(),
-    };
-
-    switch(this.config.getPlatformId()) {
-        case util.ANDROID:
-            caps.platformName = 'Android';
-            caps.appPackage = 'io.cordova.hellocordova';
-            caps.appActivity = 'io.cordova.hellocordova.MainActivity';
-            caps.app = 'sauce-storage:' + this.getAppName();
-            caps.deviceType = 'phone';
-            caps.deviceOrientation = 'portrait';
-            caps.appiumVersion = this.config.getSauceAppiumVersion();
-            caps.deviceName = this.config.getSauceDeviceName();
-            caps.platformVersion = this.config.getSaucePlatformVersion();
-            break;
-        case util.IOS:
-            caps.platformName = 'iOS';
-            caps.autoAcceptAlerts = true;
-            caps.waitForAppScript = 'true;';
-            caps.app = 'sauce-storage:' + this.getAppName();
-            caps.deviceType = 'phone';
-            caps.deviceOrientation = 'portrait';
-            caps.appiumVersion = this.config.getSauceAppiumVersion();
-            caps.deviceName = this.config.getSauceDeviceName();
-            caps.platformVersion = this.config.getSaucePlatformVersion();
-            break;
-        case util.BROWSER:
-            caps.browserName = this.config.getSauceDeviceName() || 'chrome';
-            caps.version = this.config.getSaucePlatformVersion() || '45.0';
-            caps.platform = caps.browserName.indexOf('Edge') > 0 ? 'Windows 10' : 'macOS 10.13';
-            // setting from env.var here and not in the config
-            // because for any other platform we don't need to put the sauce connect up 
-            // unless the tunnel id is explicitly passed (means that user wants it anyway)
-            if (!caps.tunnelIdentifier && process.env[util.SAUCE_TUNNEL_ID_ENV_VAR]) {
-                caps.tunnelIdentifier = process.env[util.SAUCE_TUNNEL_ID_ENV_VAR];
-            } else if (!caps.tunnelIdentifier) {
-                throw new Error('Testing browser platform on Sauce Labs requires Sauce Connect tunnel. Please specify tunnel identifier via --sauceTunnelId');
-            }
-            break;
-        default:
-            throw new Error('Don\'t know the Sauce caps for platform: ' + this.config.getPlatformId());
-    }
-    return caps;
-};
-
-ParamedicRunner.prototype.connectWebdriver = function () {
-    var user = this.config.getSauceUser();
-    var key = this.config.getSauceKey();
-    var caps = this.getSauceCaps();
-
-    logger.normal('cordova-paramedic: connecting webdriver');
-    var spamDots = setInterval(function () {
-        process.stdout.write('.');
-    }, 1000);
-
-    wd.configureHttp({
-        timeout: util.WD_TIMEOUT,
-        retryDelay: util.WD_RETRY_DELAY,
-        retries: util.WD_RETRIES
-    });
-
-    var driver = wd.promiseChainRemote(util.SAUCE_HOST, util.SAUCE_PORT, user, key);
-    return driver
-        .init(caps)
-        .then(function () {
-            clearInterval(spamDots);
-            process.stdout.write('\n');
-        }, function (error) {
-            clearInterval(spamDots);
-            process.stdout.write('\n');
-            throw(error);
-        });
-};
-
-ParamedicRunner.prototype.connectSauceConnect = function () {
-    var self = this;
-    var isBrowser = self.config.getPlatformId() === util.BROWSER;
-
-    // on platforms other than browser, only run sauce connect if user explicitly asks for it
-    if (!isBrowser && !self.config.getSauceTunnelId()) {
-        return Q();
-    }
-    // on browser, run sauce connect in any case
-    if (isBrowser && !self.config.getSauceTunnelId()) {
-        self.config.setSauceTunnelId(process.env[util.SAUCE_TUNNEL_ID_ENV_VAR] || self.config.getBuildName());
-    }
-
-    return Q.Promise(function (resolve, reject) {
-        logger.info('cordova-paramedic: Starting Sauce Connect...');
-        sauceConnectLauncher({
-            username: self.config.getSauceUser(),
-            accessKey: self.config.getSauceKey(),
-            tunnelIdentifier: self.config.getSauceTunnelId(),
-            connectRetries: util.SAUCE_CONNECT_CONNECTION_RETRIES,
-            connectRetryTimeout: util.SAUCE_CONNECT_CONNECTION_TIMEOUT,
-            downloadRetries: util.SAUCE_CONNECT_DOWNLOAD_RETRIES,
-            downloadRetryTimeout: util.SAUCE_CONNECT_DOWNLOAD_TIMEOUT,
-        }, function (err, sauceConnectProcess) {
-            if (err) {
-                reject(err);
-            }
-            self.sauceConnectProcess = sauceConnectProcess;
-            logger.info('cordova-paramedic: Sauce Connect ready');
-            resolve();
-        });
-    });
-};
-
-ParamedicRunner.prototype.runSauceTests = function () {
-    var self = this;
-    var isTestPassed = false;
-    var pollForResults;
-    var driver;
-    var runProcess = null;
-
-    if (!self.config.runMainTests()) {
-        logger.normal('Skipping main tests...');
-        return Q(util.TEST_PASSED);
-    }
-
-    logger.info('cordova-paramedic: running tests with sauce');
-
-    return Q().then(function () {
-        // Build + "Upload" app
-        if (self.config.getPlatformId() === util.BROWSER) {
-            // for browser, we need to serve the app for Sauce Connect
-            // we do it by just running "cordova run" and ignoring the chrome instance that pops up
-            return Q()
-                .then(function() {
-                    appPatcher.addCspSource(self.tempFolder.name, 'connect-src', 'http://*');
-                    appPatcher.permitAccess(self.tempFolder.name, '*');
-                    return self.getCommandForStartingTests();
-                })
-                .then(function (command) {
-                    console.log('$ ' + command);
-                    runProcess = cp.exec(command, function onExit() {
-                        // a precaution not to try to kill some other process
-                        runProcess = null;
-                    });
-                });
-        } else {
-            return self.buildApp()
-                .then(self.packageApp.bind(self))
-                .then(self.uploadApp.bind(self));
-        }
-    })
-    .then(function () {
-        return self.connectSauceConnect();
-    })
-    .then(function () {
-        driver = self.connectWebdriver();
-        if (self.config.getPlatformId() === util.BROWSER) {
-            return driver.get('http://localhost:8000/cdvtests/index.html');
-        }
-        return driver;
-    })
-    .then(function () {
-        if (self.config.getUseTunnel() || self.config.getPlatformId() === util.BROWSER) {
-            return driver;
-        }
-        return driver
-        .getWebviewContext()
-        .then(function (webview) {
-            return driver.context(webview);
-        });
-    })
-    .then(function () {
-        var isWkWebview = false;
-        var plugins = self.config.getPlugins();
-        for (var plugin in plugins) {
-            if (plugins[plugin].indexOf('wkwebview') >= 0) {
-                isWkWebview = true;
-            }
-        }
-        if (isWkWebview) {
-            logger.normal('cordova-paramedic: navigating to a test page');
-            return driver
-                .sleep(1000)
-                .elementByXPath('//*[text() = "Auto Tests"]')
-                .click();
-        }
-        return driver;
-    })
-    .then(function () {
-        logger.normal('cordova-paramedic: connecting to app');
-
-        var platform = self.config.getPlatformId();
-        var plugins = self.config.getPlugins();
-
-        var skipBuster = false;
-        // skip permission buster for splashscreen and inappbrowser plugins
-        // it hangs the test run on Android 7 for some reason
-        for (var i = 0; i < plugins.length; i++) {
-            if (plugins[i].indexOf('cordova-plugin-splashscreen') >= 0 || plugins[i].indexOf('cordova-plugin-inappbrowser') >= 0) {
-                skipBuster = true;
-            }
-        }
-        // always skip buster for browser platform
-        if (platform === util.BROWSER) {
-            skipBuster = true;
-        }
-
-        if (!self.config.getUseTunnel()) {
-            var polling = false;
-            pollForResults = setInterval(function () {
-                if (!polling) {
-                    polling = true;
-                    driver.pollForEvents(platform, skipBuster)
-                    .then(function (events) {
-                        for (var i = 0; i < events.length; i++) {
-                            self.server.emit(events[i].eventName, events[i].eventObject);
-                        }
-                        polling = false;
-                    })
-                    .fail(function (error) {
-                        logger.warn('appium: ' + error);
-                        polling = false;
-                    });
-                }
-            }, 2500);
-        }
-
-        return self.waitForTests();
-    })
-    .then(function (result) {
-        logger.normal('cordova-paramedic: Tests finished');
-        isTestPassed = result;
-    }, function (error) {
-        logger.normal('cordova-paramedic: Tests failed to complete; ending appium session. The error is:\n' + error.stack);
-    })
-    .fin(function () {
-        if (pollForResults) {
-            clearInterval(pollForResults);
-        }
-        if (driver && typeof driver.quit === 'function') {
-            return driver.quit();
-        }
-    })
-    .fin(function () {
-        if (self.config.getPlatformId() === util.BROWSER && !self.browserPatched) {
-            // we need to kill chrome
-            self.killEmulatorProcess();
-        }
-        if (runProcess) {
-            // as well as we need to kill the spawned node process serving our app
-            return Q.Promise(function (resolve, reject) {
-                util.killProcess(runProcess.pid, function () {
-                    resolve();
-                });
-            });
-        }
-    })
-    .fin(function () {
-        if (self.sauceConnectProcess) {
-            logger.info('cordova-paramedic: Closing Sauce Connect process...');
-            return Q.Promise(function (resolve, reject) {
-                self.sauceConnectProcess.close(function () {
-                    logger.info('cordova-paramedic: Successfully closed Sauce Connect process');
-                    resolve();
-                });
-            });
-        }
-    })
-    .then(function () {
-        return isTestPassed;
-    });
-};
-
 var storedCWD = null;
 
 exports.run = function(paramedicConfig) {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org