зеркало из https://github.com/microsoft/appium.git
805 строки
26 KiB
JavaScript
805 строки
26 KiB
JavaScript
"use strict";
|
|
|
|
var _ = require("underscore")
|
|
, server = require('./lib/server/main.js')
|
|
, rimraf = require('rimraf')
|
|
, path = require('path')
|
|
// , temp = require('temp')
|
|
, mkdirp = require('mkdirp')
|
|
, ncp = require('ncp')
|
|
// , difflib = require('difflib')
|
|
// , prompt = require('prompt')
|
|
, exec = require('child_process').exec
|
|
, spawn = require('win-spawn')
|
|
, parser = require('./lib/server/parser.js')
|
|
, namp = require('namp')
|
|
, parseXmlString = require('xml2js').parseString
|
|
, appiumVer = require('./package.json').version
|
|
, fs = require('fs')
|
|
, xcode = require('./lib/future.js').xcode
|
|
, isWindows = require('appium-support').system.isWindows()
|
|
, MAX_BUFFER_SIZE = 524288
|
|
, SELENDROID_MAX_BUFFER_SIZE = 4 * MAX_BUFFER_SIZE;
|
|
|
|
module.exports.startAppium = function (appName, verbose, readyCb, doneCb) {
|
|
var app;
|
|
if (appName) {
|
|
app = (fs.existsSync(appName)) ? appName:
|
|
path.resolve(__dirname, "sample-code", "apps", appName, "build", "Release-iphonesimulator", appName + ".app");
|
|
} else {
|
|
app = null;
|
|
}
|
|
return server.run({
|
|
app: app
|
|
, udid: null
|
|
, quiet: !verbose
|
|
, port: 4723
|
|
, nativeInstrumentsLib: false
|
|
, fullReset: false
|
|
, noReset: false
|
|
, launch: app ? true : false
|
|
, log: path.resolve(__dirname, "appium.log")
|
|
, address: '127.0.0.1'
|
|
, androidDeviceReadyTimeout: 5
|
|
, nodeconfig: null
|
|
, robotPort: -1
|
|
, robotAddresss: '0.0.0.0'
|
|
, keepArtifacts: false
|
|
, ipa: null
|
|
, avd: null
|
|
}
|
|
, readyCb
|
|
, doneCb
|
|
);
|
|
};
|
|
|
|
var execWithOutput = function (cmd, cb) {
|
|
exec(cmd, function (err, stdout, stderr) {
|
|
if (err) {
|
|
console.error("Command failed");
|
|
console.error("stdout:");
|
|
console.error(stdout);
|
|
console.error("stderr:");
|
|
console.error(stderr);
|
|
}
|
|
cb(err, stdout, stderr);
|
|
});
|
|
};
|
|
|
|
module.exports.getSampleCode = function (grunt, hardcore, cb) {
|
|
var submodulesDir = path.resolve(__dirname, "submodules");
|
|
var sampleCodeGit = path.resolve(submodulesDir, "sample-code");
|
|
var sampleCodeDir = path.resolve(__dirname, "sample-code");
|
|
var sampleCodeExists = fs.existsSync(sampleCodeDir);
|
|
var updateCmd = "git submodule update --init \"" + sampleCodeGit + "\"";
|
|
console.log("Cloning/updating Appium sample-code submodule");
|
|
execWithOutput(updateCmd, function (err, stdout, stderr) {
|
|
if (err) return cb(err);
|
|
var updated = false;
|
|
if (stdout + stderr !== "") {
|
|
// there were submodule updates
|
|
console.log("There were updates to the submodule");
|
|
updated = true;
|
|
}
|
|
if (hardcore || updated) {
|
|
console.log("Removing old sample-code");
|
|
console.log("Please remember to rebuild test apps");
|
|
rimraf.sync(sampleCodeDir);
|
|
}
|
|
if (hardcore || updated || !sampleCodeExists) {
|
|
console.log("Copying sample-code out for use");
|
|
ncp(path.resolve(sampleCodeGit, "sample-code"), sampleCodeDir, function (err) {
|
|
if (err) return cb(err);
|
|
console.log("Test apps are ready for building");
|
|
cb();
|
|
});
|
|
} else {
|
|
console.log("Sample code was not updated, doing nothing");
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports.runTestsWithServer = function (grunt, appName, testType, deviceType, verbose, cb) {
|
|
if (typeof verbose === "undefined") {
|
|
verbose = false;
|
|
}
|
|
var exitCode = null;
|
|
var appServer = module.exports.startAppium(appName, verbose, function () {
|
|
module.exports.runMochaTests(grunt, appName, testType, deviceType, function (code) {
|
|
appServer.close();
|
|
exitCode = code;
|
|
});
|
|
}, function () {
|
|
console.log("Appium server exited");
|
|
cb(exitCode === 0);
|
|
});
|
|
};
|
|
|
|
module.exports.runMochaTests = function (grunt, appName, testType, deviceType, cb) {
|
|
|
|
// load the options if they are specified
|
|
var options = grunt.config(['mochaTestConfig', testType, 'options']);
|
|
if (typeof options !== 'object') {
|
|
options = grunt.config(['mochaTestConfig', 'options']);
|
|
}
|
|
if (typeof options.timeout === "undefined") {
|
|
options.timeout = 60000;
|
|
}
|
|
if (typeof options.reporter === "undefined") {
|
|
options.reporter = "tap";
|
|
}
|
|
var args = ['-t', options.timeout, '-R', options.reporter, '--colors'];
|
|
var mochaFiles = [];
|
|
var fileConfig = grunt.config(['mochaTestWithServer']);
|
|
var configAppDevice, nameOk, deviceOk, configAppTests;
|
|
_.each(fileConfig, function (config, configAppName) {
|
|
configAppDevice = config[0];
|
|
configAppTests = config[1];
|
|
nameOk = !appName || appName === configAppName;
|
|
deviceOk = !deviceType || deviceType === configAppDevice;
|
|
if (nameOk && deviceOk) {
|
|
_.each(configAppTests, function (testFiles, testKey) {
|
|
if (testType === "*" || testType === testKey) {
|
|
_.each(testFiles, function (file) {
|
|
_.each(grunt.file.expand(file), function (file) {
|
|
mochaFiles.push(file);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
var exitCodes = [];
|
|
var runMochaProc = function () {
|
|
var file = mochaFiles.shift();
|
|
if (typeof file !== "undefined") {
|
|
var mochaProc = spawn('mocha', args.concat(file), {cwd: __dirname});
|
|
mochaProc.on("error", function () {
|
|
grunt.fatal("Unable to spawn mocha process: mocha not installed?");
|
|
});
|
|
mochaProc.stdout.setEncoding('utf8');
|
|
mochaProc.stderr.setEncoding('utf8');
|
|
mochaProc.stdout.on('data', function (data) {
|
|
grunt.log.write(data);
|
|
});
|
|
mochaProc.stderr.on('data', function (data) {
|
|
grunt.log.write(data);
|
|
});
|
|
mochaProc.on('exit', function (code) {
|
|
exitCodes.push(code);
|
|
runMochaProc();
|
|
});
|
|
} else {
|
|
cb(_.max(exitCodes));
|
|
}
|
|
};
|
|
runMochaProc();
|
|
};
|
|
|
|
module.exports.tail = function (grunt, filename, cb) {
|
|
var proc = spawn('tail', ['-f', filename]);
|
|
proc.on("error", function (err) {
|
|
grunt.fatal("Unable to spawn \"tail\": " + err.message);
|
|
});
|
|
proc.stdout.setEncoding('utf8');
|
|
proc.stdout.on('data', function (data) {
|
|
grunt.log.write(data);
|
|
});
|
|
proc.on('exit', function (code) {
|
|
cb(code);
|
|
});
|
|
};
|
|
|
|
module.exports.setDeviceConfigVer = function (grunt, device, cb) {
|
|
var value = {version: appiumVer};
|
|
exports.writeConfigKey(grunt, device, value, cb);
|
|
};
|
|
|
|
module.exports.writeConfigKey = function (grunt, key, value, cb) {
|
|
var configPath = path.resolve(__dirname, ".appiumconfig.json");
|
|
fs.readFile(configPath, function (err, data) {
|
|
var writeConfig = function (config) {
|
|
config[key] = value;
|
|
grunt.log.write("\n");
|
|
grunt.log.write(JSON.stringify(config));
|
|
fs.writeFile(configPath, JSON.stringify(config), cb);
|
|
};
|
|
if (err) {
|
|
grunt.log.write("Config file doesn't exist, creating it");
|
|
var config = {};
|
|
writeConfig(config);
|
|
} else {
|
|
grunt.log.write("Config file exists, updating it");
|
|
writeConfig(JSON.parse(data.toString('utf8')));
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports.setGitRev = function (grunt, rev, cb) {
|
|
exports.writeConfigKey(grunt, "git-sha", rev, cb);
|
|
};
|
|
|
|
module.exports.setBuildTime = function (grunt, cb) {
|
|
var time = new Date();
|
|
exports.writeConfigKey(grunt, "built", time.toISOString(), cb);
|
|
};
|
|
|
|
var auth_enableDevTools = function (grunt, cb) {
|
|
grunt.log.writeln("Enabling DevToolsSecurity");
|
|
exec('DevToolsSecurity --enable', function (err) {
|
|
if (err) grunt.fatal(err);
|
|
cb();
|
|
});
|
|
};
|
|
|
|
var auth_updateSecurityDb = function (grunt, insecure, cb) {
|
|
grunt.log.writeln("Updating security db for " + (insecure ? "insecure" :
|
|
"developer") + " access");
|
|
var cmd = "security authorizationdb write system.privilege.taskport " +
|
|
(insecure ? "allow" : "is-developer");
|
|
exec(cmd, function (err) {
|
|
if (err) grunt.fatal(err);
|
|
cb();
|
|
});
|
|
};
|
|
|
|
var auth_chmodApps = function (grunt, cb) {
|
|
grunt.log.writeln("Granting access to built-in simulator apps");
|
|
var user;
|
|
if (!process.env.HOME) {
|
|
grunt.fatal(new Error("Could not determine your $HOME"));
|
|
} else {
|
|
user = /\/([^\/]+)$/.exec(process.env.HOME)[1];
|
|
}
|
|
xcode.getPath(function (err, xcodeDir) {
|
|
if (err) return cb(err);
|
|
var glob = path.resolve(xcodeDir, "Platforms/iPhoneSimulator.platform/" +
|
|
"Developer/SDKs/iPhoneSimulator*.sdk/Applications");
|
|
glob += " ";
|
|
glob += path.resolve("/Library/Developer/CoreSimulator/" +
|
|
"Profiles/Runtimes/iOS\\ *.simruntime/" +
|
|
"Contents/Resources/RuntimeRoot/Applications/");
|
|
var cmd = "chown -R " + user + ": " + glob;
|
|
exec(cmd, function (err) {
|
|
if (err) {
|
|
grunt.log.writeln("Encountered an issue chmodding iOS sim app dirs. " +
|
|
"This may be because they don't exist on your " +
|
|
"system, which is not necessarily a problem. The " +
|
|
"error was: " + err.message);
|
|
}
|
|
cb();
|
|
});
|
|
});
|
|
};
|
|
|
|
module.exports.authorize = function (grunt, insecure, cb) {
|
|
auth_enableDevTools(grunt, function () {
|
|
auth_updateSecurityDb(grunt, insecure, function () {
|
|
auth_chmodApps(grunt, cb);
|
|
});
|
|
});
|
|
};
|
|
|
|
module.exports.build = function (appRoot, cb, sdk, xcconfig) {
|
|
var next = function () {
|
|
var cmd = 'xcodebuild -sdk ' + sdk + ' clean';
|
|
console.log('Using sdk: ' + sdk + '...');
|
|
console.log("Cleaning build...");
|
|
var xcode = exec(cmd, {cwd: appRoot, maxBuffer: MAX_BUFFER_SIZE}, function (err, stdout, stderr) {
|
|
if (err) {
|
|
console.log("Failed cleaning app, maybe it doesn't exist?");
|
|
return cb(stdout + "\n" + stderr);
|
|
}
|
|
console.log("Building app...");
|
|
var args = ['-sdk', sdk];
|
|
if (typeof xcconfig !== "undefined") {
|
|
args = args.concat(['-xcconfig', xcconfig]);
|
|
}
|
|
xcode = spawn('xcodebuild', args, {
|
|
cwd: appRoot
|
|
});
|
|
xcode.on("error", function (err) {
|
|
cb(new Error("Failed spawning xcodebuild: " + err.message));
|
|
});
|
|
var output = '';
|
|
var collect = function (data) { output += data; };
|
|
xcode.stdout.on('data', collect);
|
|
xcode.stderr.on('data', collect);
|
|
xcode.on('exit', function (code) {
|
|
if (code === 0) {
|
|
cb(null);
|
|
} else {
|
|
console.log("Failed building app, maybe it doesn't exist?");
|
|
cb(output);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
if (typeof sdk === "undefined") {
|
|
xcode.getVersion(function (err, version) {
|
|
if (err) return cb(err);
|
|
var sdkVersion = version[0] === "5" ? "7.0" : "6.1";
|
|
sdk = 'iphonesimulator' + sdkVersion;
|
|
next();
|
|
});
|
|
} else {
|
|
next();
|
|
}
|
|
};
|
|
|
|
module.exports.buildApp = function (appDir, cb, sdk) {
|
|
var appRoot = path.resolve(__dirname, "sample-code", "apps", appDir);
|
|
module.exports.build(appRoot, function (err) {
|
|
if (err !== null) {
|
|
console.log(err);
|
|
cb(false);
|
|
} else {
|
|
cb(true);
|
|
}
|
|
}, sdk);
|
|
};
|
|
|
|
module.exports.signApp = function (appName, certName, cb) {
|
|
var appPath = path.resolve(__dirname, "sample-code", "apps", appName,
|
|
"build", "Release-iphonesimulator");
|
|
exec("codesign -f -s \"" + certName + "\" -v " + appName + ".app", {cwd: appPath, maxBuffer: MAX_BUFFER_SIZE}, function (err, stdout, stderr) {
|
|
console.log(stdout);
|
|
console.log(stderr);
|
|
if (err) {
|
|
cb(false);
|
|
} else {
|
|
cb(true);
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports.buildSafariLauncherApp = function (cb, sdk, xcconfig) {
|
|
var appRoot = path.resolve(__dirname, "submodules", "SafariLauncher");
|
|
module.exports.build(appRoot, function (err) {
|
|
if (err !== null) {
|
|
console.log(err);
|
|
cb(false);
|
|
} else {
|
|
cb(true);
|
|
}
|
|
}, sdk, xcconfig);
|
|
};
|
|
|
|
|
|
var setupAndroidProj = function (grunt, projPath, args, cb) {
|
|
if (!process.env.ANDROID_HOME) {
|
|
grunt.fatal("Could not find Android SDK, make sure to export ANDROID_HOME");
|
|
}
|
|
var tool = "android";
|
|
if (isWindows) {
|
|
tool = "android.bat";
|
|
}
|
|
var cmd = path.resolve(process.env.ANDROID_HOME, "tools", tool);
|
|
if (!fs.existsSync(cmd)) {
|
|
grunt.fatal("The `android` command was not found at \"" + cmd + "\", are you sure ANDROID_HOME is set properly?");
|
|
}
|
|
var proc = spawn(cmd, args, {cwd: projPath});
|
|
proc.on("error", function (err) {
|
|
grunt.fatal("Unable to spawn android: " + err.message);
|
|
});
|
|
proc.stdout.setEncoding('utf8');
|
|
proc.stderr.setEncoding('utf8');
|
|
proc.stdout.on('data', function (data) {
|
|
grunt.log.write(data);
|
|
});
|
|
proc.stderr.on('data', function (data) {
|
|
grunt.log.write(data);
|
|
});
|
|
proc.on('exit', function (code) {
|
|
cb(code === 0 ? null : new Error("Setup cmd " + cmd + " failed with code " + code));
|
|
});
|
|
};
|
|
|
|
module.exports.setupAndroidBootstrap = function (grunt, cb) {
|
|
var projPath = path.resolve(__dirname, "lib", "devices", "android",
|
|
"bootstrap");
|
|
var args = ["create", "uitest-project", "-n", "AppiumBootstrap", "-t",
|
|
"android-19", "-p", "."];
|
|
// TODO: possibly check output of `android list target` to make sure api level 19 is available?
|
|
setupAndroidProj(grunt, projPath, args, cb);
|
|
};
|
|
|
|
module.exports.setupAndroidApp = function (grunt, appName, cb) {
|
|
var appPath = path.resolve(__dirname, "sample-code", "apps", appName);
|
|
var args = ["update", "project", "--subprojects", "-t", "android-19", "-p", ".", "-n", appName];
|
|
setupAndroidProj(grunt, appPath, args, cb);
|
|
};
|
|
|
|
var buildAndroidProj = function (grunt, projPath, target, cb) {
|
|
var cmdName = 'ant';
|
|
if (!fs.existsSync(path.resolve(projPath, "build.xml")) &&
|
|
fs.existsSync(path.resolve(projPath, "pom.xml"))) {
|
|
cmdName = 'mvn';
|
|
}
|
|
var whichCmd = 'which ';
|
|
if (isWindows) {
|
|
whichCmd = 'where ';
|
|
}
|
|
exec(whichCmd + cmdName, { maxBuffer: MAX_BUFFER_SIZE }, function (err, stdout) {
|
|
if (err) {
|
|
grunt.fatal("Error finding " + cmdName + " binary, is it on your path?");
|
|
} else {
|
|
if (stdout) {
|
|
var cmd = stdout.split('\r\n')[0].trim();
|
|
if (isWindows && cmdName === 'ant') {
|
|
cmd = cmd + '.bat';
|
|
}
|
|
grunt.log.write("Using " + cmdName + " found at " + cmd + "\n");
|
|
var proc = spawn(cmd, [target], {cwd: projPath});
|
|
proc.on("error", function () {
|
|
grunt.fatal("Unable to spawn \"" + cmdName + "\"");
|
|
});
|
|
proc.stdout.setEncoding('utf8');
|
|
proc.stderr.setEncoding('utf8');
|
|
proc.stdout.on('data', function (data) {
|
|
grunt.log.write(data);
|
|
});
|
|
proc.stderr.on('data', function (data) {
|
|
grunt.log.write(data);
|
|
});
|
|
proc.on('exit', function (code) {
|
|
cb(code ? new Error("Building project exited with " + code) : null);
|
|
});
|
|
} else {
|
|
grunt.fatal("Could not find " + cmdName + " installed; please make sure it's on PATH");
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports.buildAndroidBootstrap = function (grunt, cb) {
|
|
var projPath = path.resolve(__dirname, "lib", "devices", "android",
|
|
"bootstrap");
|
|
var binSrc = path.resolve(projPath, "bin", "AppiumBootstrap.jar");
|
|
var binDestDir = path.resolve(__dirname, "build", "android_bootstrap");
|
|
var binDest = path.resolve(binDestDir, "AppiumBootstrap.jar");
|
|
buildAndroidProj(grunt, projPath, "build", function (err) {
|
|
if (err) {
|
|
console.log("Could not build android bootstrap");
|
|
return cb(err);
|
|
}
|
|
mkdirp(binDestDir, function (err) {
|
|
if (err) {
|
|
console.log("Could not mkdirp " + binDestDir);
|
|
return cb(err);
|
|
}
|
|
rimraf(binDest, function (err) {
|
|
if (err) {
|
|
console.log("Could not delete old " + binDest);
|
|
return cb(err);
|
|
}
|
|
ncp(binSrc, binDest, function (err) {
|
|
if (err) {
|
|
console.log("Could not copy " + binSrc + " to " + binDest);
|
|
return cb(err);
|
|
}
|
|
cb();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
var fixSelendroidAndroidManifest = function (dstManifest, cb) {
|
|
console.log("Modifying manifest for no icons");
|
|
fs.readFile(dstManifest, function (err, data) {
|
|
if (err) {
|
|
console.error("Could not open new manifest");
|
|
return cb(err);
|
|
}
|
|
data = data.toString('utf8');
|
|
console.log(data);
|
|
var iconRe = /application[\s\S]+android:icon="[^"]+"/;
|
|
data = data.replace(iconRe, "application");
|
|
fs.writeFile(dstManifest, data, function (err) {
|
|
if (err) {
|
|
console.error("Could not write modified manifest");
|
|
return cb(err);
|
|
}
|
|
cb(null);
|
|
});
|
|
});
|
|
};
|
|
module.exports.fixSelendroidAndroidManifest = fixSelendroidAndroidManifest;
|
|
|
|
|
|
module.exports.buildSelendroidServer = function (cb) {
|
|
getSelendroidVersion(function (err, version) {
|
|
if (err) return cb(err);
|
|
console.log("Installing Cordova");
|
|
var cordovaDir = path.resolve(__dirname, "submodules", "selendroid",
|
|
"third-party", "cordova-3.7.0");
|
|
var cmd = "mvn install:install-file -Dfile=classes.jar -DgroupId=org.apache.cordova " +
|
|
"-DartifactId=cordova -Dversion=3.7.0 -Dclassifier=android -Dpackaging=jar";
|
|
exec(cmd, {cwd: cordovaDir}, function (err, stdout, stderr) {
|
|
if (err) {
|
|
console.error("Unable to install Cordova. Stdout was: ");
|
|
console.error(stdout);
|
|
console.error(stderr);
|
|
return cb(err);
|
|
}
|
|
});
|
|
console.log("Building selendroid server");
|
|
var buildDir = path.resolve(__dirname, "submodules", "selendroid");
|
|
var target = path.resolve(buildDir, "selendroid-server", "target",
|
|
"selendroid-server-" + version + ".apk");
|
|
var destDir = path.resolve(__dirname, "build", "selendroid");
|
|
var destBin = path.resolve(destDir, "selendroid.apk");
|
|
var srcManifest = path.resolve(__dirname, "submodules", "selendroid",
|
|
"selendroid-server", "AndroidManifest.xml");
|
|
var dstManifest = path.resolve(destDir, "AndroidManifest.xml");
|
|
cmd = "mvn clean package -DskipTests=true";
|
|
exec(cmd, {cwd: buildDir, maxBuffer: SELENDROID_MAX_BUFFER_SIZE}, function (err, stdout, stderr) {
|
|
if (err) {
|
|
console.error("Unable to build selendroid server. Stdout was: ");
|
|
console.error(stdout);
|
|
console.error(stderr);
|
|
return cb(err);
|
|
}
|
|
console.log("Making sure target exists");
|
|
fs.stat(target, function (err) {
|
|
if (err) {
|
|
console.error("Selendroid doesn't exist! Not sure what to do.");
|
|
return cb(err);
|
|
}
|
|
console.log("Selendroid server built successfully, copying to build/selendroid");
|
|
rimraf(destDir, function (err) {
|
|
if (err) {
|
|
console.error("Could not remove " + destDir);
|
|
return cb(err);
|
|
}
|
|
mkdirp(destDir, function (err) {
|
|
if (err) {
|
|
console.error("Could not create " + destDir);
|
|
return cb(err);
|
|
}
|
|
ncp(target, destBin, function (err) {
|
|
if (err) {
|
|
console.error("Could not copy " + target + " to " + destBin);
|
|
return cb(err);
|
|
}
|
|
console.log("Copying selendroid manifest as well");
|
|
ncp(srcManifest, dstManifest, function (err) {
|
|
if (err) {
|
|
console.error("Could not copy manifest");
|
|
return cb(err);
|
|
}
|
|
fixSelendroidAndroidManifest(dstManifest, cb);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
var getSelendroidVersion = function (cb) {
|
|
console.log("Getting Selendroid version");
|
|
var pomXml = path.resolve(__dirname, "submodules", "selendroid",
|
|
"selendroid-server", "pom.xml");
|
|
fs.readFile(pomXml, function (err, xmlData) {
|
|
if (err) {
|
|
console.error("Could not find selendroid's pom.xml at");
|
|
return cb(err);
|
|
}
|
|
parseXmlString(xmlData.toString('utf8'), function (err, res) {
|
|
if (err) {
|
|
console.error("Error parsing selendroid's pom.xml");
|
|
return cb(err);
|
|
}
|
|
var version = res.project.parent[0].version[0];
|
|
if (typeof version === "string") {
|
|
console.log("Selendroid version is " + version);
|
|
cb(null, version);
|
|
} else {
|
|
cb(new Error("Version " + version + " was not valid"));
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
module.exports.buildAndroidApp = function (grunt, appName, cb) {
|
|
var appPath = path.resolve(__dirname, "sample-code", "apps", appName);
|
|
buildAndroidProj(grunt, appPath, "clean", function (err) {
|
|
if (err) return cb(err);
|
|
buildAndroidProj(grunt, appPath, "debug", cb);
|
|
});
|
|
};
|
|
|
|
module.exports.buildSelendroidAndroidApp = function (grunt, appName, cb) {
|
|
var appPath = path.resolve(__dirname, "sample-code", "apps" + appName);
|
|
buildAndroidProj(grunt, appPath, "package", cb);
|
|
};
|
|
|
|
module.exports.installAndroidApp = function (grunt, appName, cb) {
|
|
var pkgMap = {'ApiDemos': 'com.example.android.apis'};
|
|
if (!_.has(pkgMap, appName)) {
|
|
var msg = "We don't know about appName " + appName + ", please edit " +
|
|
"grunt-helpers.js:installAndroidApp() to add it and its " +
|
|
"package identifier";
|
|
grunt.fatal(new Error(msg));
|
|
}
|
|
|
|
var appPath = path.resolve(__dirname, "sample-code", "apps", appName,
|
|
"bin/" + appName + "-debug.apk");
|
|
exec("adb uninstall " + pkgMap[appName], { maxBuffer: MAX_BUFFER_SIZE }, function (err, stdout) {
|
|
if (err) return grunt.fatal(err);
|
|
grunt.log.write(stdout);
|
|
exec("adb install -r " + appPath, { maxBuffer: MAX_BUFFER_SIZE }, function (err, stdout) {
|
|
if (err) return grunt.fatal(err);
|
|
grunt.log.write(stdout);
|
|
cb();
|
|
});
|
|
});
|
|
};
|
|
|
|
module.exports.generateServerDocs = function (grunt, cb) {
|
|
var p = parser();
|
|
var docFile = path.resolve(__dirname, "docs/en/writing-running-appium/server-args.md");
|
|
var md = "# Appium server arguments\n\n";
|
|
md += "Usage: `node . [flags]`\n\n";
|
|
md += "## Server flags\n";
|
|
md += "All flags are optional, but some are required in conjunction with " +
|
|
"certain others.\n\n";
|
|
md += "\n\n<expand_table>\n\n";
|
|
md += "|Flag|Default|Description|Example|\n";
|
|
md += "|----|-------|-----------|-------|\n";
|
|
_.each(p.rawArgs, function (arg) {
|
|
var argNames = arg[0];
|
|
var exampleArg = typeof arg[0][1] === "undefined" ? arg[0][0] : arg[0][1];
|
|
var argOpts = arg[1];
|
|
md += "|`" + argNames.join("`, `") + "`";
|
|
md += "|" + ((typeof argOpts.defaultValue === "undefined") ? "" : argOpts.defaultValue);
|
|
md += "|" + argOpts.help;
|
|
md += "|" + ((typeof argOpts.example === "undefined") ? "" : "`" + exampleArg + " " + argOpts.example + "`");
|
|
md += "|\n";
|
|
});
|
|
fs.writeFile(docFile, md, function (err) {
|
|
if (err) {
|
|
console.log(err.stack);
|
|
grunt.fatal(err);
|
|
} else {
|
|
grunt.log.write("New docs written! Don't forget to commit and push");
|
|
cb();
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports.generateAppiumIo = function (grunt, cb) {
|
|
getAppiumIoFiles(function (err, template, readme) {
|
|
if (err) {
|
|
return grunt.fatal(err);
|
|
}
|
|
var readmeLex = namp.lexer(readme)
|
|
, headers = getMarkdownHeaders(readmeLex)
|
|
, sidebarHtml = generateSidebarHtml(headers)
|
|
, warning = "<!-- THIS FILE IS AUTOMATICALLY GENERATED DO NOT EDIT -->\n"
|
|
, bodyHtml = generateBodyHtml(readmeLex, headers)
|
|
, submod = path.resolve(__dirname, "submodules", "appium.io")
|
|
, outfile = submod + "/getting-started.html";
|
|
|
|
var newDoc = template.replace("{{ SIDENAV }}", sidebarHtml)
|
|
.replace("{{ README_SECTIONS }}", bodyHtml)
|
|
.replace("{{ WARNING }}", warning);
|
|
fs.writeFile(outfile, newDoc, function (err) {
|
|
if (err) {
|
|
grunt.fatal(err);
|
|
} else {
|
|
grunt.log.write("Pushing changes to appium.io...");
|
|
var cmd = 'git commit -am "updating getting-started via grunt" && ' +
|
|
'git pull --rebase origin master && ' +
|
|
'git push origin master && ' +
|
|
'git checkout gh-pages && ' +
|
|
'git pull origin gh-pages && ' +
|
|
'git merge master && ' +
|
|
'git push origin gh-pages && ' +
|
|
'git checkout master';
|
|
exec(cmd, {cwd: submod, maxBuffer: MAX_BUFFER_SIZE}, function (err, stdout, stderr) {
|
|
if (err) {
|
|
console.log(stdout);
|
|
console.log(stderr);
|
|
grunt.fatal(err);
|
|
} else {
|
|
grunt.log.write("success!");
|
|
cb();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
var getAppiumIoFiles = function (cb) {
|
|
var templateFile = path.resolve(__dirname, "submodules", "appium.io", "getting-started-template.html")
|
|
, readmeFile = path.resolve(__dirname, "README.md");
|
|
|
|
fs.readFile(templateFile, function (err, templateData) {
|
|
if (err) {
|
|
cb(err);
|
|
} else {
|
|
fs.readFile(readmeFile, function (err, readmeData) {
|
|
if (err) cb(err); else cb(null, templateData.toString(), readmeData.toString());
|
|
});
|
|
}
|
|
});
|
|
|
|
};
|
|
|
|
var getMarkdownHeaders = function (mdTree) {
|
|
var headers = {};
|
|
_.each(mdTree, function (mdObj) {
|
|
if (mdObjIsHeader(mdObj)) {
|
|
headers[mdObj.text] = sanitizeMdHeader(mdObj.text);
|
|
}
|
|
});
|
|
return headers;
|
|
};
|
|
|
|
var mdObjIsHeader = function (mdObj) {
|
|
return mdObj.depth === 2 && mdObj.type === 'heading';
|
|
};
|
|
|
|
var sanitizeMdHeader = function (header) {
|
|
var re = new RegExp(/[^a-zA-Z0-9]/g);
|
|
return header.replace(re, "-")
|
|
.replace(/-$/, "")
|
|
.replace(/-+/g, "-")
|
|
.toLowerCase();
|
|
};
|
|
|
|
var generateSidebarHtml = function (headers) {
|
|
var html = '';
|
|
_.each(headers, function (link, header) {
|
|
header = namp(header).html;
|
|
header = header.replace(/<[^>]+>/ig, "");
|
|
html += '<li><a href="#' + link + '"><i class="icon-chevron-right"></i> ' +
|
|
header + '</a></li>\n';
|
|
});
|
|
return html;
|
|
};
|
|
|
|
var generateBodyHtml = function (readmeMd, headers) {
|
|
var html = ''
|
|
, inBetweens = []
|
|
, inBetweenHtml = ''
|
|
, inSection = false;
|
|
var addInBetweenHtml = function () {
|
|
if (inBetweens.length) {
|
|
inBetweenHtml = namp.parser(inBetweens)[0];
|
|
html += inBetweenHtml;
|
|
inBetweens = [];
|
|
}
|
|
};
|
|
|
|
for (var i = 1; i < readmeMd.length; i++) {
|
|
var mdObj = readmeMd[i];
|
|
if (mdObjIsHeader(mdObj)) {
|
|
addInBetweenHtml();
|
|
var headerHtml = namp.parser([mdObj])[0];
|
|
headerHtml = headerHtml.replace(/<.?p>/ig, '');
|
|
if (inSection) {
|
|
html += '</section>\n';
|
|
}
|
|
html += '<section id="' + headers[mdObj.text] + '">\n';
|
|
html += '<h2>\n<a href="#' + headers[mdObj.text] + '" class="anchor" ' +
|
|
'name="requirements">\n<span class="mini-icon mini-icon-link">' +
|
|
'</span>\n</a>\n' + headerHtml + '\n</h2>\n\n';
|
|
inSection = true;
|
|
} else if (inSection) {
|
|
inBetweens.push(mdObj);
|
|
}
|
|
if (i === readmeMd.length - 1) {
|
|
addInBetweenHtml();
|
|
}
|
|
}
|
|
html += '</section>';
|
|
return html;
|
|
};
|