Skip to content
This repository has been archived by the owner on Jun 23, 2019. It is now read-only.

Commit

Permalink
Enhancements (#1)
Browse files Browse the repository at this point in the history
* no need to use require

* moved functions into class

* added boilerplate files

* fixed eslint

* added unit tests

* more unit tests

* added eslint standard plugin to dev deps
  • Loading branch information
christian-bromann authored and rhysd committed Sep 23, 2016
1 parent c73415b commit 95c11ad
Show file tree
Hide file tree
Showing 18 changed files with 357 additions and 222 deletions.
3 changes: 3 additions & 0 deletions .babelrc
@@ -0,0 +1,3 @@
{
"presets": ["es2015"]
}
15 changes: 15 additions & 0 deletions .editorconfig
@@ -0,0 +1,15 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = true

[*]

indent_style = space
indent_size = 4

end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
1 change: 1 addition & 0 deletions .eslintignore
@@ -0,0 +1 @@
build/
14 changes: 7 additions & 7 deletions .eslintrc.json → .eslintrc
@@ -1,11 +1,14 @@
{
"extends": ["eslint:recommended", "plugin:node/recommended"],
"plugins": ["node", "promise"],
"extends": ["standard"],
"plugins": ["promise"],
"env": {
"node": true,
"mocha": true
"es6": true
},
"parserOptions": {
"ecmaVersion": 6
},
"rules": {
"indent": [2, 4],
"semi": [2, "always"],
"quotes": [2, "single"],
"linebreak-style": [2, "unix"],
Expand All @@ -14,8 +17,5 @@
"promise/always-return": 2,
"promise/catch-or-return": 2,
"promise/no-native": 0
},
"globals": {
"browser": true
}
}
1 change: 1 addition & 0 deletions .gitignore
@@ -1,2 +1,3 @@
node_modules
npm-debug.log
build
Empty file added .npmignore
Empty file.
6 changes: 2 additions & 4 deletions README.md
Expand Up @@ -2,7 +2,7 @@ Webdriver.io service plugin for Appium
======================================
[![Build Status](https://travis-ci.org/rhysd/wdio-appium-service.svg?branch=master)](https://travis-ci.org/rhysd/wdio-appium-service)

[webdriver.io](http://webdriver.io/) service plugin for [Appium](http://appium.io/). With this service installed, you need not to run Appium manually. Please see [the manual for wdio-selenium-standalone-service](http://webdriver.io/guide/services/selenium-standalone.html) for more detail.
[webdriver.io](http://webdriver.io/) service plugin for [Appium](http://appium.io/). With this service installed, you need not to run Appium manually.

## How to Use

Expand All @@ -12,9 +12,7 @@ Please register this package as service plugin and specify command line argument
{
... // Other config

services: [
require('wdio-appium-service')
],
services: ['appium'],

appium: {
args: {
Expand Down
61 changes: 61 additions & 0 deletions gruntfile.js
@@ -0,0 +1,61 @@
module.exports = function (grunt) {
grunt.initConfig({
pkgFile: 'package.json',
clean: ['build'],
babel: {
options: {
sourceMap: false
},
dist: {
files: [{
expand: true,
cwd: './lib',
src: ['**/*.js'],
dest: 'build',
ext: '.js'
}]
}
},
watch: {
dist: {
files: ['./lib/**/*.js'],
tasks: ['babel:dist']
}
},
eslint: {
target: [
'index.js',
'launcher.js',
'lib/**/*.js'
]
},
contributors: {
options: {
commitMessage: 'update contributors'
}
},
bump: {
options: {
commitMessage: 'v%VERSION%',
pushTo: 'upstream'
}
}
});

require('load-grunt-tasks')(grunt);
grunt.registerTask('default', ['build']);
grunt.registerTask('build', 'Build wdio-appium-service', function () {
grunt.task.run([
'eslint',
'clean',
'babel'
]);
});
grunt.registerTask('release', 'Bump and tag version', function (type) {
grunt.task.run([
'build',
'contributors',
'bump:' + (type || 'patch')
]);
});
};
4 changes: 4 additions & 0 deletions index.js
@@ -0,0 +1,4 @@
/**
* no service required
*/
module.exports = {};
113 changes: 2 additions & 111 deletions launcher.js
@@ -1,111 +1,2 @@
'use strict';

const spawn = require('child_process').spawn;
const path = require('path');
const fs = require('fs');

function lowerCamelToOptionName(s) {
let ret = '--';
const A = 'A'.charCodeAt(0);
const Z = 'Z'.charCodeAt(0);
for (let idx = 0; idx < s.length; ++idx) {
const c = s.charAt(idx);
const code = s.charCodeAt(idx);
if (A <= code && code <= Z) {
ret += '-' + c.toLowerCase();
} else {
ret += c;
}
}
return ret;
}

function keyValueToCliArgs(keyValue) {
if (Array.isArray(keyValue)) {
// Note:
// If specified as array, this plugin assumes it as the string list of command line arguments.
return keyValue;
}

const ret = [];
for (let key in keyValue) {
const value = keyValue[key];
if (typeof value === 'boolean' && !value) {
continue;
}

ret.push(lowerCamelToOptionName(key));

if (typeof value !== 'boolean') {
ret.push(value.toString());
}
}
return ret;
}

function detectAppiumCommand(p) {
while (true) {
p = path.dirname(p);

const parsed = path.parse(p);
if (parsed.root === parsed.dir && parsed.name === '') {
// When 'p' indicates root directory, local 'appium' command was not found.
return null;
}

if (parsed.name !== 'node_modules') {
continue;
}

const cmd = path.join(p, '.bin', 'appium');
try {
if (fs.lstatSync(cmd).isFile()) {
return cmd;
}
} catch(e) {
// Do nothing
}
}
}

class AppiumLauncher {
onPrepare(config) {
const c = config.appium || {};

this.appiumArgs = keyValueToCliArgs(c.args || {});
this.appiumCommand = c.command || detectAppiumCommand(__dirname) || 'appium';
this.appiumWaitStartTime = c.waitStartTime || 5000;

return this._startAppium().then(p => {
this.process = p;
return;
});
}

onComplete() {
if (this.process !== undefined && !this.process.killed) {
this.process.kill();
}
}

_startAppium() {
return new Promise((resolve, reject) => {
const p = spawn(this.appiumCommand, this.appiumArgs, {stdio: ['ignore', 'pipe', 'pipe']});
const timer = setTimeout(() => {
p.removeListener('exit', exitCallback);
if (p.exitCode === null) {
resolve(p);
} else {
reject(new Error('Appium exited just after starting with exit code:' + p.exitCode));
}
}, this.appiumWaitStartTime);
const exitCallback = code => {
clearTimeout(timer);
reject(new Error('Appium exited before timeout (Exit code: ' + code + ')'));
};
p.once('exit', exitCallback);
});
}
}

module.exports = new AppiumLauncher();
var SeleniumLauncher = require('./build/launcher').default;
module.exports = new SeleniumLauncher();
113 changes: 113 additions & 0 deletions lib/launcher.js
@@ -0,0 +1,113 @@
'use strict';

import { spawn } from 'child_process';
import path from 'path';
import fs from 'fs';

class AppiumLauncher {
onPrepare (config) {
const c = config.appium || {};

this.appiumArgs = this._keyValueToCliArgs(c.args || {});
this.appiumCommand = c.command || this._detectAppiumCommand(__dirname) || 'appium';
this.appiumWaitStartTime = c.waitStartTime || 5000;

return this._startAppium().then(p => {
this.process = p;
return;
});
}

onComplete () {
if (this.process !== undefined && !this.process.killed) {
this.process.kill();
}
}

_startAppium () {
return new Promise((resolve, reject) => {
const p = spawn(this.appiumCommand, this.appiumArgs, {stdio: ['ignore', 'pipe', 'pipe']});
const timer = setTimeout(() => {
p.removeListener('exit', exitCallback);
if (p.exitCode === null) {
return resolve(p);
}

return reject(new Error('Appium exited just after starting with exit code:' + p.exitCode));
}, this.appiumWaitStartTime);

const exitCallback = code => {
clearTimeout(timer);
reject(new Error('Appium exited before timeout (Exit code: ' + code + ')'));
};

p.once('exit', exitCallback);
});
}

_detectAppiumCommand (p) {
while (true) {
p = path.dirname(p);

const parsed = path.parse(p);
if (parsed.root === parsed.dir && parsed.name === '') {
// When 'p' indicates root directory, local 'appium' command was not found.
return null;
}

if (parsed.name !== 'node_modules') {
continue;
}

const cmd = path.join(p, '.bin', 'appium');
try {
if (fs.lstatSync(cmd).isFile()) {
return cmd;
}
} catch (e) {
// Do nothing
}
}
}

_lowerCamelToOptionName (s) {
let ret = '--';
const A = 'A'.charCodeAt(0);
const Z = 'Z'.charCodeAt(0);
for (let idx = 0; idx < s.length; ++idx) {
const c = s.charAt(idx);
const code = s.charCodeAt(idx);
if (A <= code && code <= Z) {
ret += '-' + c.toLowerCase();
} else {
ret += c;
}
}
return ret;
}

_keyValueToCliArgs (args) {
if (Array.isArray(args)) {
// Note:
// If specified as array, this plugin assumes it as the string list of command line arguments.
return args;
}

const ret = [];
for (let key in args) {
const value = args[key];
if (typeof value === 'boolean' && !value) {
continue;
}

ret.push(this._lowerCamelToOptionName(key));

if (typeof value !== 'boolean') {
ret.push(value.toString());
}
}
return ret;
}
}

export default AppiumLauncher;

0 comments on commit 95c11ad

Please sign in to comment.