Compare commits

...
Author SHA1 Message Date
Daren Desjardins 209e378ccf Reset Push Notification privacy setting
When performing a clean of the simulator, the push notification privacy setting is now reset as well
2014-10-04 11:31:12 -07:00
Jonathan Lipps 9662914c66 rimraf.sync fixup 2014-10-04 11:31:12 -07:00
Sergey Belov 44e2ed5262 Increase interval to capture webview from 6 to 10 sec 2014-10-04 11:31:12 -07:00
Jonathan Lipps 8e9cf69fac fix issue with starting real device in ios8 (fix #3665) 2014-10-04 11:31:12 -07:00
Sergey Belov 9ee9664f20 Move builtin Safari app to a new name containing SDK version 2014-10-04 11:31:12 -07:00
Jonathan Lipps ec97159c29 add --isolate-sim-device which enables appium to delete all other sims
as a workaround to xcode 6's crazy bugs
2014-10-04 11:31:12 -07:00
Jonathan Lipps e7eb407fdd selectively clean out sim preferences on each run so it doesn't launch with the wrong sim 2014-10-04 11:31:12 -07:00
Jonathan Lipps 6941558bf7 bump for beta version 2014-10-04 11:31:12 -07:00
Jonathan Lipps db501a5ff6 add some xcode-6 specific docs 2014-10-04 11:31:12 -07:00
Jonathan Lipps 78dcd85935 handle locale-setting responsibilities in-house instead of using deprecated ios-sim-locale (fix #3595) 2014-10-04 11:31:12 -07:00
Jonathan Lipps 24e8c965f1 ios8-related testsuite updates + fixes 2014-10-04 11:31:12 -07:00
Jonathan Lipps 373379f2e4 refactor jumbled methods into new simulator.js and xcode.js
and add support for cleaning safari files on ios8
2014-10-04 11:31:11 -07:00
Jonathan Lipps fd611f7428 add ability for testsuite to determine ios8 sim udid
and add support for pull/pushFile in ios8
2014-10-04 11:31:11 -07:00
Jonathan Lipps 6ee51a8bd8 refactor ios setLocale() and quarantine localization specs for ios8 due to #3595 2014-10-04 11:31:11 -07:00
Jonathan Lipps 76adbe365d update ios syslog capture for ios8 2014-10-04 11:31:11 -07:00
Jonathan Lipps 2366944e69 update ios settings module to handle ios8 paths 2014-10-04 11:31:11 -07:00
Jonathan Lipps f9646dd185 refactor setDevice logic and capture ios8 device udid 2014-10-04 11:31:11 -07:00
Jonathan Lipps ca9109f1bd ios8 safari / new remote debugger protocol support 2014-10-04 11:31:11 -07:00
Jonathan Lipps 5a9538fb98 fix screenshot logic to use --tmp dir 2014-10-04 11:31:11 -07:00
Jonathan Lipps 67affb6d8d add support for starting an app via bundleId on the ios sim 2014-10-04 11:31:11 -07:00
Jonathan Lipps 208be83202 basic ios8 support 2014-10-04 11:31:11 -07:00
84 changed files with 1847 additions and 1257 deletions
Binary file not shown.
+9 -1
View File
@@ -5,6 +5,7 @@ ios_only=false
ios6_only=false
ios7_only=false
ios71_only=false
ios8_only=false
android_only=false
android_chrome=false
selendroid_only=false
@@ -43,6 +44,9 @@ for arg in "$@"; do
elif [ "$arg" = "--ios71" ]; then
ios71_only=true
all_tests=false
elif [ "$arg" = "--ios8" ]; then
ios8_only=true
all_tests=false
elif [ "$arg" = "--no-xcode-switch" ]; then
xcode_switch=false
elif [ "$arg" = "--real-device" ]; then
@@ -84,7 +88,11 @@ if $ios7_only || $all_tests; then
fi
if $ios71_only || $all_tests; then
run_ios_tests "7.1" "ios71" "@skip-ios71|@skip-ios7|@skip-ios-all"
run_ios_tests "7.1" "ios71" "@skip-ios71|@skip-ios7|@skip-ios-all|@skip-ios7up"
fi
if $ios8_only || $all_tests; then
run_ios_tests "8.0" "ios8" "@skip-ios8|@skip-ios-all|@skip-ios7up"
fi
if $did_switch_xcode; then
+15
View File
@@ -14,6 +14,21 @@ Appium on OS X supports iOS and Android testing.
from source, simply run `sudo grunt authorize` to do the same thing. If you
are running [Appium.app](https://github.com/appium/appium-dot-app), you can
authorize iOS through the GUI.
* If you're on Xcode 6, you need to launch each simulator you intend to use
with appium in advance, and change the default to actually show the soft
keyboard if you want sendKeys to work. You can do this by clicking on any
textfield and hitting command-K until you notice the soft keyboard show up.
* If you're on Xcode 6, you have a feature in Xcode called Devices
(command-shift-2). You need to make sure that whichever deviceName you choose
to use with Appium in your capabilities, there is only one of those per sdk
version. In other words, if you send in a deviceName cap of "iPhone 5s" and
a platformVersion cap of "8.0", you need to make sure that there is exactly
one device with the name "iPhone 5s" and the 8.0 sdk in your devices list.
Otherwise, Appium won't know which one to use.
* In iOS8, devices each have their own setting which enables or disables
UIAutomation. It lives in a "Developer" view in the Settings app. You need to
verify that UIAutomaion is enabled in this view before the simulator or
device can be automated.
### Testing against multiple iOS SDKs
@@ -66,7 +66,7 @@ All flags are optional, but some are required in conjunction with certain others
|`--chromedriver-port`|9515|Port upon which ChromeDriver will run|`--chromedriver-port 9515`|
|`--chromedriver-executable`|null|ChromeDriver executable full path||
|`--use-keystore`|false|(Android-only) When set the keystore will be used to sign apks.||
|`--keystore-path`|/Users/saucelabs/.android/debug.keystore|(Android-only) Path to keystore||
|`--keystore-path`|/Users/user/.android/debug.keystore|(Android-only) Path to keystore||
|`--keystore-password`|android|(Android-only) Password to keystore||
|`--key-alias`|androiddebugkey|(Android-only) Key alias||
|`--key-password`|android|(Android-only) Key password||
@@ -75,6 +75,7 @@ All flags are optional, but some are required in conjunction with certain others
|`--command-timeout`|60|The default command timeout for the server to use for all sessions. Will still be overridden by newCommandTimeout cap||
|`--keep-keychains`|false|(iOS) Whether to keep keychains (Library/Keychains) when reset app between sessions||
|`--strict-caps`|false|Cause sessions to fail if desired caps are sent in that Appium does not recognize as valid for the selected device||
|`--isolate-sim-device`|false|Xcode 6 has a bug on some platforms where a certain simulator can only be launched without error if all other simulator devices are first deleted. This option causes Appium to delete all devices other than the one being used by Appium. Note that this is a permanent deletion, and you are responsible for using simctl or xcode to manage the categories of devices used with Appium.||
|`--tmp`|null|Absolute path to directory Appium can use to manage temporary files, like built-in iOS apps it needs to move around. On *nix/Mac defaults to /tmp, on Windows defaults to C:\Windows\Temp||
|`--trace-dir`|null|Absolute path to directory Appium use to save ios instruments traces, defaults to <tmp dir>/appium-instruments||
|`--intent-action`|android.intent.action.MAIN|(Android-only) Intent action which will be used to start activity|`--intent-action android.intent.action.MAIN`|
+3 -4
View File
@@ -17,9 +17,8 @@ var _ = require("underscore")
, appiumVer = require('./package.json').version
, fs = require('fs')
, helpers = require('./lib/helpers')
, xcode = require('./lib/devices/ios/xcode.js')
, isWindows = helpers.isWindows()
, getXcodeFolder = helpers.getXcodeFolder
, getXcodeVersion = helpers.getXcodeVersion
, MAX_BUFFER_SIZE = 524288
, SELENDROID_MAX_BUFFER_SIZE = 4 * MAX_BUFFER_SIZE;
@@ -254,7 +253,7 @@ var auth_chmodApps = function (grunt, cb) {
} else {
user = /\/([^\/]+)$/.exec(process.env.HOME)[1];
}
getXcodeFolder(function (err, xcodeDir) {
xcode.getPath(function (err, xcodeDir) {
if (err) return cb(err);
var glob = path.resolve(xcodeDir, "Platforms/iPhoneSimulator.platform/" +
"Developer/SDKs/iPhoneSimulator*.sdk/Applications");
@@ -310,7 +309,7 @@ module.exports.build = function (appRoot, cb, sdk, xcconfig) {
});
};
if (typeof sdk === "undefined") {
getXcodeVersion(function (err, version) {
xcode.getVersion(function (err, version) {
if (err) return cb(err);
var sdkVersion = version[0] === "5" ? "7.0" : "6.1";
sdk = 'iphonesimulator' + sdkVersion;
+6 -1
View File
@@ -64,12 +64,16 @@ exports.waitForCondition = function (waitMs, condFn, cb, intervalMs) {
var endAt = begunAt + waitMs;
var spin = function () {
condFn(function (condMet) {
var now = Date.now();
var waited = now - begunAt;
var args = Array.prototype.slice.call(arguments);
if (condMet) {
cb.apply(this, args.slice(1));
} else if (Date.now() < endAt) {
} else if (now < endAt) {
logger.debug("Waited for " + waited + "ms so far");
setTimeout(spin, intervalMs);
} else {
logger.debug("Condition unmet after " + waited + "ms. Timing out.");
cb.apply(this, args.slice(1));
}
}.bind(this));
@@ -78,6 +82,7 @@ exports.waitForCondition = function (waitMs, condFn, cb, intervalMs) {
};
exports.implicitWaitForCondition = function (condFn, cb) {
logger.debug("Waiting up to " + this.implicitWaitMs + "ms for condition");
var _condFn = condFn;
condFn = function () {
var args = Array.prototype.slice.call(arguments, 0);
+12 -10
View File
@@ -19,9 +19,7 @@ var uuid = require('uuid-js')
, js2xml = require("js2xmlparser2")
, xpath = require("xpath")
, XMLDom = require("xmldom")
, settings = require('./settings.js')
, IOSPerfLog = require('./ios-perf-log')
, getSimRootsWithVersion = settings.getSimRootsWithVersion
, errors = require('../../server/errors.js')
, NotImplementedError = errors.NotImplementedError
, NotYetImplementedError = errors.NotYetImplementedError;
@@ -477,7 +475,7 @@ iOSController.pushFile = function (base64Data, remotePath, cb) {
iOSController._getFullPath = function (remotePath, cb) {
var fullPath = "";
var v = (this.args.platformVersion || this.iOSSDKVersion);
var simRoots = getSimRootsWithVersion(v);
var simRoots = this.sim.getDirs();
if (simRoots.length < 1) {
return cb(new Error("Could not find simulator root for SDK version " +
v + "; have you launched this sim before?"));
@@ -488,7 +486,8 @@ iOSController._getFullPath = function (remotePath, cb) {
if (simRoots.length > 1) {
var filteredSimRoots = simRoots.filter(function (root) {
return fs.existsSync(root + "/Applications");
return fs.existsSync(root + "/Applications") || // ios7
fs.existsSync(root + "/Containers/Data/Application"); // ios8
});
if (filteredSimRoots.length > 0) {
@@ -782,6 +781,10 @@ iOSController.clickWebCoords = function (cb) {
var xRatio = realDims.w / wvDims.w;
var yRatio = realDims.h / wvDims.h;
var serviceBarHeight = 20;
if (parseFloat(this.args.platformVersion) >= 8) {
// ios8 includes the service bar height in the app
serviceBarHeight = 0;
}
coords = {
x: wvPos.x + (xRatio * coords.x)
, y: wvPos.y + yOffset + (yRatio * coords.y) - serviceBarHeight
@@ -1197,12 +1200,13 @@ iOSController.getScreenshot = function (cb) {
var guid = uuid.create();
var command = ["au.capture('screenshot", guid, "')"].join('');
var screenshotFolder = "/tmp/appium-instruments/Run 1/";
if (!fs.existsSync(screenshotFolder)) {
mkdirp.sync(screenshotFolder);
var shotFolder = path.resolve(this.args.tmpDir,
"appium-instruments/Run 1/");
if (!fs.existsSync(shotFolder)) {
mkdirp.sync(shotFolder);
}
var shotPath = [screenshotFolder, 'screenshot', guid, ".png"].join("");
var shotPath = path.resolve(shotFolder, 'screenshot' + guid + '.png');
// Retrying the whole screenshot process for three times.
async.retry(3,
function (cb) {
@@ -1846,8 +1850,6 @@ iOSController.moveTo = function (element, xoffset, yoffset, cb) {
x: res.value.x + xoffset
, y: res.value.y + yoffset
};
//console.log("moving mouse to coords:");
//console.log(coords);
if (this.isWebContext()) {
this.curWebCoords = coords;
this.useAtomsElement(element, cb, function (atomsElement) {
+48 -10
View File
@@ -22,6 +22,36 @@ iOSHybrid.closeAlertBeforeTest = function (cb) {
}.bind(this));
};
iOSHybrid.getDebuggerAppKey = function (appDict, bundleId) {
var appKey = null;
if (parseFloat(this.args.platformVersion) >= 8) {
_.each(appDict, function (data, key) {
if (data.bundleId === bundleId) {
appKey = key;
}
});
// now we need to determine if we should pick a proxy for this instead
if (appKey) {
_.each(appDict, function (data, key) {
if (data.isProxy && data.hostId === appKey) {
logger.debug("Found separate bundleId " + data.bundleId + " " +
"acting as proxy for " + bundleId + ". Going to use its " +
"app ID key of " + key + " instead");
appKey = key;
}
});
}
} else {
if (_.has(appDict, bundleId)) {
appKey = bundleId;
}
}
if (appKey === null) {
throw new Error("Couldn't get app key from app dict");
}
return appKey;
};
iOSHybrid.listWebFrames = function (cb, exitCb) {
var isDone = false;
if (!this.args.bundleId) {
@@ -43,7 +73,10 @@ iOSHybrid.listWebFrames = function (cb, exitCb) {
if (this.args.udid !== null) {
this.remote.pageArrayFromJson(cb);
} else {
this.remote.selectApp(this.args.bundleId, onDone);
if (this.remoteAppKey === null) {
return cb(new Error("remote debug app key must be set"));
}
this.remote.selectApp(this.remoteAppKey, onDone);
}
} else {
if (this.args.udid !== null) {
@@ -52,18 +85,22 @@ iOSHybrid.listWebFrames = function (cb, exitCb) {
} else {
this.remote = rd.init(exitCb);
this.remote.connect(function (appDict) {
if (!_.has(appDict, this.args.bundleId)) {
logger.error("Remote debugger did not list " + this.args.bundleId +
" among its available apps");
if (_.has(appDict, "com.apple.mobilesafari")) {
var appKey;
try {
appKey = this.getDebuggerAppKey(appDict, this.args.bundleId);
} catch (e) {
logger.warn("Remote debugger did not list " + this.args.bundleId +
" among its available apps");
try {
appKey = this.getDebuggerAppKey(appDict, "com.apple.mobilesafari");
logger.debug("Using mobile safari instead");
this.remote.selectApp("com.apple.mobilesafari", onDone);
} else {
onDone([]);
} catch (e) {
return onDone([]);
}
} else {
this.remote.selectApp(this.args.bundleId, onDone);
}
logger.debug("Using remote debugger app key: " + appKey);
this.remoteAppKey = appKey;
this.remote.selectApp(appKey, onDone);
}.bind(this), this.onPageChange.bind(this));
var loopCloseRuns = 0;
var loopClose = function () {
@@ -174,6 +211,7 @@ iOSHybrid.stopRemote = function (closeWindowBeforeDisconnecting) {
this.remote.disconnect();
}
this.curContext = null;
this.remoteAppKey = null;
this.curWebFrames = [];
this.curWebCoords = null;
this.remote = null;
+33 -6
View File
@@ -11,8 +11,11 @@ var spawn = require('win-spawn')
// Date-Utils: Polyfills for the Date object
require('date-utils');
var START_TIMEOUT = 10000;
var IosLog = function (opts) {
this.udid = opts.udid;
this.simUdid = opts.simUdid;
this.xcodeVersion = opts.xcodeVersion;
this.showLogs = opts.showLogs;
this.proc = null;
@@ -24,6 +27,7 @@ var IosLog = function (opts) {
this.logs = [];
this.logRow = "";
this.logsSinceLastRequest = [];
this.startTimer = null;
if (this.xcodeVersion === null) {
logger.warn("Xcode version passed into log capture code as null, assuming Xcode 5");
@@ -48,13 +52,25 @@ IosLog.prototype.startCapture = function (cb) {
this.proc = spawn("deviceconsole", ["-u", this.udid], {env: spawnEnv});
this.finishStartingLogCapture(cb);
} else {
if (parseInt(this.xcodeVersion.split(".")[0], 10) >= 5) {
logger.debug("Starting iOS 7.* simulator log capture");
var sim7LogsPath = path.resolve(process.env.HOME, "Library", "Logs", "iOS Simulator");
glob(sim7LogsPath + "/7.*/system.log", function (err, files) {
var ver = parseInt(this.xcodeVersion.split(".")[0], 10);
if (ver >= 5) {
var logsPath;
if (ver >= 6) {
logger.debug("Starting iOS 8.* simulator log capture");
if (_.isUndefined(this.simUdid)) {
return cb(new Error("iOS8 log capture requires a sim udid"));
}
logsPath = path.resolve(process.env.HOME, "Library", "Logs",
"CoreSimulator", this.simUdid);
} else {
logger.debug("Starting iOS 7.* simulator log capture");
logsPath = path.resolve(process.env.HOME, "Library", "Logs",
"iOS Simulator", "7.*");
}
glob(path.resolve(logsPath, "system.log"), function (err, files) {
if (err || files.length < 1) {
logger.error("Could not start log capture because no iOS 7 " +
"simulator logs could be found at " + sim7LogsPath + "/7.*. " +
logger.error("Could not start log capture because no iOS " +
"simulator logs could be found at " + logsPath + "/system.log" +
"Logging will not be functional for this run");
return cb();
} else {
@@ -82,9 +98,18 @@ IosLog.prototype.startCapture = function (cb) {
};
IosLog.prototype.finishStartingLogCapture = function (cb) {
this.startTimer = setTimeout(function () {
var msg = "Log capture did not start in a reasonable amount of time";
logger.error(msg);
if (!this.calledBack) {
this.calledBack = true;
cb(new Error(msg));
}
}, START_TIMEOUT);
this.proc.stdout.setEncoding('utf8');
this.proc.stderr.setEncoding('utf8');
this.proc.on('error', function (err) {
clearTimeout(this.startTimer);
logger.error("iOS log capture failed: " + err.message);
if (!this.calledBack) {
this.calledBack = true;
@@ -104,6 +129,7 @@ IosLog.prototype.stopCapture = function () {
};
IosLog.prototype.onStdout = function (data) {
clearTimeout(this.startTimer);
if (!this.calledBack) {
// don't store the first line of the log because it came before the sim or device was launched
this.onOutput(data);
@@ -117,6 +143,7 @@ IosLog.prototype.onStdout = function (data) {
};
IosLog.prototype.onStderr = function (data) {
clearTimeout(this.startTimer);
if (/execvp\(\)/.test(data)) {
logger.error("iOS log capture process failed to start");
if (!this.calledBack) {
+426 -391
View File
File diff suppressed because it is too large Load Diff
+27 -5
View File
@@ -59,6 +59,9 @@ RemoteDebugger.prototype.init = function (debuggerType, onDisconnect) {
, error: function (msg) {
appLogger.error("[REMOTE] " + msg);
}
, warn: function (msg) {
appLogger.warn("[REMOTE] " + msg);
}
};
};
@@ -106,8 +109,14 @@ RemoteDebugger.prototype.setConnectionKey = function (cb) {
}.bind(this), function (appDict) {
var newDict = {};
_.each(appDict, function (dict) {
newDict[dict.WIRApplicationIdentifierKey] = dict.WIRApplicationNameKey;
});
var idKey = dict.WIRApplicationIdentifierKey;
newDict[idKey] = {
name: dict.WIRApplicationNameKey,
bundleId: dict.WIRApplicationBundleIdentifierKey,
isProxy: dict.WIRIsApplicationProxyKey,
hostId: dict.WIRHostApplicationIdentifierKey
};
}.bind(this));
cb(newDict);
});
};
@@ -117,7 +126,9 @@ RemoteDebugger.prototype.selectApp = function (appIdKey, cb) {
this.appIdKey = appIdKey;
var connectToApp = messages.connectToApp(this.connId, this.appIdKey);
this.logger.debug("Selecting app");
this.send(connectToApp, function (pageDict) {
this.send(connectToApp, function (appIdKey, pageDict) {
// appIdKey comes in but we trust that it's the same here, otherwise
// we'll stall out and not call cb
cb(this.pageArrayFromDict(pageDict));
this.specialCbs['_rpc_forwardGetListing:'] = this.onPageChange.bind(this);
}.bind(this));
@@ -184,7 +195,12 @@ RemoteDebugger.prototype.checkPageIsReady = function (cb) {
}.bind(this), true);
};
RemoteDebugger.prototype.onPageChange = function (pageDict) {
RemoteDebugger.prototype.onPageChange = function (appIdKey, pageDict) {
if (this.appIdKey !== appIdKey) {
this.logger.debug("New page listing did not match the app id we " +
"are currently using, ignoring");
return;
}
this.pageChangeCb(this.pageArrayFromDict(pageDict));
};
@@ -444,8 +460,15 @@ RemoteDebugger.prototype.setHandlers = function () {
}.bind(this),
'_rpc_applicationSentListing:': function (plist) {
this.handleSpecialMessage('_rpc_forwardGetListing:',
plist.__argument.WIRApplicationIdentifierKey,
plist.__argument.WIRListingKey);
}.bind(this),
'_rpc_applicationConnected:': function (plist) {
this.logger.info("We were notified that we connected to possibly " +
"the wrong application. Ignoring for now and " +
"hoping we're going to retry looking for apps");
this.logger.debug(JSON.stringify(plist.__argument));
}.bind(this),
'_rpc_applicationSentData:': function (plist) {
var dataKey = JSON.parse(plist.__argument.WIRMessageDataKey.toString('utf8'))
, msgId = dataKey.id
@@ -457,7 +480,6 @@ RemoteDebugger.prototype.setHandlers = function () {
if (dataKey.method === "Profiler.resetProfiles") {
this.logger.debug("Device is telling us to reset profiles. Should probably " +
"do some kind of callback here");
//me.onPageChange();
} else if (dataKey.method === "Page.frameNavigated") {
if (!this.willNavigateWithoutReload && !this.pageLoading) {
this.logger.debug("Frame navigated, unloading page");
+44 -44
View File
@@ -3,13 +3,10 @@
var IOS = require('./ios.js')
, logger = require('../../server/logger.js').get('appium')
, path = require('path')
, helpers = require('../../helpers.js')
, cleanSafari = helpers.cleanSafari
, checkSafari = helpers.checkSafari
, getiOSSDKVersion = helpers.getiOSSDKVersion
, _ = require('underscore');
var NATIVE_REAL_SAFARI_MIN_VER = 7.0;
var NATIVE_REAL_SAFARI_MIN_VER = 7.0
, SAFARI_BUNDLE = 'com.apple.mobilesafari';
var Safari = function () {
this.init();
@@ -29,49 +26,35 @@ Safari.prototype.configure = function (args, caps, cb) {
"../../../build/SafariLauncher/SafariLauncher.zip");
this.configureLocalApp(cb);
} else {
this.configureSafari(cb);
if (parseFloat(this.args.platformVersion) >= 8) {
logger.debug("We're on iOS8+ so not copying mobile safari app");
this.args.bundleId = SAFARI_BUNDLE;
this.args.app = null;
} else {
// make sure args.app has something in it so we get to the right spots
// in moveBuiltInApp()
this.args.app = "safari";
}
cb();
}
};
Safari.prototype.configureSafari = function (cb) {
var safariVer = null;
if (typeof this.args.platformVersion !== "undefined" &&
this.args.platformVersion) {
safariVer = this.args.platformVersion;
}
logger.debug("Trying to use mobile safari, version " + safariVer);
var checkNext = function (attemptedApp, origApp, safariVer) {
logger.debug("Using mobile safari app at " + attemptedApp);
this.args.app = attemptedApp;
logger.debug("Cleaning mobile safari data files");
cleanSafari(safariVer, function (err) {
Safari.prototype.moveBuiltInApp = function (cb) {
if (this.args.app !== null) {
logger.debug("Trying to use mobile safari, version " +
this.args.platformVersion);
this.sim.prepareSafari(this.args.tmpDir, function (err, attemptedApp, origApp) {
if (err) {
logger.error(err.message);
cb(err);
} else {
this.args.origAppPath = origApp;
cb(null);
}
}.bind(this));
}.bind(this);
var sdkNext = function (safariVer) {
checkSafari(safariVer, this.args.tmpDir, function (err, attemptedApp, origApp) {
if (err) {
logger.error("Could not prepare mobile safari with version '" +
safariVer + "': " + err);
logger.error("Could not prepare mobile safari: " + err);
return cb(err);
}
checkNext(attemptedApp, origApp, safariVer);
});
}.bind(this);
if (safariVer === null) {
getiOSSDKVersion(function (err, safariVer) {
if (err) return cb(err);
sdkNext(safariVer);
});
logger.debug("Using mobile safari app at " + attemptedApp);
this.args.app = attemptedApp;
this.args.origAppPath = origApp;
cb();
}.bind(this));
} else {
sdkNext(safariVer);
cb();
}
};
@@ -106,7 +89,7 @@ Safari.prototype.click = function (elementId, cb) {
};
Safari.prototype.setBundleId = function (cb) {
this.args.bundleId = "com.apple.mobilesafari";
this.args.bundleId = SAFARI_BUNDLE;
cb();
};
@@ -114,7 +97,7 @@ Safari.prototype._setInitialOrientation = IOS.prototype.setInitialOrientation;
Safari.prototype.setInitialOrientation = function (cb) {
if (this.shouldIgnoreInstrumentsExit()) {
logger.debug("Not setting initial orientation because we're on " +
"SafariLauncher");
"SafariLauncher");
return cb();
}
this._setInitialOrientation(cb);
@@ -124,7 +107,7 @@ Safari.prototype._configureBootstrap = IOS.prototype.configureBootstrap;
Safari.prototype.configureBootstrap = function (cb) {
if (this.shouldIgnoreInstrumentsExit()) {
logger.debug("Not setting bootstrap config because we're on " +
"SafariLauncher");
"SafariLauncher");
return cb();
}
this._configureBootstrap(cb);
@@ -145,6 +128,23 @@ Safari.prototype.installToRealDevice = function (cb) {
}
};
Safari.prototype.clearAppData = function (cb) {
if (this.args.fullReset) {
// Even though we delete typical apps on a regular reset, we do a good
// job of getting safari back to its original state, so actually deleting
// it is overkill in most cases, and requires an instantLaunchAndQuit.
// So we only delete it if the user requests a full reset
try {
this.sim.deleteSafari();
} catch (e) {
return cb(e);
}
cb();
} else {
this.sim.cleanSafari(this.keepAppToRetainPrefs, cb);
}
};
Safari.prototype._stopRemote = IOS.prototype.stopRemote;
Safari.prototype.stopRemote = function () {
this._stopRemote(true);
+38 -129
View File
@@ -4,10 +4,10 @@ var logger = require('../../server/logger.js').get('appium')
, _ = require('underscore')
, fs = require('fs')
, path = require('path')
, xmlplist = require('plist')
, bplistCreate = require('bplist-creator')
, mkdirp = require('mkdirp')
, bplistParse = require('bplist-parser');
, Simulator = require('./simulator.js')
, multiResolve = require('../../helpers.js').multiResolve;
var settings = {};
var plists = {
@@ -26,13 +26,17 @@ var prefs = {
mobileSafari: {
OpenLinksInBackground: [0, 1],
WebKitJavaScriptCanOpenWindowsAutomatically: [true, false],
JavaScriptCanOpenWindowsAutomatically: [true, false],
SearchEngineStringSetting: ['Google', 'Yahoo!', 'Bing'],
SafariDoNotTrackEnabled: [true, false],
SuppressSearchSuggestions: [true, false],
SpotlightSuggestionsEnabled: [true, false],
SpeculativeLoading: [true, false],
WarnAboutFraudulentWebsites: [true, false],
ReadingListCellularFetchingEnabled: [true, false],
WebKitJavaScriptEnabled: [true, false]
WebKitJavaScriptEnabled: [true, false],
JavaScriptEnabled: [true, false],
DisableWebsiteSpecificSearch: [true, false]
},
webFoundation: {
NSHTTPAcceptCookies: ['never', 'always', 'current page']
@@ -54,99 +58,36 @@ var prefs = {
}
};
var multiResolve = function (roots) {
var args = Array.prototype.slice.call(arguments, 1);
var paths = [];
_.each(roots, function (root) {
var resolveArgs = [root].concat(args);
paths.push(path.resolve.apply(null, resolveArgs));
});
return paths;
};
var getPlistPaths = function (plist, sdk) {
var getPlistPaths = function (plist, sim) {
var files;
var file = plists[plist];
var bases = getPrefsDirs(sdk);
if (plist === 'mobileSafari' && parseFloat(sdk) >= 7) {
bases = getSafari7Dirs(sdk);
var bases = getPrefsDirs(sim);
if (plist === 'mobileSafari' && parseFloat(sim.platformVer) >= 7) {
bases = multiResolve(sim.getSafariDirs(), "Library", "Preferences");
} else if (plist === 'locationClients') {
bases = multiResolve(getSimDirs(sdk), "Library", "Caches", "locationd");
bases = multiResolve(sim.getDirs(), "Library", "Caches", "locationd");
} else if (plist === 'locationCache') {
var bases2 = multiResolve(getSimDirs(sdk), "Library", "Caches", "locationd");
var bases2 = multiResolve(sim.getDirs(), "Library", "Caches", "locationd");
files = multiResolve(bases, file);
files = files.concat(multiResolve(bases2, file));
return files;
} else if (plist === 'userSettings') {
files = multiResolve(getSimDirs(sdk), "Library", "ConfigurationProfiles", file);
files = files.concat(multiResolve(getSimDirs(sdk), "Library", "ConfigurationProfiles",
"PublicInfo", 'EffectiveUserSettings.plist'));
files = multiResolve(sim.getDirs(), "Library", "ConfigurationProfiles",
file);
files = files.concat(multiResolve(sim.getDirs(), "Library",
"ConfigurationProfiles", "PublicInfo",
"EffectiveUserSettings.plist"));
files = files.concat(multiResolve(sim.getDirs(), "Library",
"ConfigurationProfiles", "PublicInfo",
"PublicEffectiveUserSettings.plist"));
return files;
}
return multiResolve(bases, file);
};
settings.getSimRoot = function () {
var u = process.env.USER;
return path.resolve("/Users", u, "Library", "Application Support",
"iPhone Simulator");
};
settings.getSimRootsWithVersion = function (sdk) {
sdk = sdk.toString();
var base = settings.getSimRoot();
var list = [];
try {
list = fs.readdirSync(base);
} catch (e) {
}
var dirs = [];
_.each(list, function (sdkDir) {
if (sdkDir.indexOf(sdk) !== -1) {
dirs.push(path.resolve(base, sdkDir));
}
});
return dirs;
};
var getSimDirs = settings.getSimRootsWithVersion;
var getPrefsDirs = function (sdk) {
return multiResolve(getSimDirs(sdk), "Library", "Preferences");
};
var getSafari7Dirs = function (sdk) {
var appsDirs = multiResolve(getSimDirs(sdk), "Applications");
var safariDirs = [];
_.each(appsDirs, function (appsDir) {
var list;
try {
list = fs.readdirSync(appsDir);
} catch (e) {
if (/ENOENT/.test(e.message)) {
logger.warn("Applications directory " + appsDir + " doesn't exist. " +
"Have you run this simulator before?");
return;
}
throw e;
}
var safariDir = null;
for (var i = 0; i < list.length; i++) {
if (fs.existsSync(path.resolve(appsDir, list[i], "MobileSafari.app")) ||
fs.existsSync(path.resolve(appsDir, list[i], "Appium-MobileSafari.app"))) {
safariDir = path.resolve(appsDir, list[i], "Library", "Preferences");
break;
}
}
if (safariDir) {
safariDirs.push(safariDir);
} else {
logger.warn("Could not find MobileSafari in sim applications at " +
appsDir);
}
});
return safariDirs;
var getPrefsDirs = function (sim) {
return multiResolve(sim.getDirs(), "Library", "Preferences");
};
var checkValidSettings = function (forPlist, prefSet) {
@@ -206,11 +147,11 @@ settings.writeSettings = function (forPlist, prefSetPerFile, bypassCheck,
}
};
settings.updateSettings = function (sdk, forPlist, prefSet) {
settings.updateSettings = function (sim, forPlist, prefSet) {
logger.debug("Updating settings for " + forPlist);
checkValidSettings(forPlist, prefSet);
var prefSetPerFile = {};
var curSettings = settings.getSettings(sdk, forPlist);
var curSettings = settings.getSettings(sim, forPlist);
_.each(curSettings, function (settingSet, file) {
_.each(prefSet, function (prefValue, prefName) {
settingSet[prefName] = prefValue;
@@ -220,7 +161,7 @@ settings.updateSettings = function (sdk, forPlist, prefSet) {
settings.writeSettings(forPlist, prefSetPerFile, true);
};
settings.updateLocationSettings = function (sdk, bundleId, authorized) {
settings.updateLocationSettings = function (sim, bundleId, authorized) {
var weirdLocKey = "com.apple.locationd.bundle-/System/Library/" +
"PrivateFrameworks/AOSNotification.framework";
var newPrefs = {
@@ -234,11 +175,11 @@ settings.updateLocationSettings = function (sdk, bundleId, authorized) {
};
var prefSetPerFile = {};
var cachePrefSetPerFile = {};
var curCacheSettings = settings.getSettings(sdk, 'locationCache');
var curCacheSettings = settings.getSettings(sim, 'locationCache');
_.each(curCacheSettings, function (settingSet, file) {
cachePrefSetPerFile[file] = _.extend(_.clone(newCachePrefs), settingSet);
});
var curSettings = settings.getSettings(sdk, 'locationClients');
var curSettings = settings.getSettings(sim, 'locationClients');
_.each(curSettings, function (settingSet, file) {
// add this random data to the clients.plist since it always seems to be there
if (!_.has(settingSet, weirdLocKey)) {
@@ -266,12 +207,12 @@ settings.updateLocationSettings = function (sdk, bundleId, authorized) {
settings.writeSettings('locationCache', cachePrefSetPerFile, true);
};
settings.updateSafariSettings = function (sdk, settingSet) {
settings.updateSafariUserSettings(sdk, settingSet);
settings.updateSettings(sdk, 'mobileSafari', settingSet);
settings.updateSafariSettings = function (sim, settingSet) {
settings.updateSafariUserSettings(sim, settingSet);
settings.updateSettings(sim, 'mobileSafari', settingSet);
};
settings.updateSafariUserSettings = function (sdk, settingSet) {
settings.updateSafariUserSettings = function (sim, settingSet) {
// add extra stuff to UserSettings.plist and EffectiveUserSettings.plist
var newUserSettings = {};
if (_.has(settings, 'WebKitJavaScriptEnabled')) {
@@ -286,7 +227,7 @@ settings.updateSafariUserSettings = function (sdk, settingSet) {
if (_.size(newUserSettings) > 0) {
logger.debug("Updating UserSettings.plist and friends");
var userSettingsPerFile = {};
var curUserSettings = settings.getSettings(sdk, 'userSettings');
var curUserSettings = settings.getSettings(sim, 'userSettings');
_.each(curUserSettings, function (settingSet, file) {
_.extend(settingSet.restrictedBool, newUserSettings);
userSettingsPerFile[file] = settingSet;
@@ -295,33 +236,13 @@ settings.updateSafariUserSettings = function (sdk, settingSet) {
}
};
var getPlistData = function (file) {
var data;
if (fs.existsSync(file)) {
var fileData = fs.readFileSync(file);
try {
data = bplistParse.parseBuffer(fileData)[0];
} catch (err) {
if (err.message.indexOf("Invalid binary plist") !== -1) {
logger.debug("Plist was not binary format, retrying with xml");
data = xmlplist(file)[0];
} else {
throw err;
}
}
} else {
throw new Error("Settings file " + file + " did not exist");
}
return data;
};
settings.getSettings = function (sdk, forPlist) {
var files = getPlistPaths(forPlist, sdk);
settings.getSettings = function (sim, forPlist) {
var files = getPlistPaths(forPlist, sim);
var bplists = {};
_.each(files, function (file) {
logger.debug("Getting current settings for " + forPlist + " from " + file);
try {
bplists[file] = getPlistData(file);
bplists[file] = Simulator.getPlistData(file);
} catch (err) {
bplists[file] = {};
logger.warn(err.message);
@@ -330,20 +251,8 @@ settings.getSettings = function (sdk, forPlist) {
return bplists;
};
settings.simDirsExist = function (sdk) {
return getSimDirs(sdk).length > 0;
};
settings.safari7DirsExist = function (sdk) {
try {
return getSafari7Dirs(sdk).length > 0;
} catch (e) {
return false;
}
};
settings.locServicesDirsExist = function (sdk) {
return getPlistPaths('locationClients', sdk).length > 0;
settings.locServicesDirsExist = function (sim) {
return getPlistPaths('locationClients', sim).length > 0;
};
module.exports = settings;
+83
View File
@@ -0,0 +1,83 @@
"use strict";
var logger = require('../../server/logger.js').get('appium')
, _ = require('underscore')
, exec = require('child_process').exec;
var Simctl = function (opts) {
var requiredOpts = [];
_.each(requiredOpts, function (opt) {
if (!_.has(opts, opt)) {
throw new Error(opt + " is required");
}
this[opt] = opts[opt];
}.bind(this));
};
Simctl.exec = function (cmd, args, cb) {
if (typeof args === "function") {
cb = args;
args = [];
}
args = _.map(args, function (arg) {
if (args.indexOf(" ") !== -1) {
return '"' + arg + '"';
}
return arg;
});
cmd = "xcrun simctl " + cmd + " " + args.join(' ');
logger.debug("Executing: " + cmd);
exec(cmd, cb);
};
Simctl.delete = function (udid, cb) {
Simctl.exec("delete", [udid], cb);
};
Simctl.getDevices = function (forSdk, cb) {
if (typeof forSdk === "function") {
cb = forSdk;
forSdk = null;
}
Simctl.exec("list", ["devices"], function (err, stdout) {
if (err) return cb(err);
var deviceSecRe = /-- iOS (.+) --(\n .+)*/mg;
var matches = [];
var devices = {};
var match = deviceSecRe.exec(stdout);
while (match !== null) {
matches.push(match);
match = deviceSecRe.exec(stdout);
}
if (matches.length < 1) {
return cb(new Error("Could not find device section"));
}
_.each(matches, function (match) {
var sdk = match[1];
devices[sdk] = [];
_.each(match[0].split("\n").slice(1), function (line) {
var lineRe = /^ (.+) \((.+)\) \((.+)\)/;
var match = lineRe.exec(line);
if (match === null) {
throw new Error("Couldn't match line");
}
var device = {};
device.name = match[1];
device.udid = match[2];
device.state = match[3];
devices[sdk].push(device);
});
});
if (forSdk) {
if (!_.has(devices, forSdk)) {
cb(new Error("Sdk " + forSdk + " was not in list of simctl sdks"));
} else {
cb(null, devices[forSdk]);
}
} else {
cb(null, devices);
}
});
};
module.exports = Simctl;
+490
View File
@@ -0,0 +1,490 @@
"use strict";
var logger = require('../../server/logger.js').get('appium')
, _ = require('underscore')
, fs = require('fs')
, async = require('async')
, exec = require('child_process').exec
, path = require('path')
, ncp = require('ncp')
, xcode = require('./xcode.js')
, Simctl = require('./simctl.js')
, multiResolve = require('../../helpers.js').multiResolve
, rimraf = require('rimraf')
, xmlplist = require('plist')
, bplistCreate = require('bplist-creator')
, bplistParse = require('bplist-parser');
var Simulator = function (opts) {
var requiredOpts = ['sdkVer', 'platformVer', 'udid'];
_.each(requiredOpts, function (opt) {
if (!_.has(opts, opt)) {
throw new Error(opt + " is required");
}
this[opt] = opts[opt];
}.bind(this));
};
var wrapInArray = function (res) {
if (res) {
return [res];
} else {
return [];
}
};
var rmrf = function (delPath, cb) {
exec("rm -rf " + delPath, function (err) {
cb(err);
});
};
var safeRimRafSync = function (delPath, tries) {
if (_.isUndefined(tries)) tries = 0;
try {
rimraf.sync(delPath);
} catch (e) {
if (e.message.indexOf("ENOTEMPTY") !== -1 && tries < 20) {
logger.debug("Path " + delPath + " was not empty during delete; retrying");
safeRimRafSync(delPath, tries + 1);
} else {
throw e;
}
}
};
Simulator.prototype.getRootDir = function () {
var u = process.env.USER;
if (parseFloat(this.sdkVer) >= 8) {
return path.resolve("/Users", u, "Library", "Developer",
"CoreSimulator", "Devices");
}
return path.resolve("/Users", u, "Library", "Application Support",
"iPhone Simulator");
};
Simulator.prototype.getDirs = function () {
var base;
if (parseFloat(this.sdkVer) >= 8) {
base = path.resolve(this.getRootDir(), this.udid, "data");
if (fs.existsSync(base)) {
return [base];
}
return [];
} else {
var sdk = this.sdkVer.toString();
base = this.getRootDir();
var list = [];
try {
list = fs.readdirSync(base);
} catch (e) {
}
var dirs = [];
_.each(list, function (sdkDir) {
if (sdkDir.indexOf(sdk) !== -1) {
dirs.push(path.resolve(base, sdkDir));
}
});
return dirs;
}
};
Simulator.getPlistData = function (file) {
var data;
if (fs.existsSync(file)) {
var fileData = fs.readFileSync(file);
try {
data = bplistParse.parseBuffer(fileData)[0];
} catch (err) {
if (err.message.indexOf("Invalid binary plist") !== -1) {
logger.debug("Plist was not binary format, retrying with xml");
data = xmlplist(file)[0];
} else {
throw err;
}
}
} else {
throw new Error("Settings file " + file + " did not exist");
}
return data;
};
var findAppByCondition = function (dir, condition) {
var list;
try {
list = fs.readdirSync(dir);
} catch (e) {
if (/ENOENT/.test(e.message)) {
logger.warn("Applications directory " + dir + " doesn't exist. " +
"Have you run this simulator before?");
return null;
}
throw e;
}
var appDir = null;
for (var i = 0; i < list.length; i++) {
if (condition(list[i])) {
appDir = path.resolve(dir, list[i]);
break;
}
}
return appDir;
};
Simulator.prototype.getSafariDirs = function () {
if (parseFloat(this.platformVer) >= 8) {
return wrapInArray(this.getSafari8Dir());
} else {
return this.getSafari7Dirs();
}
};
Simulator.prototype.getAppDirs = function (appFile, appBundleId) {
if (parseFloat(this.platformVer) >= 8) {
var dataDirs = wrapInArray(this.getApp8Dir(appBundleId, "Data"));
var bundleDirs = wrapInArray(this.getApp8Dir(appBundleId, "Bundle"));
return dataDirs.concat(bundleDirs);
} else {
return this.getApp7Dirs(appFile);
}
};
Simulator.prototype.getSafari7Dirs = function () {
return this.getApp7Dirs(["MobileSafari.app", "Appium-MobileSafari-" + this.platformVer + ".app"]);
};
Simulator.prototype.getApp7Dirs = function (appFiles) {
var appsDirs = multiResolve(this.getDirs(), "Applications");
var foundAppDirs = [];
_.each(appsDirs, function (appsDir) {
var cond = function (d) {
var found = false;
_.each(appFiles, function (appFile) {
found = found || fs.existsSync(path.resolve(appsDir, d, appFile));
});
return found;
};
var appDir = findAppByCondition(appsDir, cond);
if (appDir !== null) {
foundAppDirs.push(appDir);
}
});
return foundAppDirs;
};
Simulator.prototype.getSafari8Dir = function () {
return this.getApp8Dir("com.apple.mobilesafari", "Data");
};
Simulator.prototype.getApp8Dir = function (bundleId, dataOrBundle) {
var root = this.getDirs()[0];
if (!root) {
return null;
}
var appsDir = path.resolve(root, "Containers", dataOrBundle, "Application");
var magicFile = ".com.apple.mobile_container_manager.metadata.plist";
var cond = function (d) {
var magicPlist = path.resolve(appsDir, d, magicFile);
if (!fs.existsSync(magicPlist)) {
return false;
}
var data = Simulator.getPlistData(magicPlist);
return data.MCMMetadataIdentifier === bundleId;
};
return findAppByCondition(appsDir, cond);
};
Simulator.prototype.dirsExist = function () {
return this.getDirs().length > 0;
};
Simulator.prototype.safariDirsExist = function () {
try {
return this.getSafariDirs().length > 0;
} catch (e) {
return false;
}
};
Simulator.prototype.cleanSafari = function (keepPrefs, cb) {
// this method is async because we use glob paths for deletes and rimraf
// can't handle them, so we use our own async 'rmrf' model that calls out
// to shell to do it
logger.debug("Cleaning mobile safari data files");
if (!this.dirsExist()) {
logger.info("Couldn't find Safari support directories to clean out old " +
"data. Probably there's nothing to clean out");
return;
}
var libraryDirs = multiResolve(this.getDirs(), 'Library');
var safariLibDirs = multiResolve(this.getSafariDirs(), 'Library');
var toDeletes = [
'Caches/Snapshots/com.apple.mobilesafari'
, 'Caches/com.apple.mobilesafari/Cache.db*'
, 'Caches/com.apple.WebAppCache/*.db'
, 'Safari/*.plist'
, 'WebKit/LocalStorage/*.*'
, 'WebKit/GeolocationSites.plist'
, 'Cookies/*.binarycookies'
];
var deleteActions = [];
var buildLibraryDeletes = function (libDirs, toDeletes) {
_.each(libDirs, function (libraryDir) {
_.each(toDeletes, function (relRmPath) {
var rmPath = path.resolve(libraryDir, relRmPath);
deleteActions.push(function (cb) {
logger.debug("Deleting " + rmPath);
rmrf(rmPath, cb);
});
});
});
};
var safariToDeletes = _.clone(toDeletes);
if (!keepPrefs) {
safariToDeletes.push('Preferences/*.plist');
}
buildLibraryDeletes(libraryDirs, toDeletes);
buildLibraryDeletes(safariLibDirs, safariToDeletes);
async.parallel(deleteActions, cb);
};
Simulator.prototype.deleteSafari = function () {
logger.debug("Deleting Safari apps");
var safariDirs = multiResolve(this.getSafariDirs());
if (parseFloat(this.platformVer) >= 8) {
var safariBundleDir = this.getApp8Dir("com.apple.mobilesafari", "Bundle");
safariDirs = safariDirs.concat(wrapInArray(safariBundleDir));
}
_.each(safariDirs, function (safariDir) {
logger.debug("Deleting " + safariDir);
safeRimRafSync(safariDir);
});
};
Simulator.prototype.cleanCustomApp = function (appFile, appBundleId) {
logger.debug("Cleaning app data files");
var appDirs = this.getAppDirs(appFile, appBundleId);
if (appDirs.length === 0) {
logger.info("Couldn't find app directories to delete. Probably it's not " +
"installed");
return;
}
_.each(appDirs, function (appDir) {
logger.debug("Deleting " + appDir);
safeRimRafSync(appDir);
});
};
Simulator.prototype.cleanSim = function (keepKeychains) {
logger.debug("Cleaning sim data files");
if (!this.dirsExist()) {
logger.info("Couldn't find support directories to clean out old " +
"data. Probably there's nothing to clean out");
return;
}
var toDeletes = [
'Library/TCC',
'Library/Caches/locationd',
'Library/BackBoard/applicationState.plist',
'Media'
];
if (!keepKeychains) {
toDeletes.push('Library/Keychains');
}
_.each(this.getDirs(), function (simDir) {
_.each(toDeletes, function (relRmPath) {
var rmPath = path.resolve(simDir, relRmPath);
logger.debug("Deleting " + rmPath);
safeRimRafSync(rmPath);
});
});
logger.debug("Cleaning sim preferences");
var prefsPlist = Simulator.getSimAppPrefsFile();
var data = Simulator.getPlistData(prefsPlist);
var keysToClear = [
'CurrentDeviceUDID',
'SimulateDevice'
];
var changed = false;
_.each(keysToClear, function (key) {
if (_.has(data, key)) {
logger.debug("Clearing key: " + key);
delete data[key];
changed = true;
}
});
if (changed) {
logger.debug("Writing new preferences plist data");
fs.writeFileSync(prefsPlist, bplistCreate(data));
}
};
Simulator.prototype.moveBuiltInApp = function (appPath, appName, newAppDir, cb) {
ncp(appPath, newAppDir, function (err) {
if (err) return cb(err);
logger.debug("Copied " + appName + " to " + newAppDir);
rimraf(appPath, function (err) {
if (err) {
if (err.message.indexOf("EACCES") !== -1) {
return cb(new Error("We don't have write access to " + appPath +
", please re-run authorize as " + process.env.USER));
}
return cb(err);
}
logger.debug("Temporarily deleted original app at " + appPath);
cb(null, newAppDir, appPath);
});
});
};
Simulator.prototype.getBuiltInApp = function (appName, cb) {
this.getBuiltInAppDir(function (err, appDir) {
if (err) return cb(err);
var appPath = path.resolve(appDir, appName + ".app");
fs.stat(appPath, function (err, s) {
if (err && err.message.indexOf("ENOENT") !== -1) {
logger.debug("App is not at " + appPath);
return cb(err);
}
cb(null, s, appPath);
});
});
};
Simulator.prototype.prepareBuiltInApp = function (appName, tmpDir, cb) {
logger.debug("Looking for built in app " + appName);
var newAppDir = path.resolve(tmpDir, 'Appium-' + appName + '-' + this.platformVer + '.app');
var checkApp = function (s, appPath, cb) {
if (!s.isDirectory()) {
cb(new Error("App package was not a directory"), appPath);
return false;
}
return true;
};
this.getBuiltInApp(appName, function (err, s, appPath) {
if (err) {
fs.stat(newAppDir, function (err, s) {
if (err) {
logger.warn("App is also not at " + newAppDir);
return cb(new Error("Couldn't find built in app in its home " +
"or temp dir!"));
}
if (checkApp(s, appPath, cb)) {
logger.debug("Couldn't find original app, but found the temp " +
"Appium one so using that");
cb(null, newAppDir, appPath);
}
});
return;
}
if (checkApp(s, appPath, cb)) {
if (parseInt(this.platformVer, 10) < 7) {
logger.debug("Got app, trying to copy " + appPath + " to tmp dir");
ncp(appPath, newAppDir, function (err) {
if (err) return cb(err);
logger.debug("Copied " + appName);
cb(null, newAppDir, null);
});
} else {
logger.debug("Got app, trying to move " + appPath + " to tmp dir");
this.moveBuiltInApp(appPath, appName, newAppDir, cb);
}
}
}.bind(this));
};
Simulator.prototype.getBuiltInAppDir = function (cb) {
xcode.getPath(function (err, xcodeDir) {
if (err) return cb(err);
var appDir = path.resolve(xcodeDir, "Platforms/iPhoneSimulator.platform/" +
"Developer/SDKs/iPhoneSimulator" +
this.platformVer + ".sdk/Applications/");
fs.stat(appDir, function (err, s) {
if (err) {
cb(err);
} else if (!s.isDirectory()) {
cb(new Error("Could not load built in applications directory"));
} else {
cb(null, appDir);
}
});
}.bind(this));
};
Simulator.prototype.prepareSafari = function (tmpDir, cb) {
this.prepareBuiltInApp("MobileSafari", tmpDir, cb);
};
Simulator.prototype.preparePreferencesApp = function (tmpDir, cb) {
this.prepareBuiltInApp("Preferences", tmpDir, cb);
};
Simulator.prototype.deleteSim = function () {
var root = this.getRootDir();
logger.debug("Deleting simulator folder: " + root);
safeRimRafSync(root);
};
Simulator.prototype.setLocale = function (language, locale, calendar) {
var globalPrefs = multiResolve(this.getDirs(), "Library", "Preferences",
".GlobalPreferences.plist");
_.each(globalPrefs, function (plist) {
var data = Simulator.getPlistData(plist);
var curLocaleAndCal = data.AppleLocale || null;
var supportedLangs = data.AppleLanguages || [];
var newLangs = [];
if (language) {
logger.debug("New language: " + language);
newLangs.push(language);
newLangs = newLangs.concat(_.without(supportedLangs, language));
data.AppleLanguages = newLangs;
}
if (locale || calendar) {
var calSplit = "@calendar=";
if (curLocaleAndCal === null) {
curLocaleAndCal = language || 'en';
}
var curLoc = curLocaleAndCal.split(calSplit)[0];
var newLocaleAndCal = locale ? locale : curLoc;
if (calendar) {
newLocaleAndCal += calSplit + calendar;
}
logger.debug("New locale: " + newLocaleAndCal);
data.AppleLocale = newLocaleAndCal;
}
logger.debug("Writing new locale plist data");
fs.writeFileSync(plist, bplistCreate(data));
});
};
Simulator.getSimAppPrefsFile = function () {
var u = process.env.USER;
return path.resolve("/Users", u, "Library", "Preferences",
"com.apple.iphonesimulator.plist");
};
Simulator.prototype.deleteOtherSims = function (cb) {
logger.debug("Isolating the requested simulator by deleting all others");
Simctl.getDevices(function (err, devices) {
if (err) return cb(err);
var udids = [];
var deleteCalls = [];
_.each(devices, function (sdkDevices) {
udids = udids.concat(_.pluck(sdkDevices, 'udid'));
});
_.each(_.without(udids, this.udid), function (udid) {
deleteCalls.push(_.partial(Simctl.delete, udid));
});
async.series(deleteCalls, cb);
}.bind(this));
};
module.exports = Simulator;
+86
View File
@@ -0,0 +1,86 @@
"use strict";
var logger = require('../../server/logger.js').get('appium')
, fs = require('fs')
, path = require('path')
, exec = require('child_process').exec;
var xcode = {};
xcode.getPath = function (cb) {
exec('xcode-select --print-path', { maxBuffer: 524288, timeout: 3000 }, function (err, stdout, stderr) {
if (!err) {
var xcodeFolderPath = stdout.replace("\n", "");
if (fs.existsSync(xcodeFolderPath)) {
cb(null, xcodeFolderPath);
} else {
logger.error(xcodeFolderPath + "does not exist.");
cb(new Error("xcode-select could not find xcode"), null);
}
} else {
logger.error("xcode-select threw error " + err);
logger.error("Stderr: " + stderr);
logger.error("Stdout: " + stdout);
cb(new Error("xcode-select threw an error"), null);
}
});
};
xcode.getVersion = function (cb) {
xcode.getPath(function (err, xcodePath) {
if (err !== null) {
cb(err, null);
} else {
var xcodebuildPath = path.resolve(xcodePath, "usr/bin/xcodebuild");
if (!fs.existsSync(xcodebuildPath)) {
cb(new Error("Could not get Xcode version: " + xcodebuildPath + " does not exist on disk."), null);
} else {
exec(JSON.stringify(xcodebuildPath) + ' -version', { maxBuffer: 524288, timeout: 3000 }, function (err, stdout) {
var versionPattern = /\d\.\d\.*\d*/;
var match = versionPattern.exec(stdout);
if (match === null) {
cb(new Error("Could not parse Xcode version (xcodebuild output was: " + stdout + ")"), null);
} else {
var versionNumber = match[0];
cb(null, versionNumber);
}
});
}
}
});
};
xcode.getiOSSDKVersion = function (cb) {
var msg;
xcode.getVersion(function (err, versionNumber) {
if (err) {
msg = "Could not get the iOS SDK version because the Xcode version could not be determined.";
logger.error(msg);
cb(new Error(msg), null);
} else if (versionNumber[0] === '4') {
cb(null, '6.1');
} else {
exec('xcrun --sdk iphonesimulator --show-sdk-version', { maxBuffer: 524288, timeout: 3000 }, function (err, stdout, stderr) {
if (!err) {
var iosSDKVersion = stdout.replace("\n", "");
var match = /\d.\d/.exec(iosSDKVersion);
if (match) {
cb(null, iosSDKVersion);
} else {
msg = "xcrun returned a non-numeric iOS SDK version: " + iosSDKVersion;
logger.error(msg);
cb(new Error(msg), null);
}
} else {
msg = "xcrun threw an error " + err;
logger.error(msg);
logger.error("Stderr: " + stderr);
logger.error("Stdout: " + stdout);
cb(new Error(msg), null);
}
});
}
});
};
module.exports = xcode;
+10 -255
View File
@@ -2,15 +2,12 @@
var logger = require('./server/logger.js').get('appium')
, fs = require('fs')
, ncp = require('ncp').ncp
, request = require('request')
, _ = require('underscore')
, path = require('path')
, rimraf = require('rimraf')
, exec = require('child_process').exec
, osType = require('os').type()
, tempdir = require('./tempdir')
, glob = require('glob')
, AdmZip = require('adm-zip');
@@ -147,161 +144,6 @@ exports.unzipApp = function (zipPath, appExt, cb) {
});
};
exports.checkBuiltInApp = function (appName, version, tmpDir, cb) {
logger.debug("Looking for built in app " + appName);
var newAppDir = path.resolve(tmpDir, 'Appium-' + appName + '.app');
var checkApp = function (s, appPath, cb) {
if (!s.isDirectory()) {
cb(new Error("App package was not a directory"), appPath);
return false;
}
return true;
};
exports.getBuiltInAppDir(version, function (err, appDir) {
if (err) return cb(err);
var appPath = path.resolve(appDir, appName + ".app");
fs.stat(appPath, function (err, s) {
if (err && err.message.indexOf("ENOENT") !== -1) {
logger.debug("App is not at " + appPath);
fs.stat(newAppDir, function (err, s) {
if (err) {
logger.warn("App is also not at " + newAppDir);
return cb(new Error("Couldn't find built in app in its home " +
"or temp dir!"));
}
if (checkApp(s, appPath, cb)) {
logger.debug("Couldn't find original app, but found the temp " +
"Appium one so using that");
cb(null, newAppDir, appPath);
}
});
return;
} else if (err) {
return cb(err);
}
if (checkApp(s, appPath, cb)) {
if (parseInt(version, 10) < 7) {
logger.debug("Got app, trying to copy " + appPath + " to tmp dir");
ncp(appPath, newAppDir, function (err) {
if (err) return cb(err);
logger.debug("Copied " + appName);
cb(null, newAppDir, null);
});
} else {
logger.debug("Got app, trying to move " + appPath + " to tmp dir");
exports.moveBuiltInApp(appPath, appName, newAppDir, cb);
}
}
});
});
};
exports.checkSafari = function (version, tmpDir, cb) {
exports.checkBuiltInApp("MobileSafari", version, tmpDir, cb);
};
exports.checkPreferencesApp = function (version, tmpDir, cb) {
exports.checkBuiltInApp("Preferences", version, tmpDir, cb);
};
exports.getBuiltInAppDir = function (version, cb) {
exports.getXcodeFolder(function (err, xcodeDir) {
if (err) return cb(err);
var appDir = path.resolve(xcodeDir, "Platforms/iPhoneSimulator.platform/" +
"Developer/SDKs/iPhoneSimulator" + version +
".sdk/Applications/");
fs.stat(appDir, function (err, s) {
if (err) {
cb(err);
} else if (!s.isDirectory()) {
cb(new Error("Could not load built in applications directory"));
} else {
cb(null, appDir);
}
});
});
};
exports.moveBuiltInApp = function (appPath, appName, newAppDir, cb) {
ncp(appPath, newAppDir, function (err) {
if (err) return cb(err);
logger.debug("Copied " + appName + " to " + newAppDir);
rimraf(appPath, function (err) {
if (err) {
if (err.message.indexOf("EACCES") !== -1) {
return cb(new Error("We don't have write access to " + appPath +
", please re-run authorize as " + process.env.USER));
}
return cb(err);
}
logger.debug("Temporarily deleted original app at " + appPath);
cb(null, newAppDir, appPath);
});
});
};
exports.cleanSafari = function (safariVer, cb) {
var baseDir = "Library/Application Support/iPhone Simulator/" +
safariVer + "*/Library/";
exports.getUser(function (err, user) {
if (err) {
logger.error(err);
cb(err);
} else {
var baseSupportDirs = path.resolve("/Users", user, baseDir);
glob(baseSupportDirs, function (err, pathList) {
if (err) return cb(err);
if (pathList.length === 0) {
logger.debug("Couldn't find Safari support directories in order " +
"to clear out old data");
return cb();
}
var pathsFinished = 0;
var errToRet = null;
var finishAll = function (err) {
if (err) errToRet = err;
pathsFinished++;
if (pathsFinished === pathList.length) {
cb(err);
}
};
_.each(pathList, function (baseSupportDir) {
var toDeletes = [
'Caches/Snapshots/com.apple.mobilesafari'
, 'Caches/com.apple.mobilesafari/Cache.db*'
, 'Caches/com.apple.WebAppCache/*.db'
, 'Safari/*.plist'
, 'WebKit/LocalStorage/*.*'
, 'Library/WebKit/GeolocationSites.plist'
, 'Cookies/*.binarycookies'
];
var deletes = 0;
var errToRet = null;
var finishOne = function (err) {
deletes++;
if (err) {
errToRet = err;
}
if (deletes === toDeletes.length) {
finishAll(errToRet);
}
};
_.each(toDeletes, function (del) {
var toDelete = path.resolve(baseSupportDir, del);
toDelete = toDelete.replace(/ /g, "\\ ");
logger.debug("Deleting " + toDelete);
var cmd = "rm -rf " + toDelete;
exec(cmd, function (err) {
finishOne(err);
});
});
});
});
}
});
};
exports.getUser = function (cb) {
logger.debug("Determining current user");
exec("whoami", { maxBuffer: 524288 }, function (err, stdout) {
@@ -315,6 +157,16 @@ exports.getUser = function (cb) {
});
};
exports.multiResolve = function (roots) {
var args = Array.prototype.slice.call(arguments, 1);
var paths = [];
_.each(roots, function (root) {
var resolveArgs = [root].concat(args);
paths.push(path.resolve.apply(null, resolveArgs));
});
return paths;
};
exports.delay = function (secs) {
var date = new Date();
var curDate = null;
@@ -322,13 +174,6 @@ exports.delay = function (secs) {
while (curDate - date < (secs * 1000.0));
};
// var pad0 = function (x) {
// if (x.toString().length == 1) {
// x = '0' + x;
// }
// return x;
// };
exports.escapeSpecialChars = function (str, quoteEscape) {
if (typeof str !== "string") {
return str;
@@ -482,96 +327,6 @@ exports.getGitRev = function (cb) {
});
};
exports.getXcodeFolder = function (cb) {
exec('xcode-select --print-path', { maxBuffer: 524288, timeout: 3000 }, function (err, stdout, stderr) {
if (!err) {
var xcodeFolderPath = stdout.replace("\n", "");
if (fs.existsSync(xcodeFolderPath)) {
cb(null, xcodeFolderPath);
} else {
logger.error(xcodeFolderPath + "does not exist.");
cb(new Error("xcode-select could not find xcode"), null);
}
} else {
logger.error("xcode-select threw error " + err);
logger.error("Stderr: " + stderr);
logger.error("Stdout: " + stdout);
cb(new Error("xcode-select threw an error"), null);
}
});
};
exports.getXcodeVersion = function (cb) {
exports.getXcodeFolder(function (err, xcodePath) {
if (err !== null) {
cb(err, null);
} else {
var xcodebuildPath = path.resolve(xcodePath, "usr/bin/xcodebuild");
if (!fs.existsSync(xcodebuildPath)) {
cb(new Error("Could not get Xcode version: " + xcodebuildPath + " does not exist on disk."), null);
} else {
exec(JSON.stringify(xcodebuildPath) + ' -version', { maxBuffer: 524288, timeout: 3000 }, function (err, stdout) {
var versionPattern = /\d\.\d\.*\d*/;
var match = versionPattern.exec(stdout);
if (match === null) {
cb(new Error("Could not parse Xcode version (xcodebuild output was: " + stdout + ")"), null);
} else {
var versionNumber = match[0];
cb(null, versionNumber);
}
});
}
}
});
};
exports.getiOSSDKVersion = function (cb) {
var msg;
exports.getXcodeVersion(function (err, versionNumber) {
if (err) {
msg = "Could not get the iOS SDK version because the Xcode version could not be determined.";
logger.error(msg);
cb(new Error(msg), null);
} else if (versionNumber[0] === '4') {
cb(null, '6.1');
} else {
exec('xcrun --sdk iphonesimulator --show-sdk-version', { maxBuffer: 524288, timeout: 3000 }, function (err, stdout, stderr) {
if (!err) {
var iosSDKVersion = stdout.replace("\n", "");
var match = /\d.\d/.exec(iosSDKVersion);
if (match) {
cb(null, iosSDKVersion);
} else {
msg = "xcrun returned a non-numeric iOS SDK version: " + iosSDKVersion;
logger.error(msg);
cb(new Error(msg), null);
}
} else {
msg = "xcrun threw an error " + err;
logger.error(msg);
logger.error("Stderr: " + stderr);
logger.error("Stdout: " + stdout);
cb(new Error(msg), null);
}
});
}
});
};
exports.getiOSSimulatorDirectories = function (cb) {
var basePath = path.resolve(process.env.HOME, "Library", "Application Support",
"iPhone Simulator");
glob(basePath + "/*.*", function (err, pathList) {
if (err) {
var msg = "Could not find any directories for the iOS Simulator";
logger.error(msg);
cb(new Error(msg));
} else {
cb(null, pathList);
}
});
};
exports.getAppiumConfig = function () {
var configPath = path.resolve(__dirname, "..", ".appiumconfig.json");
var config
+14
View File
@@ -545,6 +545,20 @@ var args = [
, nargs: 0
}],
[['--isolate-sim-device'], {
defaultValue: false
, dest: 'isolateSimDevice'
, action: 'storeTrue'
, required: false
, help: "Xcode 6 has a bug on some platforms where a certain simulator " +
"can only be launched without error if all other simulator devices " +
"are first deleted. This option causes Appium to delete all " +
"devices other than the one being used by Appium. Note that this " +
"is a permanent deletion, and you are responsible for using simctl " +
"or xcode to manage the categories of devices used with Appium."
, nargs: 0
}],
[['--tmp'], {
defaultValue: null
, dest: 'tmpDir'
+1 -1
View File
@@ -11,7 +11,7 @@
"firefoxos",
"testing"
],
"version": "1.2.3",
"version": "1.3.0-beta1",
"author": "appium-discuss@googlegroups.com",
"licenses": [
{
+12 -10
View File
@@ -152,19 +152,21 @@ reset_ios() {
set +e
sdk_ver=$(xcrun --sdk iphonesimulator --show-sdk-version 2>/dev/null)
sdk_status=$?
ios7_active=true
if [ $sdk_status -gt 0 ] || [[ "$sdk_ver" != "7."* ]]; then
echo "--------------------------------------------------"
echo "WARNING: you do not appear to have iOS7 SDK active"
echo "--------------------------------------------------"
ios7_active=false
ios7_active=false
ios8_active=false
if [[ "$sdk_ver" == "7."* ]]; then
ios7_active=true
elif [[ "$sdk_ver" == "8."* ]]; then
ios8_active=true
fi
if [ $sdk_status -gt 0 ] || (! $ios7_active && ! $ios8_active); then
echo "---------------------------------------------------"
echo "WARNING: you do not appear to have iOS7/8 SDK active"
echo "---------------------------------------------------"
fi
set -e
echo "* Setting iOS config to Appium's version"
run_cmd "$grunt" setConfigVer:ios
echo "* Installing ios-sim-locale"
run_cmd rm -f build/ios-sim-locale
run_cmd cp assets/ios-sim-locale build/ios-sim-locale
echo "* Cloning/updating udidetect"
run_cmd git submodule update --init submodules/udidetect
echo "* Building udidetect"
@@ -186,7 +188,7 @@ reset_ios() {
echo "* Cloning/npm linking appium-adb"
run_cmd ./bin/npmlink.sh -l appium-adb
fi
if $ios7_active ; then
if $ios7_active || $ios8_active ; then
if $hardcore ; then
echo "* Clearing out old UICatalog download"
run_cmd rm -rf ./sample-code/apps/UICatalog/
+3
View File
@@ -8,3 +8,6 @@ Here's a list of things that aren't tested in this suite:
* android with --udid (even if emulator)
* --avd / avdName
* sudo grunt authorize
* non-default --tmp
* --full-reset
* --isolate-sim-device
@@ -1,25 +0,0 @@
"use strict";
var setup = require("../../common/setup-base")
, desired = require("./desired");
describe("apidemos - stability @skip-ci", function () {
var runs = 20;
var test = function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
it('should complete successfully', function (done) {
driver
.elementByName('Animation')
.nodeify(done);
});
};
for (var n = 0; n < runs; n++) {
describe('burn-in test ' + n, test);
}
});
+7 -2
View File
@@ -20,11 +20,16 @@ function log(data) {
}
var waitForLaunch = function (app, extraArgs, done) {
var args = [".", "-p", crazyPort, "-l", "-dd", "-r", "3", "-lt", JSON.stringify(env.LAUNCH_TIMEOUT)];
var args = [".", "-p", crazyPort, "-l", "-dd", "-r", "5", "-lt", JSON.stringify(env.LAUNCH_TIMEOUT), "--platform-version", env.CAPS.platformVersion];
if (app) {
args = args.concat(["--app", app]);
if (app === "safari") {
args = args.concat(["--browser-name", "safari"]);
} else {
args = args.concat(["--app", app]);
}
}
args = args.concat(extraArgs);
log("Launching appium with args: " + JSON.stringify(args));
var proc = spawn('node', args, {cwd: path.resolve(__dirname, "..", "..", "..")});
proc.stdout.setEncoding('utf8');
proc.stderr.setEncoding('utf8');
@@ -22,7 +22,7 @@ module.exports = function (desired) {
return;
}
return driver
.execute("mobile: leaveWebView")
.context('NATIVE_APP')
.title()
.should.be.rejectedWith(/status: 13/);
}).nodeify(done);
+46 -17
View File
@@ -6,14 +6,16 @@ var setup = require("../common/setup-base")
, fs = require('fs')
, path = require('path')
, Readable = require('stream').Readable
, iOSSettings = require('../../../lib/devices/ios/settings.js')
, Simulator = require('../../../lib/devices/ios/simulator.js')
, xcode = require('../../../lib/devices/ios/xcode.js')
, exec = require('child_process').exec
, getSimUdid = require('../../helpers/sim-udid.js').getSimUdid
, Unzip = require('unzip');
describe('file movements - pullFile and pushFile', function () {
var driver;
var desired = {
app: getAppPath('testapp')
app: getAppPath('TestApp')
};
setup(this, desired).then(function (d) { driver = d; });
@@ -54,20 +56,47 @@ describe('file movements - pullFile and pushFile', function () {
var fullPath = "";
before(function (done) {
var pv = env.CAPS.platformVersion || '7.1';
var simRoots = iOSSettings.getSimRootsWithVersion(pv);
if (simRoots.length < 1) {
return done(new Error("Didn't find any simulator directories"));
}
var basePath = path.resolve(simRoots[0], 'Applications')
.replace(/\s/g, '\\ ');
var findCmd = 'find ' + basePath + ' -name "testapp.app"';
exec(findCmd, function (err, stdout) {
var ios8 = parseFloat(pv) >= 8;
xcode.getiOSSDKVersion(function (err, sdk) {
if (err) return done(err);
if (!stdout) return done(new Error("Could not find testapp.app"));
var appRoot = stdout.replace(/\n$/, '');
fullPath = path.resolve(appRoot, fileName);
fs.writeFile(fullPath, fileContent, done);
var next = function (udid) {
var sim = new Simulator({
platformVer: pv,
sdkVer: sdk,
udid: udid
});
var simRoots = sim.getDirs();
if (simRoots.length < 1) {
return done(new Error("Didn't find any simulator directories"));
}
var basePath;
if (ios8) {
// ios8 apps are stored in a different directory structure, need
// to navigate down a few more here
basePath = path.resolve(simRoots[0], 'Containers', 'Bundle',
'Application');
} else {
basePath = path.resolve(simRoots[0], 'Applications');
}
basePath = basePath.replace(/\s/g, '\\ ');
var findCmd = 'find ' + basePath + ' -name "TestApp.app"';
exec(findCmd, function (err, stdout) {
if (err) return done(err);
if (!stdout) return done(new Error("Could not find testapp.app"));
var appRoot = stdout.replace(/\n$/, '');
fullPath = path.resolve(appRoot, fileName);
fs.writeFile(fullPath, fileContent, done);
});
};
if (parseFloat(sdk) >= 8) {
getSimUdid('6', sdk, env.CAPS, function (err, udid) {
if (err) return done(err);
next(udid);
});
} else {
next();
}
});
});
after(function (done) {
@@ -78,7 +107,7 @@ describe('file movements - pullFile and pushFile', function () {
}
});
it('should be able to fetch a file from the app directory', function (done) {
var arg = path.resolve('/testapp.app', fileName);
var arg = path.resolve('/TestApp.app', fileName);
driver
.pullFile(arg)
.then(function (data) {
@@ -92,7 +121,7 @@ describe('file movements - pullFile and pushFile', function () {
it('should pull all the files in Library/AddressBook', function (done) {
var entryCount = 0;
driver.pullFolder('Library/AddressBook')
.then( function (data) {
.then(function (data) {
var zipStream = new Readable();
zipStream._read = function noop() {};
zipStream
@@ -17,12 +17,14 @@ describe("prefs @skip-ios6", function () {
setup(this, desired).then(function (d) { driver = d; });
it('should turn off autocomplete', function (done) {
var ios7 = env.DEVICE.indexOf("7") !== -1;
var ios7up = ["ios7", "ios71", "ios8"].indexOf(env.DEVICE) !== -1;
var ios8 = env.DEVICE === "ios8";
var switchEl;
driver
.elementsByClassName("UIATableCell").at(ios7 ? 0 : 1).click()
.elementsByClassName("UIATableCell").at(ios7up ? 0 : 1).click()
.sleep(1000)
.elementsByClassName("UIATableCell").at(ios7 ? 3 : 1).click()
.elementsByClassName("UIATableCell").at(ios7up ? (ios8 ? 2 : 3) : 1)
.click()
.elementByXPath('//UIASwitch[@name="Auto-Correction"]')
.then(function (el) { switchEl = el; return el; })
.getValue().then(function (checked) {
@@ -0,0 +1,22 @@
"use strict";
var setup = require("../../common/setup-base")
, chai = require('chai');
chai.should();
var desired = {
bundleId: 'com.apple.Preferences'
};
describe('settings app @skip-ios6 @skip-ios7', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
it('should launch via bundle id', function (done) {
driver
.source()
.should.eventually.contain("Settings")
.nodeify(done);
});
});
@@ -1,6 +1,11 @@
"use strict";
var _ = require('underscore');
var _ = require('underscore')
, env = require('../../../helpers/env.js')
, xcode = require('../../../../lib/devices/ios/xcode.js')
, Simulator = require('../../../../lib/devices/ios/simulator.js')
, settingsPlists = require('../../../../lib/devices/ios/settings.js')
, getSimUdid = require('../../../helpers/sim-udid').getSimUdid;
exports.ios6 = function (driver, setting, expected, cb) {
driver
@@ -20,29 +25,49 @@ exports.ios6 = function (driver, setting, expected, cb) {
}).nodeify(cb);
};
exports.ios7 = function (setting, expected, cb) {
var ios7up = function (version, udid, setting, expected, cb) {
var settingsSets;
var foundSettings;
try {
var settingsPlists = require('../../../../lib/devices/ios/settings.js');
settingsSets = settingsPlists.getSettings('7', 'mobileSafari');
} catch (e) {
return cb(e);
}
_.size(settingsSets).should.be.above(0);
for (var i = 0; i < settingsSets.length; i++) {
xcode.getiOSSDKVersion(function (err, sdk) {
var sim = new Simulator({
platformVer: env.CAPS.platformVersion,
sdkVer: sdk,
udid: udid
});
if (err) return cb(err);
try {
foundSettings.push(settingsSets[i][setting]);
settingsSets = settingsPlists.getSettings(sim, 'mobileSafari');
} catch (e) {
return cb(e);
}
}
if (settingsSets.length > 0) {
console.log("More than one safari settings set found, a failure here " +
"might not be accurate");
}
for (i = 0; i < settingsSets.length; i++) {
foundSettings[i].should.eql(expected);
}
cb();
_.size(settingsSets).should.be.above(0);
for (var i = 0; i < settingsSets.length; i++) {
try {
foundSettings.push(settingsSets[i][setting]);
} catch (e) {
return cb(e);
}
}
if (settingsSets.length > 0) {
console.log("More than one safari settings set found, a failure here " +
"might not be accurate");
}
for (i = 0; i < settingsSets.length; i++) {
foundSettings[i].should.eql(expected);
}
cb();
});
};
exports.ios7up = function (desired, setting, expected, cb) {
xcode.getiOSSDKVersion(function (err, sdk) {
if (parseFloat(sdk) >= 8) {
getSimUdid('6', sdk, desired, function (err, udid) {
if (err) return cb(err);
ios7up(desired.platformVersion, udid, setting, expected, cb);
});
} else {
ios7up(desired.platformVersion, null, setting, expected, cb);
}
});
};
@@ -10,13 +10,13 @@ var desired = {
app: 'settings'
};
describe("prefs @skip-ios7 @skip-ios6", function () {
// TODO: check why it fails on ios7
describe("prefs @skip-ios6 @skip-ios7 @skip-ios8", function () {
// on ios8 sim it crashes when tapping on "location services"
// TODO: cannot install settings app on ios6
var checkLocServ = function (driver, expected, cb) {
driver
.elementsByClassName('UIATableCell').at(2).click()
.elementsByClassName('UIATableCell').at(1).click()
.sleep(1000)
.elementByClassName('UIATableCell').click()
.elementByClassName('UIASwitch')
@@ -36,11 +36,11 @@ if (env.IOS6) {
});
});
});
} else if (env.IOS7) {
describe('safari ios7 prefs @skip-ci', function () {
} else if (env.IOS7 || env.IOS8) {
describe('safari ios7/8 prefs @skip-ci', function () {
// TODO modify the test to enable ci, right know it is checking a local file,
// not gonna work with sauce
var checkSafariSetting = require('./check-safari-settings').ios7;
var checkSafariSetting = require('./check-safari-settings').ios7up;
describe('using safariAllowPopups', function () {
var driver;
@@ -48,7 +48,9 @@ if (env.IOS6) {
.then(function (d) { driver = d; });
it('should respond to cap when true', function (done) {
checkSafariSetting('WebKitJavaScriptCanOpenWindowsAutomatically', true, done);
checkSafariSetting(driver._origCaps,
'WebKitJavaScriptCanOpenWindowsAutomatically',
true, done);
});
});
@@ -58,7 +60,9 @@ if (env.IOS6) {
.then(function (d) { driver = d; });
it('should respond to cap when false', function (done) {
checkSafariSetting('WebKitJavaScriptCanOpenWindowsAutomatically', false, done);
checkSafariSetting(driver._origCaps,
'WebKitJavaScriptCanOpenWindowsAutomatically',
false, done);
});
});
});
@@ -36,11 +36,11 @@ if (env.IOS6) {
});
});
});
} else if (env.IOS7) {
describe('safari ios7 prefs @skip-ci', function () {
} else if (env.IOS7 || env.IOS8) {
describe('safari ios7/8 prefs @skip-ci', function () {
// TODO modify the test to enable ci, right know it is checking a local file,
// not gonna work with sauce
var checkSafariSetting = require('./check-safari-settings').ios7;
var checkSafariSetting = require('./check-safari-settings').ios7up;
describe('using safariIgnoreFraudWarning', function () {
var driver;
@@ -48,7 +48,8 @@ if (env.IOS6) {
.then(function (d) { driver = d; });
it('should respond to cap when true', function (done) {
checkSafariSetting('WarnAboutFraudulentWebsites', false, done);
checkSafariSetting(driver._origCaps, 'WarnAboutFraudulentWebsites',
false, done);
});
});
@@ -58,8 +59,9 @@ if (env.IOS6) {
.then(function (d) { driver = d; });
it('should respond to cap when false', function (done) {
checkSafariSetting('WarnAboutFraudulentWebsites', true, done);
checkSafariSetting(driver._origCaps, 'WarnAboutFraudulentWebsites',
true, done);
});
});
});
}
}
@@ -19,7 +19,11 @@ describe('safari - webview - special capabilities', function () {
loadWebView(specialCaps, driver).nodeify(done);
});
it('should not display a phishing warning with safariIgnoreFraudWarning @skip-chrome', function (done) {
// iOS8 currently does not disable the phishing warning for foo:bar@ type
// addresses, even when running the sim manually
// TODO: find another way to trigger the phishing warning that IS disabled
// by the pref on iOS8
it('should not display a phishing warning with safariIgnoreFraudWarning @skip-chrome @skip-ios8', function (done) {
var titleToBecomeRight = new ChaiAsserter(function (driver) {
return driver
.title()
@@ -23,7 +23,9 @@ describe('safari - windows and frames (' + env.DEVICE + ') @skip-ios6"', functio
.should.be.rejectedWith(/status: 23/)
.nodeify(done);
});
it("should be able to open and close windows", function (done) {
it("should be able to open and close windows @skip-ios8", function (done) {
// unfortunately, iOS8 doesn't respond to the close() method on window
// the way iOS7 does
driver
.elementById('blanklink').click()
.then(function () { return spinTitle("I am another page title", driver); })
@@ -13,8 +13,8 @@ describe('testapp - autoAcceptAlerts cap = ""', function () {
setup(self, caps).then(function (d) { driver = d; });
it('does not auto-accept alerts', function (done) {
driver.elementsByClassName('UIAButton')
.then(function (buttons) { return buttons[3].click(); })
driver
.elementByAccessibilityId('show alert').click()
.sleep(2000)
.alertText()
.should.eventually.exist
@@ -13,8 +13,8 @@ describe('testapp - autoAcceptAlerts cap = false', function () {
setup(self, caps).then(function (d) { driver = d; });
it('does not auto-accept alerts', function (done) {
driver.elementsByClassName('UIAButton')
.then(function (buttons) { return buttons[3].click(); })
driver
.elementByAccessibilityId('show alert').click()
.sleep(2000)
.alertText()
.should.eventually.exist
@@ -31,8 +31,8 @@ describe('testapp - autoAcceptAlerts cap = "false"', function () {
setup(self, caps).then(function (d) { driver = d; });
it('does not auto-accept alerts', function (done) {
driver.elementsByClassName('UIAButton')
.then(function (buttons) { return buttons[3].click(); })
driver
.elementByAccessibilityId('show alert').click()
.sleep(2000)
.alertText()
.should.eventually.exist
@@ -13,8 +13,8 @@ describe('testapp - autoAcceptAlerts cap = true', function () {
setup(self, caps).then(function (d) { driver = d; });
it('auto-accepts alerts', function (done) {
driver.elementsByClassName('UIAButton')
.then(function (buttons) { return buttons[3].click(); })
driver
.elementByAccessibilityId('show alert').click()
.sleep(2000)
.alertText()
.should.be.rejectedWith(/status: 27/)
@@ -31,8 +31,8 @@ describe('testapp - autoAcceptAlerts cap = "true"', function () {
setup(self, caps).then(function (d) { driver = d; });
it('auto-accepts alerts', function (done) {
driver.elementsByClassName('UIAButton')
.then(function (buttons) { return buttons[3].click(); })
driver
.elementByAccessibilityId('show alert').click()
.sleep(2000)
.alertText()
.should.be.rejectedWith(/status: 27/)
@@ -52,7 +52,7 @@ describe('testapp - basics - calc app 2', function () {
});
// TODO: Fails on sauce, investigate
it('should be able to get syslog logs @skip-ios6 @skip-ci', function (done) {
it('should be able to get syslog logs @skip-ios6 @skip-ios8 @skip-ci', function (done) {
driver
.setImplicitWaitTimeout(4000)
.elementByName('SumLabelz')
+42 -24
View File
@@ -1,41 +1,59 @@
"use strict";
var setup = require("../../common/setup-base"),
_ = require('underscore'),
var _ = require('underscore'),
initSession = require('../../../helpers/session').initSession,
should = require('chai').should(),
desired = require('./desired');
require('../../../helpers/setup-chai.js');
describe('testapp - device', function () {
describe('target actions', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
it("should die in background and respond within (+/- 6 secs)", function (done) {
var before;
driver
.sleep(5000)
.then(function () { before = new Date().getTime() / 1000; })
.backgroundApp(1)
.catch(function (err) {
err.cause.value.message.should.contain("Instruments died");
throw err;
}).should.be.rejectedWith(/status: 13/)
.then(function () { ((new Date().getTime() / 1000) - before).should.be.below(10); })
.sleep(5000) // cooldown
.nodeify(done);
});
});
describe('deviceName @skip-ios6', function () {
describe('invalid deviceName @skip-ios6', function () {
var newDesired = _.extend(_.clone(desired), {deviceName: "iFailure 3.5-inch"});
var session = initSession(newDesired, {'no-retry': true});
it('should fail gracefully with an invalid deviceName', function (done) {
session.setUp()
.should.be.rejectedWith(/environment you requested was unavailable/)
.should.eventually.be.rejectedWith(/environment you requested was unavailable/)
.nodeify(done);
});
});
_.each(['iPhone', 'iPad'], function (device) {
describe('generic ' + device + ' deviceName @skip-ios6', function () {
var newDesired = _.extend(_.clone(desired), {
deviceName: device + " Simulator"
});
var session = initSession(newDesired, {'no-retry': true});
after(function (done) {
session.tearDown(this.currentTest.state === 'passed').nodeify(done);
});
it('should work with a generic ' + device + ' deviceName', function (done) {
session.setUp()
.nodeify(done);
});
});
});
describe("real device", function () {
var newDesired = _.extend(_.clone(desired), {
deviceName: "BadSimulator",
udid: "12341234123412341234"
});
var session = initSession(newDesired, {'no-retry': true});
it("shouldn't try to validate against sims", function (done) {
session.setUp()
.nodeify(function (err) {
should.exist(err);
var data = JSON.parse(err.data);
data.value.origValue.should.not.contain("BadSimulator");
err.message.should.contain("requested was unavailable");
done();
});
});
});
});
@@ -129,14 +129,4 @@ describe('testapp - find element', function () {
driver.elementsByName('AppElem').should.eventually.have.length(3)
.nodeify(done);
});
it('should find an element within its parent', function (done) {
// there are 2 textfields with the same name imbricated.
driver
.elementById('TextField2').then(function (parent) {
parent.should.exist;
return parent.elementById('TextField2').should.eventually.exist;
})
.nodeify(done);
});
});
@@ -11,7 +11,7 @@ describe("testapp - keyboard stability @skip-ci", function () {
var driver;
setup(this, _.defaults({
deviceName: 'iPad Simulator',
deviceName: 'iPad 2',
}, desired)).then(function (d) { driver = d; });
var test = function () {
@@ -7,7 +7,7 @@ var env = require('../../../../helpers/env'),
rimraf = require('rimraf'),
path = require('path');
describe('localization - calendarFormat', function () {
describe('localization - calendarFormat @skip-ios8', function () {
this.timeout(env.MOCHA_INIT_TIMEOUT);
after(function () {
@@ -19,7 +19,7 @@ describe('localization - calendarFormat', function () {
describe('changing to gregorian calendar', function () {
var driver;
setup(this, _.defaults({calendarFormat: 'gregorian'} , desired)).then(function (d) { driver = d; });
setup(this, _.defaults({calendarFormat: 'gregorian'}, desired)).then(function (d) { driver = d; });
it('should have gregorian calendar format', function (done) {
driver.execute('$.mainApp().preferencesValueForKey("AppleLocale");')
@@ -30,7 +30,7 @@ describe('localization - calendarFormat', function () {
describe('changing to buddhist calendar', function () {
var driver;
setup(this, _.defaults({calendarFormat: 'buddhist'} , desired)).then(function (d) { driver = d; });
setup(this, _.defaults({calendarFormat: 'buddhist'}, desired)).then(function (d) { driver = d; });
it('should have buddhist calendar format', function (done) {
driver.execute('$.mainApp().preferencesValueForKey("AppleLocale");')
@@ -7,7 +7,7 @@ var env = require('../../../../helpers/env'),
rimraf = require('rimraf'),
path = require('path');
describe('localization - language', function () {
describe('localization - language @skip-ios8', function () {
this.timeout(env.MOCHA_INIT_TIMEOUT);
after(function () {
@@ -19,7 +19,7 @@ describe('localization - language', function () {
describe('changing to fr', function () {
var driver;
setup(this, _.defaults({language: 'fr'} , desired)).then(function (d) { driver = d; });
setup(this, _.defaults({language: 'fr'}, desired)).then(function (d) { driver = d; });
it('should be fr', function (done) {
driver.execute('$.mainApp().preferencesValueForKey("AppleLanguages")[0];')
@@ -30,7 +30,7 @@ describe('localization - language', function () {
describe('changing to de', function () {
var driver;
setup(this, _.defaults({language: 'de'} , desired)).then(function (d) { driver = d; });
setup(this, _.defaults({language: 'de'}, desired)).then(function (d) { driver = d; });
it('should be de', function (done) {
driver.execute('$.mainApp().preferencesValueForKey("AppleLanguages")[0];')
@@ -20,7 +20,9 @@ describe('localization - locale', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
it('should be english', function (done) {
it('should be english @skip-ios8', function (done) {
// ios8 doesn't do default locale; it will be whatever was last in
// the sim
driver.execute('$.mainApp().preferencesValueForKey("AppleLocale");')
.should.become('en_US')
.nodeify(done);
@@ -29,7 +31,7 @@ describe('localization - locale', function () {
describe('changing locale', function () {
var driver;
setup(this, _.defaults({locale: 'fr'} , desired)).then(function (d) { driver = d; });
setup(this, _.defaults({locale: 'fr'}, desired)).then(function (d) { driver = d; });
it('should be fr', function (done) {
driver.execute('$.mainApp().preferencesValueForKey("AppleLocale");')
@@ -40,7 +42,7 @@ describe('localization - locale', function () {
describe('changing back', function () {
var driver;
setup(this, _.defaults({locale: 'en_US'} , desired)).then(function (d) { driver = d; });
setup(this, _.defaults({locale: 'en_US'}, desired)).then(function (d) { driver = d; });
it('should be en', function (done) {
driver.execute('$.mainApp().preferencesValueForKey("AppleLocale");')
@@ -16,7 +16,8 @@ describe('testapp - location - 1 @skip-ci', function () {
it('should return the right x/y coordinates', function (done) {
driver
.elementByClassName('UIAButton').getLocation().then(function (location) {
location.x.should.equal(94);
var possibleLocs = [94, 110]; // 110 is for iphone 6
possibleLocs.should.contain(parseInt(location.x, 10));
location.y.should.be.above(120);
})
.nodeify(done);
+2 -2
View File
@@ -11,8 +11,8 @@ describe('testapp - size', function () {
it('should return the right element size', function (done) {
driver.elementByClassName('UIAButton').getSize().then(function (size) {
size.width.should.eql(113);
size.height.should.eql(37);
size.width.should.be.above(112);
size.height.should.be.above(36);
}).nodeify(done);
});
+13 -6
View File
@@ -3,19 +3,26 @@
var setup = require("../../common/setup-base")
, xpath = require("xpath")
, XMLDom = require("xmldom").DOMParser
, ChaiAsserter = require("../../../helpers/asserter").ChaiAsserter
, desired = require('./desired');
var sourceIsRight = function (driver) {
return new ChaiAsserter(function () {
return driver.source().then(function (source) {
var dom = new XMLDom().parseFromString(source);
var nodes = xpath.select('//UIAButton', dom);
nodes.length.should.equal(7);
});
});
};
describe('testapp - source', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
it('should return page source', function (done) {
driver
.source()
.then(function (source) {
var dom = new XMLDom().parseFromString(source);
var nodes = xpath.select('//UIAButton', dom);
nodes.length.should.equal(7);
}).nodeify(done);
.waitFor(sourceIsRight(driver), 3000)
.nodeify(done);
});
});
@@ -1,3 +1,5 @@
/* global should:true */
"use strict";
var setup = require("../../../common/setup-base"),
@@ -11,23 +13,40 @@ describe('testapp - timeout', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
var impWaitCheck = function (impWaitMs) {
var impWaitCheck = function (strat, sel, impWaitMs) {
return function () {
var before = new Date().getTime();
return driver
.elementsByClassName('UIANotGonnaBeThere').then(function (missing) {
var after = new Date().getTime();
(after - before).should.be.below(impWaitMs + 2000);
(after - before).should.be.above(impWaitMs);
missing.should.have.length(0);
});
var method = driver['elementsBy' + strat](sel);
return method.then(function (missing) {
var after = new Date().getTime();
(after - before).should.be.below(impWaitMs + 2000);
(after - before).should.be.above(impWaitMs);
missing.should.have.length(0);
});
};
};
var impWaitCheckSingle = function (strat, sel, impWaitMs) {
return function () {
var before = new Date().getTime();
var method = driver['elementBy' + strat](sel);
return method.then(function (missing) {
should.not.exist(missing);
}).catch(function (err) {
err.status.should.equal(7);
var after = new Date().getTime();
(after - before).should.be.below(impWaitMs + 2000);
(after - before).should.be.above(impWaitMs);
});
};
};
it('should set the implicit wait for finding elements', function (done) {
driver
.setImplicitWaitTimeout(4000)
.then(impWaitCheck(4000))
.then(impWaitCheck('ClassName', 'UIANotGonnaBeHere', 4000))
.then(impWaitCheck('AccessibilityId', 'FoShoIAintHere', 4000))
.then(impWaitCheckSingle('AccessibilityId', 'FoShoIAintHere', 4000))
.nodeify(done);
});
@@ -35,7 +54,7 @@ describe('testapp - timeout', function () {
driver
.setCommandTimeout(5000)
.setImplicitWaitTimeout(10000)
.then(impWaitCheck(10000))
.then(impWaitCheck('ClassName', 'UIANotGonnaBeHere', 10000))
.nodeify(done);
});
@@ -43,10 +62,10 @@ describe('testapp - timeout', function () {
driver
.setCommandTimeout(60000)
.setImplicitWaitTimeout(4000)
.then(impWaitCheck(4000))
.then(impWaitCheck('ClassName', 'UIANotGonnaBeHere', 4000))
.resetApp()
.sleep(3000) // cooldown
.then(impWaitCheck)
.then(impWaitCheck('ClassName', 'UIANotGonnaBeHere', 4000))
.nodeify(done);
});
});
@@ -12,7 +12,7 @@ var options = {
'force new connection': true
};
describe('uicatalog - alerts @skip-ios7', function () {
describe('uicatalog - alerts @skip-ios7up', function () {
var alertTag = env.IOS7 ? '@label' : '@value';
var driver;
@@ -4,7 +4,7 @@ var env = require('../../../helpers/env')
, setup = require("../../common/setup-base")
, desired = require('./desired');
describe('uicatalog - basic @skip-ios7', function () {
describe('uicatalog - basic @skip-ios7up', function () {
describe('api', function () {
var driver;
@@ -34,7 +34,7 @@ describe('uicatalog - basic @skip-ios7', function () {
.nodeify(done);
});
it('should confirm element is selected @skip-ios7', function (done) {
it('should confirm element is selected @skip-ios7up', function (done) {
driver
.elementByXPath("//UIATableCell/UIAStaticText[contains(@label, 'Pickers')]")
.click()
@@ -4,7 +4,7 @@ var env = require('../../../helpers/env')
, setup = require("../../common/setup-base")
, desired = require('./desired');
describe('uicatalog - controls @skip-ios7', function () {
describe('uicatalog - controls @skip-ios7up', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -4,7 +4,7 @@ var env = require('../../../helpers/env'),
setup = require("../../common/setup-base"),
desired = require('./desired');
describe('uicatalog - device @skip-ios7', function () {
describe('uicatalog - device @skip-ios7up', function () {
describe('lock device', function () {
var driver;
@@ -3,7 +3,7 @@
var setup = require("../../common/setup-base"),
desired = require('./desired');
describe('uicatalog - execute @skip-ios7', function () {
describe('uicatalog - execute @skip-ios7up', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -6,7 +6,7 @@ var setup = require("../../common/setup-base")
, _ = require("underscore")
, spinWait = require("../../../helpers/spin.js").spinWait;
describe('uicatalog - find element @skip-ios7', function () {
describe('uicatalog - find element @skip-ios7up', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -11,9 +11,9 @@ var env = require('../../../helpers/env')
var SLOW_DOWN_MS = 1000;
describe('uicatalog - gestures @skip-ios7', function () {
describe('uicatalog - gestures @skip-ios7up', function () {
describe('flick @skip-ios7', function () {
describe('flick @skip-ios7up', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -75,7 +75,7 @@ describe('uicatalog - gestures @skip-ios7', function () {
}).nodeify(done);
});
});
describe('swipe gesture @skip-ios7', function () {
describe('swipe gesture @skip-ios7up', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -163,7 +163,7 @@ describe('uicatalog - gestures @skip-ios7', function () {
});
});
describe("flick element @skip-ios7", function () {
describe("flick element @skip-ios7up", function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -232,7 +232,7 @@ describe('uicatalog - gestures @skip-ios7', function () {
}).nodeify(done);
});
});
describe("swipe element @skip-ios7", function () {
describe("swipe element @skip-ios7up", function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -314,7 +314,7 @@ describe('uicatalog - gestures @skip-ios7', function () {
})
.nodeify(done);
});
it('should work in relative units @skip-ios7', function (done) {
it('should work in relative units @skip-ios7up', function (done) {
var tapOpts = {
tapCount: 1 // how many taps
, duration: 2.3 // how long
@@ -330,7 +330,7 @@ describe('uicatalog - gestures @skip-ios7', function () {
})
.nodeify(done);
});
it('should work with default options @skip-ios7', function (done) {
it('should work with default options @skip-ios7up', function (done) {
driver
.execute("mobile: tap")
.elementByClassName('UIATextView').text()
@@ -387,7 +387,7 @@ describe('uicatalog - gestures @skip-ios7', function () {
});
});
describe('scroll to element @skip-ios7', function () {
describe('scroll to element @skip-ios7up', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -4,7 +4,7 @@ var env = require('../../../../helpers/env')
, setup = require("../../../common/setup-base")
, path = require('path');
describe('uicatalog - basic @skip-ios7', function () {
describe('uicatalog - basic @skip-ios7up', function () {
describe('load zipped app', function () {
var driver;
@@ -2,7 +2,7 @@
var setup = require("../../../common/setup-base");
describe('uicatalog - basic @skip-ios7', function () {
describe('uicatalog - basic @skip-ios7up', function () {
describe('load zipped app with relative path', function () {
var driver;
@@ -2,7 +2,7 @@
var setup = require("../../../common/setup-base");
describe('uicatalog - basic @skip-ios7', function () {
describe('uicatalog - basic @skip-ios7up', function () {
describe('load zipped app via url', function () {
var driver;
@@ -3,7 +3,7 @@
var setup = require("../../common/setup-base"),
desired = require('./desired');
describe('uicatalog - move @skip-ios7', function () {
describe('uicatalog - move @skip-ios7up', function () {
describe('moveTo and click', function () {
var driver;
@@ -3,7 +3,7 @@
var setup = require("../../common/setup-base"),
desired = require('./desired');
describe('uicatalog - reset @skip-ios7', function () {
describe('uicatalog - reset @skip-ios7up', function () {
describe('app reset', function () {
var driver;
@@ -3,7 +3,7 @@
var setup = require("../../common/setup-base"),
desired = require('./desired');
describe('uicatalog - contexts @skip-ios7', function () {
describe('uicatalog - contexts @skip-ios7up', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
+16 -16
View File
@@ -11,18 +11,18 @@ describe('uicatalog - basic @skip-ios6', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
// if (env.FAST_TESTS) {
// beforeEach(function (done) {
// driver
// .back()
// .nodeify(function () { done(); });
// });
// }
if (env.FAST_TESTS) {
beforeEach(function (done) {
driver
.back()
.nodeify(function () { done(); });
});
}
it('should confirm element is not visible', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(" + textTag + ", 'Buttons')]").click()
.elementByXPath("//UIAButton[contains(@name, 'UINavigationBarBackIndicatorDefault')]")
.elementByXPath("//UIANavigationBar/UIAImage")
.isDisplayed()
.should.not.eventually.be.ok
.nodeify(done);
@@ -36,20 +36,20 @@ describe('uicatalog - basic @skip-ios6', function () {
.nodeify(done);
});
it('should confirm element is selected @skip-ios7', function (done) {
// TODO: review select implementation for ios7
it('should confirm element is selected', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@label, 'Pickers')]").click()
.elementByXPath("//UIAButton[contains(@label, 'UIPicker')]").isSelected()
.execute("mobile: scroll", {direction: 'down'})
.elementByXPath("//UIAStaticText[contains(@label, 'Switches')]").click()
.elementByClassName("UIASwitch").isSelected()
.should.eventually.be.ok
.nodeify(done);
});
it('should confirm element is not selected returns false @skip-ios7', function (done) {
// TODO: review select implementation for ios7
it('should confirm element is not selected returns false', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@label, 'Pickers')]").click()
.elementByXPath("//UIAButton[contains(@label, 'Custom')]").isSelected()
.execute("mobile: scroll", {direction: 'down'})
.elementByXPath("//UIAStaticText[contains(@label, 'Switches')]").click()
.elementByClassName("UIASwitch").click().isSelected()
.should.not.eventually.be.ok
.nodeify(done);
});
+6 -3
View File
@@ -16,7 +16,8 @@ describe('uicatalog - clear @skip-ios6', function () {
it('should be able to hide keyboard with the default strategy', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@name, 'Web View')]").moveTo(10, 10).click()
.execute("mobile: scroll", {direction: 'down'})
.elementByXPath("//UIAStaticText[contains(@name, 'Web View')]").click()
.waitForElementByXPath('//UIANavigationBar[@name="Web View"]', 5000, 500)
.elementByXPath("//UIATextField").click()
.waitForElementByAccessibilityId("Go", 5000, 500)
@@ -29,7 +30,8 @@ describe('uicatalog - clear @skip-ios6', function () {
it('should be able to hide keyboard with the tapOutside strategy', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@name, 'Web View')]").moveTo(10, 10).click()
.execute("mobile: scroll", {direction: 'down'})
.elementByXPath("//UIAStaticText[contains(@name, 'Web View')]").click()
.waitForElementByXPath('//UIANavigationBar[@name="Web View"]', 5000, 500)
.elementByXPath("//UIATextField").click()
.waitForElementByAccessibilityId("Go", 5000, 500)
@@ -42,7 +44,8 @@ describe('uicatalog - clear @skip-ios6', function () {
it('should be able to hide keyboard with the tapOut strategy', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@name, 'Web View')]").moveTo(10, 10).click()
.execute("mobile: scroll", {direction: 'down'})
.elementByXPath("//UIAStaticText[contains(@name, 'Web View')]").click()
.waitForElementByXPath('//UIANavigationBar[@name="Web View"]', 5000, 500)
.elementByXPath("//UIATextField").click()
.waitForElementByAccessibilityId("Go", 5000, 500)
@@ -24,11 +24,11 @@ describe('uicatalog - controls @skip-ios6', function () {
.then(function (wheel) {
return wheel
.getAttribute("value")
.should.become("65. 14 of 52")
.should.eventually.contain("65")
.then(function () {
return wheel.type("70")
.getAttribute("value")
.should.become("70. 15 of 52");
.should.eventually.contain("70");
});
})
.elementByXPath("//UIAPickerWheel[@name = 'Green color component value']")
@@ -36,14 +36,14 @@ describe('uicatalog - controls @skip-ios6', function () {
return wheel
.type("70")
.getAttribute("value")
.should.become("70. 15 of 52");
.should.eventually.contain("70");
})
.elementByXPath("//UIAPickerWheel[@name = 'Blue color component value']")
.then(function (wheel) {
return wheel
.type("70")
.getAttribute("value")
.should.become("70. 15 of 52");
.should.eventually.contain("70");
})
.elementByClassName("UIAPickerWheel")
.getAttribute("values")
@@ -59,7 +59,11 @@ describe('uicatalog - controls @skip-ios6', function () {
.getAttribute("value").should.become('42%')
.then(function () {
return slider.sendKeys(0.8).getAttribute("value")
.should.become('80%');
.then(function (val) {
var numeric = parseInt(val.replace('%', ''));
numeric.should.be.above(75);
numeric.should.be.below(85);
});
})
;
}).nodeify(done);
@@ -1,6 +1,7 @@
"use strict";
var setup = require("../../common/setup-base")
, env = require("../../../helpers/env.js")
, desired = require('./desired')
, _ = require('underscore');
@@ -8,44 +9,44 @@ describe('uicatalog - find - basics @skip-ios6', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
it('should find a single element by id @skip-ios7', function (done) {
// ById is not avalable in ios7
it('should find a single element by id', function (done) {
driver
.elementById('Date Picker')
.should.eventually.exist
.nodeify(done);
});
it('should find a single element by id wrapped in array for multi @skip-ios7', function (done) {
// ById is not avalable in ios7
it('should find a single element by id wrapped in array for multi', function (done) {
driver
.elementsById('Date Picker')
.elementsById('Back')
.then(function (els) {
els.length.should.equal(1);
})
.nodeify(done);
});
it('should find a single element using elementByName @skip-ios7', function (done) {
it('should find a single element using elementByAccessibilityId', function (done) {
var axId = env.IOS8 ? 'AAPLImageViewController' :
'Image View, AAPLImageViewController';
driver
.elementByName('Image View, AAPLImageViewController')
.elementByAccessibilityId(axId)
.then(function (el) {
el.should.exist;
}).nodeify(done);
});
it('should find an element within descendants @skip-ios7', function (done) {
it('should find an element within descendants', function (done) {
driver
.elementByXPath("//UIATableCell[@name = 'Buttons, AAPLButtonViewController']")
.elementByXPath("//UIATableCell[contains(@name, 'Buttons')]")
.then(function (el) {
el.should.exist;
return el.elementByClassName('UIAStaticText').getAttribute('name')
.should.become("Buttons, AAPLButtonViewController");
.should.eventually.contain("Buttons");
}).nodeify(done);
});
it('should not find an element not within itself', function (done) {
driver
.elementByXPath("//UIATableCell[@name = 'Buttons, AAPLButtonViewController']")
.elementByXPath("//UIATableCell[contains(@name, 'Buttons')]")
.then(function (el) {
el.should.exist;
return el.elementByClassName('UIANavigationBar')
@@ -54,18 +55,19 @@ describe('uicatalog - find - basics @skip-ios6', function () {
});
it('should find some elements within itself', function (done) {
var elLength = env.IOS8 ? 2 : 1;
driver
.elementByXPath("//UIATableCell[@name = 'Buttons, AAPLButtonViewController']")
.elementByXPath("//UIATableCell[contains(@name, 'Buttons')]")
.then(function (el) {
el.should.exist;
return el.elementsByClassName('UIAStaticText')
.should.eventually.have.length(1);
.should.eventually.have.length(elLength);
}).nodeify(done);
});
it('should not find elements not within itself', function (done) {
driver
.elementByXPath("//UIATableCell[@name = 'Buttons, AAPLButtonViewController']")
.elementByXPath("//UIATableCell[contains(@name, 'Buttons')]")
.then(function (el) {
el.should.exist;
el.elementsByClassName('UIANavigationBar')
@@ -124,72 +126,71 @@ describe('uicatalog - find - basics @skip-ios6', function () {
driver.clickButton('UICatalog')
.nodeify(done);
});
var axIdExt = env.IOS8 ? '' : ', AAPLActionSheetViewController';
it('should find only one textfield', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@label,'Action Sheets')]").click()
.elementByName('Okay / Cancel')
.elementByAccessibilityId("Action Sheets" + axIdExt).click()
.elementByAccessibilityId('Okay / Cancel')
.elementsByClassName('>', 'UIAStaticText')
.should.eventually.have.length(1)
.nodeify(done);
});
});
describe('findElement(s) containing name', function () {
after(function (done) {
describe('findElement(s) containing accessibility id', function () {
afterEach(function (done) {
driver
.elementByName('UICatalog').click()
.clickButton('UICatalog')
.sleep(1000)
.nodeify(done);
});
var axIdExt = env.IOS8 ? '' : ', AAPLActionSheetViewController';
it('should find one element', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@label,'Action Sheets')]").click()
.elementByName('*Okay*').getAttribute('name')
.elementByAccessibilityId("Action Sheets" + axIdExt).click()
.elementByAccessibilityId('Okay / Cancel').getAttribute('name')
.should.become('Okay / Cancel')
.nodeify(done);
});
it('should find several element', function (done) {
it('should find several elements', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@label,'Action Sheets')]").click()
.elementsByName('*Okay*')
.elementByAccessibilityId("Action Sheets" + axIdExt).click()
.elementsByAccessibilityId('Okay / Cancel')
.should.eventually.have.length(2)
.nodeify(done);
});
});
describe('duplicate text field', function () {
after(function (done) {
afterEach(function (done) {
driver
.elementByName('UICatalog').click()
.clickButton('UICatalog')
.sleep(1000)
.nodeify(done);
});
it('should find only one text field', function (done) {
var axIdExt = env.IOS8 ? '' : ', AAPLTextFieldViewController';
it('should find only one element per text field', function (done) {
driver
.waitForElementByName('*Text Fields*', 3000, 500).click()
.execute("mobile: scroll", {direction: 'down'})
.waitForElementByAccessibilityId('Text Fields' + axIdExt, 3000, 500)
.click()
.sleep(2000)
.elementByName('Empty list')
.elementByClassName('>','UIATableCell')
.elementsByClassName('>','UIATextField')
.should.eventually.have.length(1)
.elementsByClassName('UIATextField')
.should.eventually.have.length(4)
.nodeify(done);
});
it('should find only one secure text field', function (done) {
it('should find only one element per secure text field', function (done) {
driver
.waitForElementByName('*Text Fields*', 3000, 500).click()
.waitForElementByAccessibilityId('Text Fields' + axIdExt, 3000, 500)
.click()
.sleep(2000)
.elementByName('Empty list')
.elementsByClassName('>','UIATableCell').at(2)
.elementsByClassName('>','UIASecureTextField')
.should.eventually.have.length(1)
.elementsByClassName('UIASecureTextField')
.should.eventually.have.length(1)
.nodeify(done);
});
});
});
@@ -1,6 +1,7 @@
"use strict";
var setup = require("../../common/setup-base")
, env = require('../../../helpers/env.js')
, desired = require('./desired');
describe('uicatalog - find by accessibility id @skip-ios6', function () {
@@ -11,37 +12,12 @@ describe('uicatalog - find by accessibility id @skip-ios6', function () {
driver.clickButton('UICatalog')
.nodeify(done);
});
it('should find an element', function (done) {
driver.element('accessibility id', 'UICatalog').then(function (el) {
el.should.exist;
}).nodeify(done);
});
it('should find a deeply nested element', function (done) {
it('should find an element by name beneath another element', function (done) {
var axIdExt = env.IOS8 ? '' : ', AAPLActionSheetViewController';
driver
.elementByXPath("//UIAStaticText[contains(@label,'Action Sheets')]").click()
.element('accessibility id', 'Okay / Cancel').then(function (el) {
el.should.exist;
})
.nodeify(done);
});
it('should find an element by name beneath another element @skip-ios7', function (done) {
// TODO: this does not work
driver
.elementByXPath("//UIAStaticText[contains(@label,'Action Sheets')]").click()
.element('accessibility id', 'Empty list').then(function (el) {
el.element('accessibility id', 'Other')
.then(function (innerEl) {
innerEl.should.exist;
})
.elementByAccessibilityId("UICatalog").click()
.elementByAccessibilityId("Action Sheets" + axIdExt)
.nodeify(done);
});
});
it('should return an array of one element if the plural "elements" is used', function (done) {
driver
.elementByXPath("//UIAStaticText[contains(@label,'Action Sheets')]").click()
.elements('accessibility id', 'Okay / Cancel').then(function (els) {
els.length.should.equal(2);
})
.nodeify(done);
});
});
@@ -2,6 +2,7 @@
var setup = require("../../common/setup-base")
, desired = require('./desired')
, env = require('../../../helpers/env.js')
, Q = require("q")
, _ = require("underscore")
, filterVisible = require('../../../helpers/ios-uiautomation').filterVisible;
@@ -36,7 +37,7 @@ describe('uicatalog - find by ios-ui-automation @skip-ios6', function () {
.should.eventually.have.length(2)
.nodeify(done);
});
it('should use raw selector code if selector doesn\'t start with a dot' , function (done) {
it('should use raw selector code if selector doesn\'t start with a dot', function (done) {
driver
.elements(byUIA, '$.mainWindow().elements()').then(filterDisplayed)
.should.eventually.have.length(2)
@@ -47,9 +48,10 @@ describe('uicatalog - find by ios-ui-automation @skip-ios6', function () {
.should.become('UICatalog')
.nodeify(done);
});
it('should get a single element', function (done) {
it('should get a single element with non-zero index', function (done) {
var variableName = env.IOS8 ? '' : 'Empty list';
driver.element(byUIA, '.elements()[1]').getAttribute('name')
.should.become('Empty list')
.should.become(variableName)
.nodeify(done);
});
it('should get single element as array', function (done) {
@@ -61,8 +63,7 @@ describe('uicatalog - find by ios-ui-automation @skip-ios6', function () {
it('should find elements by index multiple times', function (done) {
driver
.element(byUIA, '.elements()[1].cells()[2]').getAttribute('name')
// TODO: the cells index looks wrong, should start at 1, investigate
.should.become('Alert Views, AAPLAlertViewController')
.should.eventually.contain('Alert Views')
.nodeify(done);
});
it('should find elements by name', function (done) {
@@ -70,10 +71,9 @@ describe('uicatalog - find by ios-ui-automation @skip-ios6', function () {
.should.become('UICatalog')
.nodeify(done);
});
it('should find elements by name and index', function (done) {
driver.element(byUIA, '.elements()["Empty list"].cells()[3]').getAttribute('name')
// TODO: the cells index looks wrong, should start at 1, investigate
.should.become('Buttons, AAPLButtonViewController')
it('should find elements by type and index', function (done) {
driver.element(byUIA, '.navigationBar().elements()[1]').getAttribute('name')
.should.become('Back')
.nodeify(done);
});
describe('start from a given context instead of root target', function () {
@@ -86,9 +86,10 @@ describe('uicatalog - find by ios-ui-automation @skip-ios6', function () {
});
});
it('should find elements by name', function (done) {
var axIdExt = env.IOS8 ? "" : ", AAPLButtonViewController";
driver.element(byUIA, '.elements()[1]').then(function (el) {
el
.element(byUIA, '.elements()["Buttons, AAPLButtonViewController"]')
.element(byUIA, '.elements()["Buttons' + axIdExt + '"]')
.should.eventually.exist
.nodeify(done);
});
@@ -6,7 +6,7 @@ var env = require('../../../../helpers/env')
var SLOW_DOWN_MS = 1000;
describe('uicatalog - gestures - flick @skip-ios7 @skip-ios6', function () {
describe('uicatalog - gestures - flick @skip-ios8 @skip-ios7 @skip-ios6', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -22,11 +22,11 @@ describe('uicatalog - gestures - flick @skip-ios7 @skip-ios6', function () {
it('should work via webdriver method', function (done) {
driver
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location1) {
return driver
.flick(0, -100, false)
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location2) {
location2.x.should.equal(location1.x);
location2.y.should.not.equal(location1.y);
@@ -35,11 +35,11 @@ describe('uicatalog - gestures - flick @skip-ios7 @skip-ios6', function () {
});
it('should work via mobile only method', function (done) {
driver
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location1) {
return driver
.execute("mobile: flick", [{endX: 0, endY: 0}])
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location2) {
location2.x.should.equal(location1.x);
location2.y.should.not.equal(location1.y);
@@ -56,11 +56,11 @@ describe('uicatalog - gestures - flick @skip-ios7 @skip-ios6', function () {
it('should work via mobile only method with percentage', function (done) {
var opts = {startX: 0.75, startY: 0.75, endX: 0.25, endY: 0.25};
driver
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location1) {
return driver
.execute("mobile: flick", [opts])
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location2) {
location2.x.should.equal(location1.x);
location2.y.should.not.equal(location1.y);
@@ -11,12 +11,12 @@ describe('uicatalog - gestures - mobile scroll @skip-ios6', function () {
var firstEl, location1, location2;
driver
.elementByClassName('UIATableCell')
.then(function (el) { firstEl = el; return el.getLocation(); })
.then(function (el) { firstEl = el; return el.getLocationInView(); })
.then(function (loc) { location1 = loc; })
.then(function () {
return driver.execute("mobile: scroll", [{direction: 'down'}]);
})
.then(function () { return firstEl.getLocation(); })
.then(function () { return firstEl.getLocationInView(); })
.then(function (loc2) {
location2 = loc2;
loc2.x.should.equal(location1.x);
@@ -25,7 +25,7 @@ describe('uicatalog - gestures - mobile scroll @skip-ios6', function () {
.then(function () {
return driver.execute("mobile: scroll", [{direction: 'up'}]);
})
.then(function () { return firstEl.getLocation(); })
.then(function () { return firstEl.getLocationInView(); })
.then(function (loc3) {
loc3.x.should.equal(location2.x);
loc3.y.should.not.equal(location2.y);
@@ -38,12 +38,12 @@ describe('uicatalog - gestures - mobile scroll @skip-ios6', function () {
table_view = el;
})
.elementByClassName('UIATableCell')
.then(function (el) { firstEl = el; return el.getLocation(); })
.then(function (el) { firstEl = el; return el.getLocationInView(); })
.then(function (loc) { location1 = loc; })
.then(function () {
return driver.execute("mobile: scroll", [{element: table_view.value, direction: 'down'}]);
})
.then(function () { return firstEl.getLocation(); })
.then(function () { return firstEl.getLocationInView(); })
.then(function (loc2) {
location2 = loc2;
loc2.x.should.equal(location1.x);
@@ -52,7 +52,7 @@ describe('uicatalog - gestures - mobile scroll @skip-ios6', function () {
.then(function () {
return driver.execute("mobile: scroll", [{element: table_view.value, direction: 'up'}]);
})
.then(function () { return firstEl.getLocation(); })
.then(function () { return firstEl.getLocationInView(); })
.then(function (loc3) {
loc3.x.should.equal(location2.x);
loc3.y.should.not.equal(location2.y);
@@ -1,36 +1,23 @@
"use strict";
var env = require('../../../../helpers/env')
, setup = require("../../../common/setup-base")
var setup = require("../../../common/setup-base")
, desired = require('../desired');
var SLOW_DOWN_MS = 1000;
describe('uicatalog - gestures - scroll to el @skip-ios7 @skip-ios6', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
if (env.FAST_TESTS) {
afterEach(function (done) {
driver
.flick(0, 100, false)
.flick(0, 100, false)
.sleep(SLOW_DOWN_MS)
.nodeify(done);
});
}
it('should bring the element into view', function (done) {
var el, scrollOpts, location1;
driver.elementsByClassName('UIATableCell').then(function (els) {
el = els[10];
scrollOpts = { element: el.value };
})
.then(function () { return el.getLocation(); })
.then(function () { return el.getLocationInView(); })
.then(function (loc) { location1 = loc; })
.then(function () {
return driver.execute("mobile: scrollTo", [scrollOpts]);
}).then(function () { return el.getLocation(); })
}).then(function () { return el.getLocationInView(); })
.then(function (location2) {
location2.x.should.equal(location1.x);
location2.y.should.not.equal(location1.y);
@@ -14,18 +14,23 @@ describe("uicatalog - gestures - swipe element @skip-ios7 @skip-ios6", function
afterEach(function (done) {
driver
.elementByClassName("UIASlider")
.then(function (el) { if (el) return el.sendKeys(0.5); })
.clickBack()
.then(function (el) { if (el) return el.sendKeys(0.42); })
.clickButton('UICatalog')
.sleep(SLOW_DOWN_MS)
.nodeify(done);
});
}
var getNumericValue = function (pctVal) {
pctVal = pctVal.replace("%", "");
pctVal = parseInt(pctVal, 10);
return pctVal;
};
it("slider value should change", function (done) {
var valueBefore, slider;
driver
.elementsByClassName('UIATableCell').then(function (els) { return els[1]; })
.click()
.elementByAccessibilityId('Sliders').click()
.elementByClassName("UIASlider").then(function (el) { slider = el; })
.then(function () { return slider.getAttribute("value"); })
.then(function (value) { valueBefore = value; })
@@ -36,15 +41,17 @@ describe("uicatalog - gestures - swipe element @skip-ios7 @skip-ios6", function
})
.then(function () { return slider.getAttribute("value"); })
.then(function (valueAfter) {
valueBefore.should.equal("50%");
valueAfter.should.equal("20%");
valueBefore.should.equal("42%");
valueAfter = getNumericValue(valueAfter);
// should be around 20
valueAfter.should.be.above(15);
valueAfter.should.be.below(25);
}).nodeify(done);
});
it("slider value should change by pixels", function (done) {
var valueBefore, slider;
driver
.elementsByClassName('UIATableCell').then(function (els) { return els[1]; })
.click()
.elementByAccessibilityId('Sliders').click()
.elementByClassName("UIASlider").then(function (el) { slider = el; })
.then(function () { return slider.getAttribute("value"); })
.then(function (value) { valueBefore = value; })
@@ -54,8 +61,10 @@ describe("uicatalog - gestures - swipe element @skip-ios7 @skip-ios6", function
})
.then(function () { return slider.getAttribute("value"); })
.then(function (valueAfter) {
valueBefore.should.equal("50%");
valueAfter.should.equal("5%");
valueBefore.should.equal("42%");
valueAfter = getNumericValue(valueAfter);
// should be around 5
valueAfter.should.be.below(8);
}).nodeify(done);
});
});
@@ -7,7 +7,7 @@ var env = require('../../../../helpers/env')
var SLOW_DOWN_MS = 1000;
describe('uicatalog - gestures - swipe @skip-ios7 @skip-ios6', function () {
describe('uicatalog - gestures - swipe @skip-ios8 @skip-ios7 @skip-ios6', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
@@ -22,12 +22,12 @@ describe('uicatalog - gestures - swipe @skip-ios7 @skip-ios6', function () {
}
it('should work with wd function in pixels', function (done) {
driver
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location1) {
return spinWait(function () {
return driver
.flick(0, -70, true)
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location2) {
((location2.x === location1.x) &&
(location2.y !== location1.y)
@@ -39,13 +39,13 @@ describe('uicatalog - gestures - swipe @skip-ios7 @skip-ios6', function () {
});
it('should work with wd function in percent', function (done) {
driver
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location1) {
return driver
.flick(0, -0.1, true) // flaky
.flick(0, -0.1, true)
.flick(0, -0.1, true)
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location2) {
location2.x.should.equal(location1.x);
location2.y.should.not.equal(location1.y, '===y');
@@ -55,12 +55,12 @@ describe('uicatalog - gestures - swipe @skip-ios7 @skip-ios6', function () {
it('should work with mobile function in pixels', function (done) {
var opts = {startX: 50, startY: 400, endX: 50, endY: 300, duration: 2};
driver
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location1) {
return spinWait(function () {
return driver
.execute("mobile: swipe", [opts])
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location2) {
location2.x.should.equal(location1.x);
location2.y.should.not.equal(location1.y);
@@ -71,12 +71,12 @@ describe('uicatalog - gestures - swipe @skip-ios7 @skip-ios6', function () {
it('should work with mobile function in percent', function (done) {
var opts = {startX: 0.5, startY: 0.9, endX: 0.5, endY: 0.7, duration: 2};
driver
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location1) {
return spinWait(function () {
return driver
.execute("mobile: swipe", [opts])
.elementByClassName('UIATableCell').getLocation()
.elementByClassName('UIATableCell').getLocationInView()
.then(function (location2) {
location2.x.should.equal(location1.x);
location2.y.should.not.equal(location1.y);
@@ -8,7 +8,7 @@ var env = require('../../../../helpers/env')
describe('uicatalog - load app with absolute path @skip-ios6', function () {
var driver;
var appPath = path.resolve(env.SAUCE? '/Users/chef/appium' : process.cwd(), desired.app);
var appPath = path.resolve(env.SAUCE ? '/Users/chef/appium' : process.cwd(), desired.app);
setup(this, _.defaults({'app': appPath}, desired))
.then(function (d) { driver = d; });
@@ -6,7 +6,7 @@ var env = require('../../../../helpers/env')
describe('uicatalog - load zipped app @skip-ios6', function () {
var driver;
var appZip = path.resolve(env.SAUCE? '/Users/chef/appium' : process.cwd(), 'assets/UICatalog6.0.app.zip');
var appZip = path.resolve(env.SAUCE ? '/Users/chef/appium' : process.cwd(), 'assets/UICatalog7.1.app.zip');
setup(this, {app: appZip})
.then(function (d) { driver = d; });
@@ -4,7 +4,7 @@ var setup = require("../../../common/setup-base");
describe('uicatalog - load zipped app with relative path @skip-ios6', function () {
var driver;
var appZip = "assets/UICatalog6.0.app.zip";
var appZip = "assets/UICatalog7.1.app.zip";
setup(this, {app: appZip})
.then(function (d) { driver = d; });
@@ -4,13 +4,13 @@ var setup = require("../../../common/setup-base");
describe('uicatalog - load zipped app via url @skip-ios6', function () {
var driver;
var appUrl = 'http://appium.s3.amazonaws.com/UICatalog6.0.app.zip';
var appUrl = 'http://appium.s3.amazonaws.com/WebViewApp7.1.app.zip';
setup(this, {app: appUrl})
.then(function (d) { driver = d; });
it('should load a zipped app via url', function (done) {
driver
.elementByClassName('UIATableView')
.elementByClassName('UIAWebView')
.should.eventually.exist
.nodeify(done);
});
@@ -7,8 +7,8 @@ var env = require('../../../helpers/env'),
describe('uicatalog - lock device @skip-ios6', function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
var allowance = env.IOS7 ? 5 : 2;
it("should lock the device for 4 of seconds (+/- " + allowance + " secs)", function (done) {
var allowance = (env.IOS7 || env.IOS8) ? 9 : 2;
it("should lock the device for 4 seconds (+/- " + allowance + " secs)", function (done) {
var before = new Date().getTime() / 1000;
driver
.lockDevice(4)
+4 -2
View File
@@ -1,6 +1,7 @@
"use strict";
var setup = require("../../common/setup-base"),
env = require("../../../helpers/env.js"),
desired = require('./desired');
describe('uicatalog - move @skip-ios6', function () {
@@ -10,10 +11,11 @@ describe('uicatalog - move @skip-ios6', function () {
setup(this, desired).then(function (d) { driver = d; });
it('should be able to click on arbitrary x-y elements', function (done) {
var axIdExt = env.IOS8 ? "" : ", AAPLButtonViewController";
driver
.elementByXPath("//UIAStaticText[contains(@name, 'Buttons')]").moveTo(10, 10).click()
.elementByAccessibilityId("Buttons" + axIdExt).moveTo(10, 10).click()
.elementByXPath("//UIAElement['SYSTEM (CONTACT ADD)']")
.should.eventually.exist .should.eventually.exist
.should.eventually.exist.should.eventually.exist
.nodeify(done);
});
});
@@ -1,26 +0,0 @@
"use strict";
var setup = require("../../common/setup-base")
, desired = require("./desired");
describe("uicatalog - stability @skip-ci @skip-ios6", function () {
var runs = 20;
var test = function () {
var driver;
setup(this, desired).then(function (d) { driver = d; });
it('should complete successfully', function (done) {
driver
.elementByClassName('UIATableView')
.nodeify(done);
});
};
for (var n = 0; n < runs; n++) {
describe('burn-in test ' + n, test);
}
});
+1 -1
View File
@@ -64,7 +64,7 @@ describe('uicatalog - touch @skip-ios6', function () {
return action.perform();
});
})
.elementsByAccessibilityId('3')
.waitForElementsByAccessibilityId('3', 3000, 500)
.should.eventually.have.length(2)
.nodeify(done);
});
@@ -19,12 +19,6 @@ describe('webview - basics', function () {
driver.contexts().should.eventually.have.length.above(0)
.nodeify(done);
});
it('getting list twice should not crash appium', function (done) {
driver
.contexts().should.eventually.have.length.above(0)
.contexts().should.eventually.have.length.above(0)
.nodeify(done);
});
it('getting list multiple times should not crash appium', function (done) {
driver
.contexts().should.eventually.have.length.above(0)
+16 -4
View File
@@ -62,9 +62,9 @@ function iphoneOrIpadSimulator(device, version) {
case '6.1':
case '7.0':
case '7.1':
return isIpad ? 'iPad Simulator' : 'iPhone Simulator';
// case '7.1':
// return isIpad ? 'iPad Retina' : 'iPhone Retina 4-inch';
return isIpad ? 'iPad 2' : 'iPhone 5s';
case '8.0':
return isIpad ? 'iPad 2' : 'iPhone 6';
default:
throw new Error("invalid version");
}
@@ -99,6 +99,15 @@ switch (env.DEVICE) {
, app: process.env.APP ? path.resolve(__dirname, "../../sample-code/apps/" + process.env.APP + "/build/Release-iphonesimulator/" + process.env.APP + ".app") : ''
};
break;
case 'ios8':
case 'ios8_iphone':
case 'ios8_ipad':
env.CAPS = {
browserName: ''
, deviceName: iphoneOrIpadSimulator(env.DEVICE, "8.0")
, app: process.env.APP ? path.resolve(__dirname, "../../sample-code/apps/" + process.env.APP + "/build/Release-iphonesimulator/" + process.env.APP + ".app") : ''
};
break;
case 'android':
env.CAPS = {
browserName: ''
@@ -132,12 +141,13 @@ env.IOS = env.DEVICE.match(/ios/i);
env.IOS6 = env.DEVICE.match(/ios6/i);
env.IOS7 = env.DEVICE.match(/ios7/i);
env.IOS71 = env.DEVICE.match(/ios71/i);
env.IOS8 = env.DEVICE.match(/ios8/i);
env.ANDROID = env.DEVICE.match(/android/i);
env.SELENDROID = env.DEVICE.match(/selendroid/i);
// better timeout settings for 71
env.LAUNCH_TIMEOUT = process.env.LAUNCH_TIMEOUT ? JSON.parse(process.env.LAUNCH_TIMEOUT) :
(env.IOS71 ? {"global": 60000, "afterSimLaunch": 10000} : 60000);
((env.IOS71 || env.IOS8) ? {"global": 60000, "afterSimLaunch": 10000} : 60000);
env.CAPS.launchTimeout = env.LAUNCH_TIMEOUT;
@@ -157,6 +167,8 @@ if (env.VERSION) {
env.CAPS.platformVersion = "7.1";
} else if (env.IOS7) {
env.CAPS.platformVersion = "7.0";
} else if (env.IOS8) {
env.CAPS.platformVersion = "8.0";
}
// max retry
+3 -2
View File
@@ -4,10 +4,11 @@ var ChaiAsserter = require('./asserter').ChaiAsserter;
function textCleared(el) {
return new ChaiAsserter(function () {
return el.text().should.become(''); });
return el.text().should.become('');
});
}
var safeClear = function (el ,remainingAttempts) {
var safeClear = function (el, remainingAttempts) {
if (typeof remainingAttempts !== 'number') remainingAttempts = 3;
return el
.clear()
+8 -7
View File
@@ -3,7 +3,6 @@ var env = require('./env')
, wd = require("wd")
, Q = require("q")
, _ = require("underscore")
, iosReset = require('./reset').iosReset
, androidUninstall = require('./reset').androidUninstall;
require('colors');
@@ -55,6 +54,10 @@ module.exports.initSession = function (desired, opts) {
});
});
wd.addPromiseChainMethod('printSource', function () {
return this.source().then(function (s) { console.log(s); });
});
return {
setUp: function (name) {
if (env.VERBOSE) {
@@ -121,18 +124,16 @@ module.exports.initSession = function (desired, opts) {
if (env.MAX_RETRY) attempts = Math.min(env.MAX_RETRY, attempts);
return browser.chain()
.then(function () {
if (!env.SAUCE && env.IOS && env.RESET_IOS && !opts['no-reset']) {
// TODO this should not be necessary
return iosReset();
}
}).then(function () {
// if android uninstall package first
if (!env.SAUCE && desired.platformName === 'Android' && desired.appPackage) {
// TODO this should not be necessary
return androidUninstall(desired.appPackage);
}
}).then(function () { return init(attempts); })
.then(function () { initialized = true; })
.then(function () {
initialized = true;
browser._origCaps = caps;
})
.setImplicitWaitTimeout(env.IMPLICIT_WAIT_TIMEOUT);
},
tearDown: function (passed) {
+24
View File
@@ -0,0 +1,24 @@
"use strict";
var IOS = require("../../lib/devices/ios/ios")
, Instruments = require('../../lib/devices/ios/instruments');
exports.getSimUdid = function (xcodeVer, sdkVer, desired, cb) {
var opts = {
forceIphone: false,
forceIpad: false,
xcodeVersion: xcodeVer,
deviceName: desired.deviceName,
platformVersion: desired.platformVersion,
iOSSDKVersion: sdkVer
};
Instruments.getAvailableDevices(function (err, availDevices) {
if (err) return cb(err);
var dString = IOS.getDeviceStringFromOpts(opts);
var sim = IOS.getSimForDeviceString(dString, availDevices);
if (sim[1] === null) {
return cb(new Error("Could not get UDID"));
}
cb(null, sim[1]);
});
};