Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paramedic improvements #3

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -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": {
}
}
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ install:
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- npm install cordova
- npm install ios-sim
# w/o ios-deploy ios requirements check fails
- npm install ios-deploy
- npm install
- npm link
script:
Expand Down
142 changes: 128 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,145 @@ 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 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
```
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
####--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.
```
module.exports = {
// "externalServerUrl": "http://10.0.8.254",
"useTunnel": true,
"plugins": [
"https://github.com/apache/cordova-plugin-inappbrowser"
],
"platform": "windows",
"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);
```


3 changes: 0 additions & 3 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ install:
- npm install cordova
- npm install
- npm link
- cd ..
- git clone https://github.com/apache/cordova-plugin-test-framework
- cd cordova-paramedic

build: off

Expand Down
154 changes: 154 additions & 0 deletions lib/LocalServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/**
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 Q = require('q'),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apache license needed in comments

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

io = require('socket.io'),
logger = require('./utils').logger,
exec = require('./utils').exec,
path = require('path'),
util = require('util'),
portChecker = require('tcp-port-used'),
EventEmitter = require('events').EventEmitter,
localtunnel = require('localtunnel');


// how many ms without a pong packet to consider the connection closed
var CONNECTION_HEARBEAT_PING_TIMEOUT = 60000,
// how many ms before sending a new ping packet
CONNECTION_HEARBEAT_PING_INTERVAL = 25000;

function LocalServer(port, externalServerUrl) {
this.port = port;
this.externalServerUrl = externalServerUrl;
}

util.inherits(LocalServer, EventEmitter);

LocalServer.startServer = function(ports, externalServerUrl, useTunnel) {
logger.normal("local-server: scanning ports from " + ports.start + " to " + ports.end);

return LocalServer.getFirstAvailablePort(ports.start, ports.end)
.then(function(port) {
logger.normal("local-server: port " + port + " is available");
logger.info("local-server: starting local medic server");

var localServer = new LocalServer(port, externalServerUrl);
localServer.createSocketListener();

if (useTunnel) {
return localServer.createTunnel();
}

return localServer;
});
};

LocalServer.getFirstAvailablePort = function(startPort, endPort) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this and startServer be prototype functions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method creates new LocalServer instance so should be static

return portChecker.check(startPort).then(function (isInUse) {
if (!isInUse) {
return startPort;
}
if (startPort < endPort) {
return LocalServer.getFirstAvailablePort(startPort + 1, endPort);
}
throw new Error('Unable to find available port');
});
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpitck: the use of reduce here makes this code pretty hard to follow. It could be refactored to be recursive:; something like this:

return portChecker.check(startPort).then(function (inUse) {
    if (inUse) {
        return getFirstAvailablePort(startPort + 1, endPort);
    }
    resolve(startPort);
});

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, updated


LocalServer.prototype.createTunnel = function() {
logger.info('cordova-paramedic: attempt to create local tunnel');
var self = this;

return Q.Promise(function(resolve, reject) {

var tunnel = localtunnel(self.port, function(err, tunnel) {
if (err) {
reject('Unable to create local tunnel: ' + err);
return;
}

self.tunneledUrl = tunnel.url;
logger.info('cordova-paramedic: using tunneled url ' + self.tunneledUrl);

resolve(self);
});

// this trace is useful to debug test run timeout issue
tunnel.on('close', function() {
logger.normal('local-server: local tunnel has been closed');
});
});
};

LocalServer.prototype.createSocketListener = function() {
var listener = io.listen(this.port, {
pingTimeout: CONNECTION_HEARBEAT_PING_TIMEOUT,
pingInterval: CONNECTION_HEARBEAT_PING_INTERVAL
});

var self = this;

listener.on('connection', function(socket) {
logger.info('local-server: new socket connection');
self.connection = socket;

// server methods
['deviceLog', 'disconnect', 'deviceInfo',
'jasmineStarted', 'specStarted', 'specDone',
'suiteStarted', 'suiteDone', 'jasmineDone'].forEach(function(route) {
socket.on(route, function(data) {
self.emit(route, data);
});
});
});
};

// Connection url could be platform specific so we pass platform as param here
LocalServer.prototype.getConnectionUrl = function(platformId) {
// --useTunnel option
if (this.tunneledUrl) {
return this.tunneledUrl;
}
// --externalServerUrl option / we know ip or dns name
if (this.externalServerUrl) {
return this.externalServerUrl + ":" + this.port;
}
// build connection uri for localhost based on platform
var connectionUrl;

switch(platformId) {
case "android" :
connectionUrl = "http://10.0.2.2";
break;
case "ios" :
case "browser" :
case "windows" :
/* falls through */
default:
connectionUrl = "http://127.0.0.1";
}

return connectionUrl + ":" + this.port;
};

LocalServer.prototype.isDeviceConnected = function() {
return !!this.connection;
};

module.exports = LocalServer;
Loading