You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by sg...@apache.org on 2016/03/25 16:39:30 UTC
[3/3] cordova-paramedic git commit: Adding real time logging and
other improvements
Adding real time logging and other improvements
This closes #1
Project: http://git-wip-us.apache.org/repos/asf/cordova-paramedic/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-paramedic/commit/b1aa699d
Tree: http://git-wip-us.apache.org/repos/asf/cordova-paramedic/tree/b1aa699d
Diff: http://git-wip-us.apache.org/repos/asf/cordova-paramedic/diff/b1aa699d
Branch: refs/heads/master
Commit: b1aa699dfa56d442e75294448e3253ac6c7f0995
Parents: 8615f31
Author: Vladimir Kotikov <ko...@gmail.com>
Authored: Fri Mar 25 13:54:21 2016 +0300
Committer: sgrebnov <se...@gmail.com>
Committed: Fri Mar 25 17:04:26 2016 +0300
----------------------------------------------------------------------
.jshintrc | 14 +
.travis.yml | 1 +
README.md | 164 +++++++++-
lib/LocalServer.js | 99 ++++++
lib/ParamedicConfig.js | 83 +++++
lib/PluginsManager.js | 51 ++++
lib/Target.js | 9 +
lib/TestsRunner.js | 121 ++++++++
lib/Tunnel.js | 19 ++
lib/logger.js | 150 +++++++++
lib/paramedic.js | 156 ++++++++++
lib/portScanner.js | 54 ++++
lib/reporters/ConsoleReporter.js | 153 ++++++++++
lib/reporters/ParamedicReporter.js | 43 +++
lib/specReporters.js | 68 +++++
lib/utils.js | 22 ++
main.js | 69 +++--
package.json | 24 +-
paramedic-plugin/JasmineParamedicProxy.js | 56 ++++
paramedic-plugin/paramedic.js | 74 +++++
paramedic-plugin/plugin.xml | 37 +++
paramedic-plugin/socket.io.js | 4 +
paramedic.js | 305 -------------------
sample-config/.paramedic-core-plugins.config.js | 51 ++++
sample-config/.paramedic.config.js | 33 ++
25 files changed, 1502 insertions(+), 358 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/.jshintrc
----------------------------------------------------------------------
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..e11705f
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,14 @@
+{
+ "node" : true
+ , "devel": true
+ , "bitwise": true
+ , "undef": true
+ , "trailing": true
+ , "quotmark": false
+ , "indent": 4
+ , "unused": "vars"
+ , "expr": true
+ , "latedef": "nofunc"
+ , "globals": {
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index 3c02467..aa2e054 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,7 @@ install:
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- npm install cordova
- npm install ios-sim
+ - npm install ios-deploy
- npm install
- npm link
script:
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 305feeb..4dcc055 100644
--- a/README.md
+++ b/README.md
@@ -7,31 +7,167 @@ Runs cordova medic/buildbot tests locally.
... provides advanced levels of care at the point of illness or injury, including out of hospital treatment, and diagnostic services
-To install :
+# To install :
``` $npm install cordova-paramedic ```
-Usage :
+## Supported Cordova Platforms
+
+- Android
+- iOS
+- Windows Phone 8
+- Windows (Windows 8.1, Windows Phone 8.1, Windows 10 Tablet/PC)
+- Browser
+
+# Usage
+
+Paramedic parameters could be passed via command line arguments or via separate configuration file:
+
+```
+cordova-paramedic --platform PLATFORM --plugin PATH <other parameters>
+cordova-paramedic --config ./sample-config/.paramedic.config.js
+```
+
+## Command Line Interface
+
+####`--platform` (required)
+
+Specifies target cordova platform (could refer to local directory, npm or git)
+
+```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser
+cordova-paramedic --platform ios@4.0 --plugin cordova-plugin-inappbrowser
+cordova-paramedic --platform ios@../cordova-ios --plugin cordova-plugin-inappbrowser
+cordova-paramedic --platform ios@https://github.com/apache/cordova-ios.git#4.1.0 --plugin cordova-plugin-inappbrowser
+```
+
+####`--plugin` (required)
+
+Specifies test plugin, you may specify multiple --plugin flags and they will all be installed and tested together. Similat to `platform` parameter you can refer to local (or absolute) path, npm registry or git repo.
```
-cordova-paramedic --platform PLATFORM --plugin PATH [--justbuild --timeout MSECS --port PORTNUM --browserify --verbose ]`PLATFORM` : the platform id, currently only supports 'ios'
-`PATH` : the relative or absolute path to a plugin folder
- expected to have a 'tests' folder.
- You may specify multiple --plugin flags and they will all
- be installed and tested together.
-`MSECS` : (optional) time in millisecs to wait for tests to pass|fail
- (defaults to 10 minutes)
-`PORTNUM` : (optional) port to use for posting results from emulator back to paramedic server
---justbuild : (optional) just builds the project, without running the tests
---browserify : (optional) plugins are browserified into cordova.js
---verbose : (optional) verbose mode. Display more information output
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser
+cordova-paramedic --platform ios --plugin ../cordova-plugin-inappbrowser
+cordova-paramedic --platform ios --plugin https://github.com/apache/cordova-plugin-inappbrowser
+// several plugins
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --plugin cordova-plugin-contacts
+```
+####--justbuild (optional)
+
+Just builds the project, without running the tests.
+
+```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --justbuild
+```
+
+####--device (optional)
+
+Tests must be run on connected device instead of emulator.
+
+```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --device
+```
+
+####--externalServerUrl (optional)
+
+Useful when testing on real device (`--device` parameter) so that tests results from device could be posted back to paramedic server.
+
+```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --externalServerUrl http://10.0.8.254
+```
+
+####--useTunnel (optional)
+
+Use [tunneling](https://www.npmjs.com/package/localtunnel) instead of local address (default is false).
+Useful when testing on real devices and don't want to specify external ip address (see `--externalServerUrl` above) of paramedic server.
```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --useTunnel
+```
+
+####--browserify (optional)
+
+Plugins are browserified into cordova.js.
+
+```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --browserify
+```
+
+####--port (optional)
+
+Port to use for posting results from emulator back to paramedic server (default is from `8008`). You can also specify a range using `--startport` and `endport` and paramedic will select the first available.
+
+```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --port 8010
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --startport 8000 endport 8020
+```
+
+####--verbose (optional)
+
+Verbose mode. Display more information output
+
+```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --verbose
+```
+
+####--timeout (optional)
+
+Time in millisecs to wait for tests to pass|fail (defaults to 10 minutes).
+
+```
+cordova-paramedic --platform ios --plugin cordova-plugin-inappbrowser --timeout 30000
+```
+
+## Paramedic configuration file
+
+Configuration file is used when no parameters are passed to `cordova-paramedic` call or explicitly specified via `--config` parameter:
+```
+cordova-paramedic <- paramedic will attempt to find .paramedic.config.js in working directory
+cordova-paramedic --config ./sample-config/.paramedic.config.js
+```
+Example configuration file is showed below. It supports similar arguments and has the following advantages over `Command Line Approach`:
+
+- Supports extra arguments which could be passed to cordova so that you have full control over build and run target.
+- Supports several test platforms (targets) to be executed as single paramedic run (results will be aggregated) so you don't need to re-install test plugins, create local server and do other steps several times.
+
+```
+module.exports = {
+ // "externalServerUrl": "http://10.0.8.254",
+ "useTunnel": true,
+ "plugins": [
+ "https://github.com/apache/cordova-plugin-inappbrowser"
+ ],
+ "targets": [
+ {
+ "platform": "ios@https://github.com/apache/cordova-ios.git",
+ "action": "run",
+ "args": "--device"
+ },
+ {
+ "platform": "android@https://github.com/apache/cordova-android.git",
+ "action": "run",
+ "args": "--device"
+ },
+ { // Windows 8.1 Desktop(anycpu)
+ "platform": "windows@https://github.com/apache/cordova-windows.git",
+ "action": "run"
+ },
+ { // Windows 10 Desktop(x64)
+ "platform": "windows@https://github.com/apache/cordova-windows.git",
+ "action": "run",
+ "args": "--archs=x64 -- --appx=uap"
+ }
+ ]
+}
+```
+More configuration file examples could be found in `sample-config` folder.
+
+## API Interface
You can also use cordova-paramedic as a module directly :
```
var paramedic = require('cordova-paramedic');
- paramedic.run('ios', '../cordova-plugin-device', onCompleteCallback,justBuild,portNum,msTimeout, useBrowserify, beSilent, beVerbose);
+ paramedic.run(config);
```
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/LocalServer.js
----------------------------------------------------------------------
diff --git a/lib/LocalServer.js b/lib/LocalServer.js
new file mode 100644
index 0000000..a7bf42c
--- /dev/null
+++ b/lib/LocalServer.js
@@ -0,0 +1,99 @@
+var Q = require('q');
+var io = require('socket.io');
+
+var logger = require('./logger').get();
+
+var specReporters = require('./specReporters');
+
+
+function LocalServer(port, externalServerUrl, tunneledUrl) {
+ this.port = port;
+ this.tunneledUrl = tunneledUrl;
+ this.externalServerUrl = externalServerUrl;
+ this.onTestsResults = null;
+}
+
+LocalServer.startServer = function(port, externalServerUrl, tunneledUrl) {
+ var localServer = new LocalServer(port, externalServerUrl, tunneledUrl);
+ localServer.createSocketListener();
+ return Q.resolve(localServer);
+};
+
+LocalServer.prototype.createSocketListener = function() {
+ var listener = io.listen(this.port, {
+ pingTimeout: 60000, // how many ms without a pong packet to consider the connection closed
+ pingInterval: 25000 // how many ms before sending a new ping packet
+ });
+
+ var me = this;
+
+ var routes = {
+ 'log': me.onDeviceLog.bind(me),
+ 'disconnect': me.onTestsCompletedOrDisconnected.bind(me),
+ 'deviceInfo': me.onDeviceInfo.bind(me),
+ 'jasmineStarted': specReporters.jasmineStarted.bind(specReporters),
+ 'specStarted': specReporters.specStarted.bind(specReporters),
+ 'specDone': specReporters.specDone.bind(specReporters),
+ 'suiteStarted': specReporters.suiteStarted.bind(specReporters),
+ 'suiteDone': specReporters.suiteDone.bind(specReporters),
+ 'jasmineDone': me.onJasmineDone.bind(me)
+ };
+
+ listener.on('connection', function(socket) {
+ logger.info('local-server: new socket connection');
+ me.connection = socket;
+
+ for (var routeType in routes) {
+ socket.on(routeType, routes[routeType]);
+ }
+ });
+};
+
+LocalServer.prototype.haveConnectionUrl = function() {
+ return !!(this.tunneledUrl || this.externalServerUrl);
+};
+
+LocalServer.prototype.getConnectionUrl = function() {
+ return this.tunneledUrl || this.externalServerUrl + ":" + this.port;
+};
+
+LocalServer.prototype.reset = function() {
+ this.onTestsResults = null;
+ if (this.connection) {
+ this.connection.disconnect();
+ this.connection = null;
+ }
+
+ specReporters.reset();
+};
+
+LocalServer.prototype.onDeviceLog = function(data) {
+ logger.verbose('device|console.'+data.type + ': ' + data.msg[0]);
+};
+
+LocalServer.prototype.onDeviceInfo = function(data) {
+ logger.info('cordova-paramedic: Device info: ' + JSON.stringify(data));
+};
+
+LocalServer.prototype.onTestsCompleted = function(msg) {
+ logger.normal('local-server: tests completed');
+ this.lastMobileSpecResults = specReporters.getResults();
+};
+
+LocalServer.prototype.onJasmineDone = function(data) {
+ specReporters.jasmineDone(data);
+ // save results to report them later
+ this.onTestsCompleted();
+ // disconnect because all tests have been completed
+ this.connection.disconnect();
+};
+
+LocalServer.prototype.onTestsCompletedOrDisconnected = function() {
+ logger.info('local-server: tests have been completed or test device has disconnected');
+ if (this.onTestsResults) {
+ this.onTestsResults(this.lastMobileSpecResults);
+ }
+ this.lastMobileSpecResults = null;
+};
+
+module.exports = LocalServer;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/ParamedicConfig.js
----------------------------------------------------------------------
diff --git a/lib/ParamedicConfig.js b/lib/ParamedicConfig.js
new file mode 100644
index 0000000..d9acdec
--- /dev/null
+++ b/lib/ParamedicConfig.js
@@ -0,0 +1,83 @@
+var Target = require('./Target');
+
+var DEFAULT_START_PORT = 8008;
+var DEFAULT_END_PORT = 8018;
+var DEFAULT_TIMEOUT = 10 * 60 * 1000; // 10 minutes in msec - this will become a param
+
+function ParamedicConfig(json) {
+ this._config = json;
+}
+
+ParamedicConfig.parseFromArguments = function (argv) {
+ return new ParamedicConfig({
+ "targets": [{
+ platform: argv.platform,
+ action: !!argv.justbuild ? 'build' : 'run',
+ args: (!!argv.browserify ? '--browserify ' : '') + (!!argv.device ? '--device' : '')
+ }],
+ "plugins": Array.isArray(argv.plugin) ? argv.plugin : [argv.plugin],
+ "useTunnel": !!argv.useTunnel,
+ "verbose": !!argv.verbose,
+ "startPort": argv.startport || argv.port,
+ "endPort": argv.endport || argv.port,
+ "externalServerUrl": argv.externalServerUrl,
+ "reportSavePath": !!argv.reportSavePath? argv.reportSavePath: undefined,
+ "cleanUpAfterRun": !!argv.cleanUpAfterRun? true: false
+ });
+};
+
+ParamedicConfig.parseFromFile = function (paramedicConfigPath) {
+ return new ParamedicConfig(require(paramedicConfigPath));
+};
+
+ParamedicConfig.prototype.useTunnel = function () {
+ return this._config.useTunnel;
+
+};
+
+ParamedicConfig.prototype.getReportSavePath = function () {
+ return this._config.reportSavePath;
+};
+
+ParamedicConfig.prototype.shouldCleanUpAfterRun = function () {
+ return this._config.cleanUpAfterRun;
+};
+
+ParamedicConfig.prototype.getTargets = function () {
+ return this._config.targets.map(function(target) {
+ return new Target(target);
+ });
+};
+
+ParamedicConfig.prototype.getPlugins = function () {
+ return this._config.plugins;
+};
+
+ParamedicConfig.prototype.getExternalServerUrl= function () {
+ return this._config.externalServerUrl;
+};
+
+ParamedicConfig.prototype.isVerbose = function() {
+ return this._config.verbose;
+};
+
+ParamedicConfig.prototype.isJustBuild = function() {
+ return this._config.justbuild;
+};
+
+ParamedicConfig.prototype.isBrowserify = function() {
+ return this._config.browserify;
+};
+
+ParamedicConfig.prototype.getPorts = function() {
+ return {
+ start: this._config.startPort || DEFAULT_START_PORT,
+ end: this._config.endPort || DEFAULT_END_PORT
+ };
+};
+
+ParamedicConfig.prototype.getTimeout = function() {
+ return DEFAULT_TIMEOUT;
+};
+
+module.exports = ParamedicConfig;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/PluginsManager.js
----------------------------------------------------------------------
diff --git a/lib/PluginsManager.js b/lib/PluginsManager.js
new file mode 100644
index 0000000..3d4c441
--- /dev/null
+++ b/lib/PluginsManager.js
@@ -0,0 +1,51 @@
+var path = require('path');
+var fs = require('fs');
+var logger = require('./logger').get();
+var exec = require('./utils').exec;
+var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
+
+function PluginsManager(appRoot, storedCWD) {
+ this.appRoot = appRoot;
+ this.storedCWD = storedCWD;
+}
+
+PluginsManager.prototype.installPlugins = function(plugins) {
+ for(var n = 0; n < plugins.length; n++) {
+ var plugin = plugins[n];
+ this.installSinglePlugin(plugin);
+ }
+};
+
+PluginsManager.prototype.installTestsForExistingPlugins = function() {
+ var installedPlugins = new PluginInfoProvider().getAllWithinSearchPath(path.join(this.appRoot, 'plugins'));
+ var me = this;
+ installedPlugins.forEach(function(plugin) {
+ // there is test plugin available
+ if (fs.existsSync(path.join(plugin.dir, 'tests', 'plugin.xml'))) {
+ me.installSinglePlugin(path.join(plugin.dir, 'tests'));
+ }
+ });
+ this.showPluginsVersions();
+};
+
+PluginsManager.prototype.installSinglePlugin = function(plugin) {
+ if (fs.existsSync(path.resolve(this.storedCWD, plugin))) {
+ plugin = path.resolve(this.storedCWD, plugin);
+ }
+
+ logger.normal("cordova-paramedic: installing " + plugin);
+ // var pluginPath = path.resolve(this.storedCWD, plugin);
+
+ var plugAddCmd = exec('cordova plugin add ' + plugin);
+ if(plugAddCmd.code !== 0) {
+ logger.error('Failed to install plugin : ' + plugin);
+ throw new Error('Failed to install plugin : ' + plugin);
+ }
+};
+
+PluginsManager.prototype.showPluginsVersions = function() {
+ logger.verbose("cordova-paramedic: versions of installed plugins: ");
+ exec('cordova plugins');
+};
+
+module.exports = PluginsManager;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/Target.js
----------------------------------------------------------------------
diff --git a/lib/Target.js b/lib/Target.js
new file mode 100644
index 0000000..0cb5c53
--- /dev/null
+++ b/lib/Target.js
@@ -0,0 +1,9 @@
+
+function Target(config) {
+ this.platform = config.platform;
+ this.action = config.action || 'run';
+ this.args = config.args || null;
+ this.platformId = this.platform.split("@")[0];
+}
+
+module.exports = Target;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/TestsRunner.js
----------------------------------------------------------------------
diff --git a/lib/TestsRunner.js b/lib/TestsRunner.js
new file mode 100644
index 0000000..8515b58
--- /dev/null
+++ b/lib/TestsRunner.js
@@ -0,0 +1,121 @@
+var Q = require('q');
+var fs = require('fs');
+var path = require('path');
+var exec = require('./utils').exec;
+var logger = require('./logger').get();
+
+var MAX_PENDING_TIME = 60000;
+
+function TestsRunner (server) {
+ this.server = server;
+}
+
+TestsRunner.prototype.runSingleTarget = function (target, savePath) {
+ logger.info("Running target: " + target.platform);
+
+ var me = this;
+
+ this.server.reset(savePath);
+
+ return this.prepareAppToRunTarget(target).then(function() {
+ return me.installPlatform(target);
+ }).then(function() {
+ return me.checkPlatform(target);
+ }).then(function() {
+ return me.runTests(target);
+ })
+ .then(function (results) {
+ logger.normal("Removing platform: " + target.platformId);
+ exec('cordova platform rm ' + target.platformId);
+
+ return results;
+ })
+ .catch(function(error) {return error;});
+};
+
+TestsRunner.prototype.prepareAppToRunTarget = function(target) {
+ var me = this;
+ return Q.Promise(function(resolve, reject) {
+ // if we know external url we can safely use it
+ if (me.server.haveConnectionUrl()) {
+ me.writeMedicLogUrl(me.server.getConnectionUrl());
+ } else {
+ // otherwise, we assume we use local PC and platforms emulators
+ switch(target.platformId) {
+ case "android":
+ me.writeMedicLogUrl("http://10.0.2.2:" + me.server.port);
+ break;
+ case "ios" :
+ case "browser" :
+ case "windows" :
+ /* falls through */
+ default:
+ me.writeMedicLogUrl("http://127.0.0.1:" + me.server.port);
+ break;
+ }
+ }
+ resolve();
+ });
+};
+
+TestsRunner.prototype.installPlatform = function(target) {
+ return Q.Promise(function(resolve, reject) {
+ logger.normal("cordova-paramedic: adding platform : " + target.platform);
+ exec('cordova platform add ' + target.platform);
+ resolve();
+ });
+};
+
+TestsRunner.prototype.checkPlatform = function(target) {
+ return Q.Promise(function(resolve, reject) {
+ logger.normal("cordova-paramedic: checking requirements for platform " + target.platformId);
+ var result = exec('cordova requirements ' + target.platformId);
+ if (result.code !== 0) {
+ reject(new Error('Platform requirements check has failed! Skipping...'));
+ } else resolve();
+ });
+};
+
+TestsRunner.prototype.writeMedicLogUrl = function(url) {
+ logger.normal("cordova-paramedic: writing medic log url to project " + url);
+ var obj = {logurl:url};
+ fs.writeFileSync(path.join("www","medic.json"), JSON.stringify(obj));
+};
+
+TestsRunner.prototype.runTests = function(target) {
+ logger.normal('Starting tests');
+ var self = this;
+
+ var cmd = "cordova " + target.action + " " + target.platformId;
+ if (target.args) {
+ cmd += " " + target.args;
+ }
+
+ logger.normal('cordova-paramedic: running command ' + cmd);
+
+ return Q.Promise(function (resolve, reject) {
+ logger.normal('Waiting for tests result');
+
+ self.server.onTestsResults = function (results) {
+ resolve(results);
+ };
+
+ exec(cmd, function(code, output){
+ if(code) {
+ reject(new Error("cordova build returned error code " + code));
+ }
+
+ var waitForTestResults = target.action === 'run' || target.action === 'emulate';
+
+ if (!waitForTestResults) resolve({passed: true}); // skip tests if it was justbuild
+
+ setTimeout(function(){
+ if (!self.server.connection)
+ reject(new Error("Seems like device not connected to local server in " + MAX_PENDING_TIME / 1000 + " secs. Skipping this platform..."));
+ }, MAX_PENDING_TIME);
+ }
+ );
+ });
+};
+
+module.exports = TestsRunner;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/Tunnel.js
----------------------------------------------------------------------
diff --git a/lib/Tunnel.js b/lib/Tunnel.js
new file mode 100644
index 0000000..bed31e5
--- /dev/null
+++ b/lib/Tunnel.js
@@ -0,0 +1,19 @@
+ var exec = require('./utils').exec;
+ var path = require('path');
+ var Q = require('Q');
+
+function Tunnel(port) {
+ this.port = port;
+}
+
+Tunnel.prototype.createTunnel = function() {
+ var self = this;
+ //TODO: use localtunnel module instead of shell
+ return Q.Promise(function(resolve, reject) {
+ exec(path.resolve(__dirname, '../node_modules/.bin/lt') + ' --port ' + self.port, null, function(output) {
+ resolve(output.split(' ')[3]);
+ });
+ });
+};
+
+module.exports = Tunnel;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/logger.js
----------------------------------------------------------------------
diff --git a/lib/logger.js b/lib/logger.js
new file mode 100644
index 0000000..6a525c9
--- /dev/null
+++ b/lib/logger.js
@@ -0,0 +1,150 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+var ansi = require('ansi');
+var EventEmitter = require('events').EventEmitter;
+var CordovaError = require('cordova-common').CordovaError;
+var EOL = require('os').EOL;
+
+var INSTANCE;
+
+function CordovaLogger () {
+ this.levels = {};
+ this.colors = {};
+ this.stdout = process.stdout;
+ this.stderr = process.stderr;
+
+ this.stdoutCursor = ansi(this.stdout);
+ this.stderrCursor = ansi(this.stderr);
+
+ this.addLevel('verbose', 1000, 'grey');
+ this.addLevel('normal' , 2000);
+ this.addLevel('warn' , 2000, 'yellow');
+ this.addLevel('info' , 3000, 'blue');
+ this.addLevel('error' , 5000, 'red');
+ this.addLevel('results' , 10000);
+
+ this.setLevel('normal');
+}
+
+CordovaLogger.get = function () {
+ return INSTANCE || (INSTANCE = new CordovaLogger());
+};
+
+CordovaLogger.VERBOSE = 'verbose';
+CordovaLogger.NORMAL = 'normal';
+CordovaLogger.WARN = 'warn';
+CordovaLogger.INFO = 'info';
+CordovaLogger.ERROR = 'error';
+CordovaLogger.RESULTS = 'results';
+
+CordovaLogger.prototype.log = function (logLevel, message) {
+ // if there is no such logLevel defined, or provided level has
+ // less severity than active level, then just ignore this call and return
+ if (!this.levels[logLevel] || this.levels[logLevel] < this.levels[this.logLevel])
+ // return instance to allow to chain calls
+ return this;
+ var isVerbose = this.logLevel === 'verbose';
+ var cursor = this.stdoutCursor;
+
+ if(message instanceof Error || logLevel === CordovaLogger.ERROR) {
+ message = formatError(message, isVerbose);
+ cursor = this.stderrCursor;
+ }
+
+ var color = this.colors[logLevel];
+ if (color) {
+ cursor.bold().fg[color]();
+ }
+
+ cursor.write(message).reset().write(EOL);
+
+ return this;
+};
+
+CordovaLogger.prototype.addLevel = function (level, severity, color) {
+
+ this.levels[level] = severity;
+
+ if (color) {
+ this.colors[level] = color;
+ }
+
+ // Define own method with corresponding name
+ if (!this[level]) {
+ this[level] = this.log.bind(this, level);
+ }
+
+ return this;
+};
+
+CordovaLogger.prototype.setLevel = function (logLevel) {
+ this.logLevel = this.levels[logLevel] ? logLevel : CordovaLogger.NORMAL;
+
+ return this;
+};
+
+CordovaLogger.prototype.subscribe = function (eventEmitter) {
+
+ if (!(eventEmitter instanceof EventEmitter))
+ throw new Error('Must provide a valid EventEmitter instance to subscribe CordovaLogger to');
+
+ var self = this;
+
+ process.on('uncaughtException', function(err) {
+ self.error(err);
+ process.exit(1);
+ });
+
+ eventEmitter.on('verbose', self.verbose)
+ .on('log', self.normal)
+ .on('info', self.info)
+ .on('warn', self.warn)
+ .on('warning', self.warn)
+ // Set up event handlers for logging and results emitted as events.
+ .on('results', self.results);
+
+ return this;
+};
+
+function formatError(error, isVerbose) {
+ var message = '';
+
+ if(error instanceof CordovaError) {
+ message = error.toString(isVerbose);
+ } else if(error instanceof Error) {
+ if(isVerbose) {
+ message = error.stack;
+ } else {
+ message = error.message;
+ }
+ } else {
+ // Plain text error message
+ message = error;
+ }
+
+ if(message.toUpperCase().indexOf('ERROR:') !== 0) {
+ // Needed for backward compatibility with external tools
+ message = 'Error: ' + message;
+ }
+
+ return message;
+}
+
+module.exports = CordovaLogger;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/paramedic.js
----------------------------------------------------------------------
diff --git a/lib/paramedic.js b/lib/paramedic.js
new file mode 100644
index 0000000..3828cf2
--- /dev/null
+++ b/lib/paramedic.js
@@ -0,0 +1,156 @@
+#!/usr/bin/env node
+
+var exec = require('./utils').exec,
+ shell = require('shelljs'),
+ Server = require('./LocalServer'),
+ Q = require('q'),
+ tmp = require('tmp'),
+ PluginsManager = require('./PluginsManager'),
+ specReporters = require('./specReporters'),
+ TestsRunner = require('./TestsRunner'),
+ portScanner = require('./portScanner'),
+ path = require('path'),
+ Tunnel = require('./Tunnel');
+
+var Q = require('q');
+var logger = require('./logger').get();
+
+var TESTS_PASS = 0;
+var TESTS_FAILURE = 1;
+var NONTESTS_FAILURE = 2;
+
+function ParamedicRunner(config, _callback) {
+ this.tunneledUrl = "";
+ this.tempFolder = null;
+ this.pluginsManager = null;
+ this.testsPassed = true;
+
+ this.config = config;
+
+ exec.setVerboseLevel(config.isVerbose());
+ logger.setLevel(config.isVerbose() ? 'verbose' : 'normal');
+}
+
+ParamedicRunner.prototype = {
+ run: function() {
+ var cordovaVersion = exec('cordova --version');
+ var npmVersion = exec('npm -v');
+
+ if (cordovaVersion.code || npmVersion.code) {
+ logger.error(cordovaVersion.output + npmVersion.output);
+ process.exit(1);
+ }
+
+ logger.normal("cordova-paramedic: using cordova version " + cordovaVersion.output.replace('\n', ''));
+ logger.normal("cordova-paramedic: using npm version " + npmVersion.output.replace('\n', ''));
+
+ var self = this;
+
+ this.createTempProject();
+ this.installPlugins();
+
+ // Set up start page for tests
+ logger.normal("cordova-paramedic: setting app start page to test page");
+ shell.sed('-i', 'src="index.html"', 'src="cdvtests/index.html"', 'config.xml');
+
+ var startPort = this.config.getPorts().start,
+ endPort = this.config.getPorts().end;
+ logger.info("cordova-paramedic: scanning ports from " + startPort + " to " + endPort);
+
+ // Initialize test reporters
+ specReporters.initialize(this.config);
+
+ return portScanner.getFirstAvailablePort(startPort, endPort).then(function(port) {
+ self.port = port;
+
+ logger.info("cordova-paramedic: port " + port + " is available");
+
+ if (self.config.useTunnel()) {
+ self.tunnel = new Tunnel(port);
+ logger.info('cordova-paramedic: attempt to create local tunnel');
+ return self.tunnel.createTunnel();
+ }
+ }).then(function(url) {
+ if (url) {
+ logger.info('cordova-paramedic: using tunneled url ' + url);
+ self.tunneledUrl = url;
+ }
+
+ logger.info("cordova-paramedic: starting local medic server");
+ return Server.startServer(self.port, self.config.getExternalServerUrl(), self.tunneledUrl);
+ }).then(function (server) {
+
+ var testsRunner = new TestsRunner(server);
+ return self.config.getTargets().reduce(function (promise, target) {
+ return promise.then( function() {
+ return testsRunner.runSingleTarget(target).then(function(results) {
+ if (results instanceof Error) {
+ self.testsPassed = false;
+ return logger.error(results.message);
+ }
+
+ logger.info("cordova-paramedic: tests done for platform " + target.platform);
+
+ var targetTestsPassed = results && results.passed;
+ self.testsPassed = self.testsPassed && targetTestsPassed;
+
+ if (!results) {
+ logger.error("Result: tests has not been completed in time, crashed or there is connectivity issue.");
+ } else if (targetTestsPassed) {
+ logger.info("Result: passed");
+ } else {
+ logger.error("Result: passed=" + results.passed + ", failures=" + results.mobilespec.failures);
+ }
+ });
+ });
+ }, Q());
+ }).then(function(res) {
+ if (self.testsPassed) {
+ logger.info("All tests have been passed.");
+ return TESTS_PASS;
+ } else {
+ logger.error("There are tests failures.");
+ return TESTS_FAILURE;
+ }
+ }, function(err) {
+ logger.error("Failed: " + err);
+ return NONTESTS_FAILURE;
+ }).then(function(exitCode){
+ if(self.config.shouldCleanUpAfterRun()) {
+ logger.info("cordova-paramedic: Deleting the application: " + self.tempFolder.name);
+ shell.popd();
+ shell.rm('-rf', self.tempFolder.name);
+ }
+ process.exit(exitCode);
+ });
+ },
+ createTempProject: function() {
+ this.tempFolder = tmp.dirSync();
+ tmp.setGracefulCleanup();
+ logger.info("cordova-paramedic: creating temp project at " + this.tempFolder.name);
+ exec('cordova create ' + this.tempFolder.name);
+ shell.pushd(this.tempFolder.name);
+ },
+ installPlugins: function() {
+ logger.info("cordova-paramedic: installing plugins");
+ this.pluginsManager = new PluginsManager(this.tempFolder.name, this.storedCWD);
+ this.pluginsManager.installPlugins(this.config.getPlugins());
+ this.pluginsManager.installTestsForExistingPlugins();
+ this.pluginsManager.installSinglePlugin('cordova-plugin-test-framework');
+ this.pluginsManager.installSinglePlugin('cordova-plugin-device');
+ this.pluginsManager.installSinglePlugin(path.join(__dirname, '../paramedic-plugin'));
+ }
+};
+
+var storedCWD = null;
+
+exports.run = function(paramedicConfig) {
+
+ storedCWD = storedCWD || process.cwd();
+
+ var runner = new ParamedicRunner(paramedicConfig, null);
+ runner.storedCWD = storedCWD;
+
+ return runner.run()
+ .timeout(paramedicConfig.getTimeout(), "This test seems to be blocked :: timeout exceeded. Exiting ...");
+};
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/portScanner.js
----------------------------------------------------------------------
diff --git a/lib/portScanner.js b/lib/portScanner.js
new file mode 100644
index 0000000..df0cf15
--- /dev/null
+++ b/lib/portScanner.js
@@ -0,0 +1,54 @@
+var net = require('net');
+var Q = require('q');
+var PORT_NOT_AVAILABLE = 'EADDRINUSE';
+
+var isPortAvailable = function (port) {
+ return new Q.Promise(function(resolve, reject) {
+ var testServer = net.createServer()
+ .once('error', function(err) {
+ if (err.code === PORT_NOT_AVAILABLE) {
+ reject(new Error('Port is not available'));
+ } else {
+ reject(err);
+ }
+ })
+ .once('listening', function() {
+ testServer.once('close', function() {
+ resolve(port);
+ }).close();
+ })
+ .listen(port);
+ });
+};
+
+var checkPorts = function(startPort, endPort, onFoundPort, onError) {
+ var currentPort = startPort;
+
+ isPortAvailable(currentPort)
+ .then(function(port) {
+ onFoundPort(port);
+ }, function(error) {
+ if (error.message === 'Port is not available') {
+ currentPort++;
+ if (currentPort > endPort) {
+ onError(new Error('All ports are unavailable!'));
+ } else {
+ checkPorts(currentPort, endPort, onFoundPort, onError);
+ }
+ } else onError(error);
+ });
+ };
+
+var getFirstAvailablePort = function (startPort, endPort) {
+ if (startPort > endPort) {
+ var buffer = startPort;
+ startPort = endPort;
+ endPort = buffer;
+ }
+
+ return new Q.Promise(function(resolve, reject) {
+ checkPorts(startPort, endPort, resolve, reject);
+ });
+};
+
+module.exports.getFirstAvailablePort = getFirstAvailablePort;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/reporters/ConsoleReporter.js
----------------------------------------------------------------------
diff --git a/lib/reporters/ConsoleReporter.js b/lib/reporters/ConsoleReporter.js
new file mode 100644
index 0000000..84f447e
--- /dev/null
+++ b/lib/reporters/ConsoleReporter.js
@@ -0,0 +1,153 @@
+var noopTimer = {
+ start: function(){},
+ elapsed: function(){ return 0; }
+ };
+
+ function ConsoleReporter(options) {
+ var print = options.print,
+ showColors = options.showColors || false,
+ onComplete = options.onComplete || function() {},
+ timer = options.timer || noopTimer,
+ specCount,
+ failureCount,
+ failedSpecs = [],
+ pendingCount,
+ ansi = {
+ green: '\x1B[32m',
+ red: '\x1B[31m',
+ yellow: '\x1B[33m',
+ none: '\x1B[0m'
+ },
+ failedSuites = [];
+
+ this.jasmineStarted = function() {
+ specCount = 0;
+ failureCount = 0;
+ pendingCount = 0;
+ print('Started');
+ printNewline();
+ timer.start();
+ };
+
+ this.jasmineDone = function() {
+ printNewline();
+ for (var i = 0; i < failedSpecs.length; i++) {
+ specFailureDetails(failedSpecs[i]);
+ }
+
+ if(specCount > 0) {
+ printNewline();
+
+ var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' +
+ failureCount + ' ' + plural('failure', failureCount);
+
+ if (pendingCount) {
+ specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount);
+ }
+
+ print(specCounts);
+ } else {
+ print('No specs found');
+ }
+
+ printNewline();
+ var seconds = timer.elapsed() / 1000;
+ print('Finished in ' + seconds + ' ' + plural('second', seconds));
+ printNewline();
+
+ for(i = 0; i < failedSuites.length; i++) {
+ suiteFailureDetails(failedSuites[i]);
+ }
+
+ onComplete(failureCount === 0);
+ };
+
+ this.specStarted = function(){};
+ this.suiteStarted = function(){};
+
+ this.specDone = function(result) {
+ specCount++;
+
+ if (result.status == 'pending') {
+ pendingCount++;
+ print(colored('yellow', '*'));
+ return;
+ }
+
+ if (result.status == 'passed') {
+ print(colored('green', '.'));
+ return;
+ }
+
+ if (result.status == 'failed') {
+ failureCount++;
+ failedSpecs.push(result);
+ print(colored('red', 'F'));
+ }
+ };
+
+ this.suiteDone = function(result) {
+ if (result.failedExpectations && result.failedExpectations.length > 0) {
+ failureCount++;
+ failedSuites.push(result);
+ }
+ };
+
+ return this;
+
+ function printNewline() {
+ print('\n');
+ }
+
+ function colored(color, str) {
+ return showColors ? (ansi[color] + str + ansi.none) : str;
+ }
+
+ function plural(str, count) {
+ return count == 1 ? str : str + 's';
+ }
+
+ function repeat(thing, times) {
+ var arr = [];
+ for (var i = 0; i < times; i++) {
+ arr.push(thing);
+ }
+ return arr;
+ }
+
+ function indent(str, spaces) {
+ var lines = (str || '').split('\n');
+ var newArr = [];
+ for (var i = 0; i < lines.length; i++) {
+ newArr.push(repeat(' ', spaces).join('') + lines[i]);
+ }
+ return newArr.join('\n');
+ }
+
+ function specFailureDetails(result) {
+ printNewline();
+ print(result.fullName);
+
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ var failedExpectation = result.failedExpectations[i];
+ printNewline();
+ print(indent(failedExpectation.message, 2));
+ print(indent(failedExpectation.stack, 2));
+ }
+
+ printNewline();
+ }
+
+ function suiteFailureDetails(result) {
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ printNewline();
+ print(colored('red', 'An error was thrown in an afterAll'));
+ printNewline();
+ print(colored('red', 'AfterAll ' + result.failedExpectations[i].message));
+
+ }
+ printNewline();
+ }
+ }
+
+module.exports = ConsoleReporter;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/reporters/ParamedicReporter.js
----------------------------------------------------------------------
diff --git a/lib/reporters/ParamedicReporter.js b/lib/reporters/ParamedicReporter.js
new file mode 100644
index 0000000..2742e67
--- /dev/null
+++ b/lib/reporters/ParamedicReporter.js
@@ -0,0 +1,43 @@
+
+function ParamedicReporter() {
+ var results = [],
+ specsExecuted = 0,
+ failureCount = 0,
+ pendingSpecCount = 0,
+ cordovaInfo = null;
+
+ this.specDone = function(result) {
+ if (result.status != "disabled") {
+ specsExecuted++;
+ }
+ if (result.status == "failed") {
+ failureCount++;
+ results.push(result);
+ }
+ if (result.status == "pending") {
+ pendingSpecCount++;
+ }
+ };
+
+ this.jasmineDone = function(data) {
+ cordovaInfo = data.cordova;
+ };
+
+ this.getResults = function() {
+ return {
+ passed: failureCount === 0,
+ mobilespec: {
+ specs:specsExecuted,
+ failures:failureCount,
+ results: results
+ },
+ platform: cordovaInfo.platform,
+ version: cordovaInfo.version,
+ model: cordovaInfo.model
+ };
+ };
+
+ return this;
+}
+
+module.exports = ParamedicReporter;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/specReporters.js
----------------------------------------------------------------------
diff --git a/lib/specReporters.js b/lib/specReporters.js
new file mode 100644
index 0000000..21e2eab
--- /dev/null
+++ b/lib/specReporters.js
@@ -0,0 +1,68 @@
+
+// not currently used
+// var ConsoleReporter = require('./reporters/ConsoleReporter');
+
+var ParamedicReporter = require('./reporters/ParamedicReporter');
+var JasmineSpecReporter = require('jasmine-spec-reporter');
+var jasmineReporters = require('jasmine-reporters');
+
+// default reporter which is used to determine whether tests pass or not
+var paramedicReporter = null;
+
+// all reporters including additional reporters to trace results to console, etc
+var reporters = [];
+
+// specifies path to save Junit results file
+var reportSavePath = null;
+
+function reset() {
+ paramedicReporter = new ParamedicReporter();
+ // default paramedic reporter
+ reporters = [paramedicReporter];
+ // extra reporters
+ reporters.push(new JasmineSpecReporter({displayPendingSummary: false, displaySuiteNumber: true}));
+ if(reportSavePath){
+ reporters.push(new jasmineReporters.JUnitXmlReporter({savePath: reportSavePath, consolidateAll: false}));
+ }
+}
+
+module.exports = {
+ reset: reset,
+ initialize: function(config) {
+ reportSavePath = config.getReportSavePath();
+ reset();
+ },
+ getResults: function() {
+ return paramedicReporter.getResults();
+ },
+ jasmineStarted: function(data) {
+ reporters.forEach(function (reporter) {
+ reporter.jasmineStarted && reporter.jasmineStarted(data);
+ });
+ },
+ specStarted: function(data) {
+ reporters.forEach(function (reporter) {
+ reporter.specStarted && reporter.specStarted(data);
+ });
+ },
+ specDone: function(data) {
+ reporters.forEach(function (reporter) {
+ reporter.specDone && reporter.specDone(data);
+ });
+ },
+ suiteStarted: function(data) {
+ reporters.forEach(function (reporter) {
+ reporter.suiteStarted && reporter.suiteStarted(data);
+ });
+ },
+ suiteDone: function(data) {
+ reporters.forEach(function (reporter) {
+ reporter.suiteDone && reporter.suiteDone(data);
+ });
+ },
+ jasmineDone: function(data) {
+ reporters.forEach(function (reporter) {
+ reporter.jasmineDone && reporter.jasmineDone(data);
+ });
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/lib/utils.js
----------------------------------------------------------------------
diff --git a/lib/utils.js b/lib/utils.js
new file mode 100644
index 0000000..35ce7c7
--- /dev/null
+++ b/lib/utils.js
@@ -0,0 +1,22 @@
+var shelljs = require('shelljs');
+var verbose;
+
+function exec(cmd, onFinish, onData) {
+ if (onFinish instanceof Function || onFinish === null) {
+ var result = shelljs.exec(cmd, {async: true, silent: !verbose}, onFinish);
+
+ if (onData instanceof Function) {
+ result.stdout.on('data', onData);
+ }
+ } else {
+ return shelljs.exec(cmd, {silent: !verbose});
+ }
+}
+
+exec.setVerboseLevel = function(_verbose) {
+ verbose = _verbose;
+};
+
+module.exports = {
+ exec: exec
+};
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/main.js
----------------------------------------------------------------------
diff --git a/main.js b/main.js
index b772665..a538c94 100755
--- a/main.js
+++ b/main.js
@@ -1,43 +1,52 @@
#!/usr/bin/env node
var parseArgs = require('minimist'),
- paramedic = require('./paramedic');
-
-var plugins,
- platformId;
+ path = require('path'),
+ paramedic = require('./lib/paramedic'),
+ ParamedicConfig = require('./lib/ParamedicConfig');
var USAGE = "Error missing args. \n" +
- "cordova-paramedic --platform PLATFORM --plugin PATH [--justbuild --timeout MSECS --port PORTNUM --browserify]\n" +
- "`PLATFORM` : the platform id, currently only supports 'ios'\n" +
- "`PATH` : the relative or absolute path to a plugin folder\n" +
- "\texpected to have a 'tests' folder.\n" +
- "\tYou may specify multiple --plugin flags and they will all\n" +
- "\tbe installed and tested together.\n" +
- "`MSECS` : (optional) time in millisecs to wait for tests to pass|fail \n" +
- "\t(defaults to 10 minutes) \n" +
- "`PORTNUM` : (optional) port to use for posting results from emulator back to paramedic server\n" +
- "--justbuild : (optional) just builds the project, without running the tests \n" +
+ "cordova-paramedic --platform PLATFORM --plugin PATH [--justbuild --timeout MSECS --startport PORTNUM --endport PORTNUM --browserify]\n" +
+ "`PLATFORM` : the platform id. Currently supports 'ios', 'browser', 'windows', 'android', 'wp8'.\n" +
+ "\tPath to platform can be specified as link to git repo like:\n" +
+ "\twindows@https://github.com/apache/cordova-windows.git\n" +
+ "\tor path to local copied git repo like:\n" +
+ "\twindows@../cordova-windows/\n" +
+ "`PATH` : the relative or absolute path to a plugin folder\n" +
+ "\texpected to have a 'tests' folder.\n" +
+ "\tYou may specify multiple --plugin flags and they will all\n" +
+ "\tbe installed and tested together.\n" +
+ "`MSECS` : (optional) time in millisecs to wait for tests to pass|fail \n" +
+ "\t(defaults to 10 minutes) \n" +
+ "`PORTNUM` : (optional) ports to find available and use for posting results from emulator back to paramedic server(default is from 8008 to 8009)\n" +
+ "--justbuild : (optional) just builds the project, without running the tests \n" +
"--browserify : (optional) plugins are browserified into cordova.js \n" +
"--verbose : (optional) verbose mode. Display more information output\n" +
- "--platformPath : (optional) path to install platform from, git or local file uri";
+ "--useTunnel : (optional) use tunneling instead of local address. default is false\n" +
+ "--config : (optional) read configuration from paramedic configuration file\n" +
+ "--reportSavePath: (optional) path to save Junit results file\n" +
+ "--cleanUpAfterRun: (optional) cleans up the application after the run.";
var argv = parseArgs(process.argv.slice(2));
+var pathToParamedicConfig = argv.config && path.resolve(argv.config);
-if(!argv.platform) {
- console.log(USAGE);
- process.exit(1);
-}
+if (pathToParamedicConfig || // --config
+ argv.platform && argv.plugin) { // or --platform and --plugin
-var onComplete = function(resCode,resObj,logStr) {
- console.log("result code is : " + resCode);
- if(resObj) {
- console.log(JSON.stringify(resObj));
- }
- if(logStr) {
- console.log(logStr);
- }
- process.exit(resCode);
-};
+ var paramedicConfig = pathToParamedicConfig ?
+ ParamedicConfig.parseFromFile(pathToParamedicConfig):
+ ParamedicConfig.parseFromArguments(argv);
-paramedic.run(argv.platform, argv.plugin, onComplete, argv.justbuild, argv.port, argv.timeout, argv.browserify, false, argv.verbose, argv.platformPath);
+ paramedic.run(paramedicConfig)
+ .catch(function (error) {
+ console.log(JSON.stringify(error));
+ process.exit(1);
+ })
+ .done(function (result) {
+ console.log(JSON.stringify(result));
+ });
+} else {
+ console.log(USAGE);
+ process.exit(1);
+}
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 8eddb4f..77e6ce0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"name": "cordova-paramedic",
"version": "0.5.0",
+ "license": "Apache-2.0",
"description": "Use medic to test a cordova plugin locally",
"main": "paramedic.js",
"bin": {
@@ -11,15 +12,14 @@
"url": "git://github.com/apache/cordova-paramedic.git"
},
"scripts": {
- "test":"npm run jshint & npm run test-ios",
- "jshint":"node node_modules/jshint/bin/jshint *.js",
- "test-appveyor":"npm run test-windows",
- "test-travis":"npm run test-ios",
- "test-android":"node main.js --platform android --plugin ./spec/testable-plugin/",
+ "test": "npm run jshint & npm run test-ios",
+ "jshint": "node node_modules/jshint/bin/jshint lib/",
+ "test-appveyor": "npm run test-windows",
+ "test-travis": "npm run test-ios",
+ "test-android": "node main.js --platform android --plugin ./spec/testable-plugin/",
"test-ios": "node main.js --platform ios --plugin ./spec/testable-plugin/ --verbose",
- "test-windows" : "node main.js --platform windows --plugin ./spec/testable-plugin/",
+ "test-windows": "node main.js --platform windows --plugin ./spec/testable-plugin/",
"test-wp8": "node main.js --platform wp8 --plugin ./spec/testable-plugin/"
-
},
"keywords": [
"cordova",
@@ -29,14 +29,20 @@
"author": "Jesse MacFadyen",
"license": "Apache 2.0",
"dependencies": {
+ "ansi": "^0.3.1",
+ "cordova-common": "^1.0.0",
+ "jasmine-spec-reporter": "^2.4.0",
+ "jasmine-reporters": "^2.1.1",
"localtunnel": "~1.5.0",
"minimist": "~1.1.0",
+ "q": "^1.4.1",
"request": "^2.53.0",
"shelljs": "~0.3.0",
+ "socket.io": "^1.4.5",
"tmp": "0.0.25"
},
"devDependencies": {
- "jasmine-node": "~1",
- "jshint": "^2.6.0"
+ "jasmine-node": "~1",
+ "jshint": "^2.6.0"
}
}
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/paramedic-plugin/JasmineParamedicProxy.js
----------------------------------------------------------------------
diff --git a/paramedic-plugin/JasmineParamedicProxy.js b/paramedic-plugin/JasmineParamedicProxy.js
new file mode 100644
index 0000000..854efc6
--- /dev/null
+++ b/paramedic-plugin/JasmineParamedicProxy.js
@@ -0,0 +1,56 @@
+function JasmineParamedicProxy(socket) {
+ this.socket = socket;
+ //jasmineRequire.JsApiReporter.apply(this, arguments);
+}
+
+// JasmineParamedicProxy.prototype = jasmineRequire.JsApiReporter.prototype;
+// JasmineParamedicProxy.prototype.constructor = JasmineParamedicProxy;
+
+JasmineParamedicProxy.prototype.jasmineStarted = function (o) {
+ this.socket.emit('jasmineStarted', o);
+};
+
+JasmineParamedicProxy.prototype.specStarted = function (o) {
+ this.socket.emit('specStarted', o);
+};
+
+JasmineParamedicProxy.prototype.specDone = function (o) {
+ this.socket.emit('specDone', o);
+};
+
+JasmineParamedicProxy.prototype.suiteStarted = function (o) {
+ this.socket.emit('suiteStarted', o);
+};
+
+JasmineParamedicProxy.prototype.suiteDone = function (o) {
+ this.socket.emit('suiteDone', o);
+};
+
+JasmineParamedicProxy.prototype.jasmineDone = function (o) {
+ var p = 'Desktop';
+ var devmodel='none';
+ var version = cordova.version;
+ if(typeof device != 'undefined') {
+ p = device.platform.toLowerCase();
+ devmodel=device.model || device.name;
+ version = device.version.toLowerCase();
+ }
+
+ o = o || {};
+
+ // include platform info
+ o.cordova = {
+ platform:(platformMap.hasOwnProperty(p) ? platformMap[p] : p),
+ version:version,
+ model:devmodel
+ }
+
+ this.socket.emit('jasmineDone', o);
+};
+
+var platformMap = {
+ 'ipod touch':'ios',
+ 'iphone':'ios'
+};
+
+module.exports = JasmineParamedicProxy;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/paramedic-plugin/paramedic.js
----------------------------------------------------------------------
diff --git a/paramedic-plugin/paramedic.js b/paramedic-plugin/paramedic.js
new file mode 100644
index 0000000..6c210e6
--- /dev/null
+++ b/paramedic-plugin/paramedic.js
@@ -0,0 +1,74 @@
+var io = cordova.require('cordova-plugin-paramedic.socket.io');
+
+var PARAMEDIC_SERVER_DEFAULT_URL = 'http://127.0.0.1:8008';
+
+function Paramedic() {
+
+}
+
+Paramedic.prototype.initialize = function() {
+ var me = this;
+ var connectionUri = loadParamedicServerUrl();
+ this.socket = io.connect(connectionUri);
+
+ this.socket.on('connect', function () {
+ console.log("Paramedic has been susccessfully connected to server");
+ if (typeof device != 'undefined') me.socket.emit('deviceInfo', device);
+ });
+
+ this.overrideConsole();
+ this.injectJasmineReporter();
+};
+
+
+Paramedic.prototype.overrideConsole = function () {
+
+ var origConsole = window.console;
+ var me = this;
+
+ function createCustomLogger(type) {
+ return function () {
+ origConsole[type].apply(origConsole, arguments);
+
+ me.socket.emit('log', { type: type, msg: Array.prototype.slice.apply(arguments) });
+ };
+ }
+ window.console = {
+ log: createCustomLogger('log'),
+ warn: createCustomLogger('warn'),
+ error: createCustomLogger('error'),
+ };
+ console.log('Paramedic console has been installed.');
+};
+
+Paramedic.prototype.injectJasmineReporter = function () {
+ var JasmineParamedicProxy = require('cordova-plugin-paramedic.JasmineParamedicProxy');
+ var jasmineProxy = new JasmineParamedicProxy(this.socket);
+ var testsModule = cordova.require("cordova-plugin-test-framework.cdvtests");
+ var defineAutoTestsOriginal = testsModule.defineAutoTests;
+
+ testsModule.defineAutoTests = function () {
+ defineAutoTestsOriginal();
+ jasmine.getEnv().addReporter(jasmineProxy);
+ };
+};
+
+new Paramedic().initialize();
+
+function loadParamedicServerUrl() {
+
+ try {
+ // attempt to synchronously load medic config
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "../medic.json", false);
+ xhr.send(null);
+ var cfg = JSON.parse(xhr.responseText);
+
+ return cfg.logurl || PARAMEDIC_SERVER_DEFAULT_URL;
+
+ } catch (ex) {}
+
+ return PARAMEDIC_SERVER_DEFAULT_URL;
+}
+
+module.exports = Paramedic;
http://git-wip-us.apache.org/repos/asf/cordova-paramedic/blob/b1aa699d/paramedic-plugin/plugin.xml
----------------------------------------------------------------------
diff --git a/paramedic-plugin/plugin.xml b/paramedic-plugin/plugin.xml
new file mode 100644
index 0000000..af4d54a
--- /dev/null
+++ b/paramedic-plugin/plugin.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
+ id="cordova-plugin-paramedic"
+ version="1.2.1-dev">
+
+ <name>Paramedic</name>
+ <description>Cordova Paramedic Plugin</description>
+ <license>Apache 2.0</license>
+
+ <js-module src="socket.io.js" name="socket.io" />
+
+ <js-module src="JasmineParamedicProxy.js" name="JasmineParamedicProxy" />
+
+ <js-module src="paramedic.js" name="paramedic">
+ <runs/>
+ </js-module>
+
+</plugin>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org