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/05 13:38:27 UTC
[cordova-paramedic] 06/08: 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_paramedicjs
in repository https://gitbox.apache.org/repos/asf/cordova-paramedic.git
commit 0902afabca4ba7e4cbc927bef14ac87bd1674de9
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} | 521 ++--------------------------
lib/paramedic.js | 501 +-------------------------
2 files changed, 36 insertions(+), 986 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 a2a6f79..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: {
@@ -504,7 +65,7 @@ ParamedicRunner.prototype.packageApp = function () {
shell.pushd(self.getPackageFolder());
shell.rm('-rf', self.getPackageName());
console.log('Running command: ' + zipCommand + ' in dir: ' + shell.pwd());
- exec(zipCommand, function (code, stdout, stderr) {
+ exec(zipCommand, { silent: !self.config.isVerbose() }, function (code, stdout, stderr) {
shell.popd();
if (code) {
reject('zip command returned with error code ' + code);
@@ -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 a2a6f79..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, 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