blob: 791d578f84d2a38f182a88d5a0367fd84119c39a [file] [log] [blame]
/* jshint node: true */
/* global navigator, document, window */
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
'use strict';
var APPIUM_SERVER_HOST = 'localhost';
var APPIUM_SERVER_PORT = 4723;
var WEBVIEW_WAIT_TIMEOUT = 5000;
var IMPLICIT_WAIT_TIMEOUT = 10000;
var ASYNC_SCRIPT_TIMEOUT = 60000;
var fs = require('fs');
var path = require('path');
var wd = global.WD || require('wd');
module.exports.getDriver = function (platform) {
var normalizedPlatform;
var driverConfig = {};
var serverConfig = {};
var driver;
switch (platform.toLowerCase()) {
case 'android':
normalizedPlatform = 'Android';
break;
case 'ios':
normalizedPlatform = 'iOS';
break;
default:
throw 'Unknown platform: ' + platform;
}
if (global.USE_SAUCE) {
serverConfig = {
host: global.SAUCE_SERVER_HOST,
port: global.SAUCE_SERVER_PORT
};
driverConfig = global.SAUCE_CAPS;
driver = global.WD.promiseChainRemote(serverConfig.host, serverConfig.port, global.SAUCE_USER, global.SAUCE_KEY);
} else {
serverConfig = {
host: APPIUM_SERVER_HOST,
port: APPIUM_SERVER_PORT
};
driverConfig = {
browserName: '',
platformName: normalizedPlatform,
platformVersion: global.PLATFORM_VERSION || '',
deviceName: global.DEVICE_NAME || '',
app: global.PACKAGE_PATH,
autoAcceptAlerts: true,
};
if (global.UDID) {
driverConfig.udid = global.UDID;
}
driver = global.WD.promiseChainRemote(serverConfig);
}
module.exports.configureLogging(driver);
return driver
.init(driverConfig)
.setImplicitWaitTimeout(IMPLICIT_WAIT_TIMEOUT);
};
module.exports.getWD = function () {
return wd;
};
module.exports.getWebviewContext = function (driver, retries) {
if (typeof retries === 'undefined') {
retries = 2;
}
return driver
.sleep(WEBVIEW_WAIT_TIMEOUT)
.contexts()
.then(function (contexts) {
// take the last webview context
for (var i = 0; i < contexts.length; i++) {
if (contexts[i].indexOf('WEBVIEW') >= 0) {
return contexts[i];
}
}
// no webview context, the app is still loading
return driver
.sleep(1000)
.then(function () {
if (retries > 0) {
console.log('No webview context. Retries remaining: ' + retries);
return driver
.then(function () {
return module.exports.getWebviewContext(driver, retries - 1);
});
}
throw 'Couldn\'t get webview context.';
});
});
};
module.exports.waitForDeviceReady = function (driver) {
return driver
.setAsyncScriptTimeout(ASYNC_SCRIPT_TIMEOUT)
.executeAsync(function (cb) {
document.addEventListener('deviceready', cb, false);
}, []);
};
module.exports.injectLibraries = function (driver) {
var q = fs.readFileSync(path.join(__dirname, '/lib/q.min.js'), 'utf8');
return driver
.execute(q)
.execute(function () {
navigator._appiumPromises = {};
}, []);
};
module.exports.configureLogging = function (driver) {
if (!global.VERBOSE) {
return;
}
driver.on('status', function (info) {
console.log(info);
});
driver.on('command', function (meth, path, data) {
console.log(' > ' + meth, path, data || '');
});
driver.on('http', function (meth, path, data) {
console.log(' > ' + meth, path, data || '');
});
};
module.exports.tapElementByXPath = function (xpath, driver) {
return driver
.waitForElementByXPath(xpath, 30000)
.getLocation()
.then(function (loc) {
if (loc.x <= 0) {
loc.x = 1;
}
if (loc.y <= 0) {
loc.y = 1;
}
loc.x = Math.floor(loc.x + 1);
loc.y = Math.floor(loc.y + 1);
var wd = module.exports.getWD();
var tapElement = new wd.TouchAction();
tapElement.tap(loc);
return driver.performTouchAction(tapElement);
});
};
module.exports.pollForEvents = function (driver, isAndroid, windowOffset) {
if (!windowOffset) {
windowOffset = 0;
}
// polling for new events
return driver
//driver.then is not a function, so
.sleep(0)
.then(function() {
if (!isAndroid) {
return driver;
}
// for some reason inappbrowser tests tend to leave an active window on android
// so for the polling to work correctly we need to
// switch back to the window where the cache is located
return driver
.windowHandles()
.then(function (windowHandles) {
if (windowOffset >= windowHandles.length) {
throw new Error('Cannot find a window with the event cache.');
}
return driver.window(windowHandles[windowOffset]);
});
})
.execute(function () {
if (typeof window._jasmineParamedicProxyCache === 'undefined') {
// wrong window
return null;
}
// get the results and clean up the cache
var result = window._jasmineParamedicProxyCache;
window._jasmineParamedicProxyCache = [];
return result;
}, [])
.then(function (result) {
if (Object.prototype.toString.call(result) === '[object Array]') {
return result;
}
if (!isAndroid) {
throw new Error('Cannot get the event cache: it doesn\'t exist in the app.');
}
// no luck finding the event cache in this window, let's try next
return module.exports.pollForEvents(driver, isAndroid, windowOffset + 1);
});
};
module.exports.addFillerImage = function (driver) {
var bitmap = fs.readFileSync(path.join(__dirname, '../cordova_logo_thumb.jpg'));
var base64str = new Buffer(bitmap).toString('base64');
return driver.executeAsync(function (b64str, cb) {
try {
window.imageSaver.saveBase64Image( {
data: b64str
}, function (fpath) {
cb(fpath);
}, function (err) {
cb('ERROR: ' + err);
});
} catch(err) {
cb('ERROR: ' + err.message);
}
}, [base64str]);
};
module.exports.deleteFillerImage = function (driver, testImagePath) {
if (!testImagePath) {
return driver;
}
return driver.executeAsync(function (testImagePath, cb) {
if (window.imageSaver) {
window.imageSaver.removeImage({
data: testImagePath
}, function () {
cb();
}, function (err) {
cb('ERROR: ' + err);
});
} else {
cb();
}
}, [testImagePath]);
};
wd.addPromiseChainMethod('getWebviewContext', function (retries) {
return module.exports.getWebviewContext(this, retries);
});
wd.addPromiseChainMethod('waitForDeviceReady', function () {
return module.exports.waitForDeviceReady(this);
});
wd.addPromiseChainMethod('injectLibraries', function () {
return module.exports.injectLibraries(this);
});
wd.addPromiseChainMethod('tapElementByXPath', function (xpath) {
return module.exports.tapElementByXPath(xpath, this);
});
wd.addPromiseChainMethod('pollForEvents', function (isAndroid) {
return module.exports.pollForEvents(this, isAndroid);
});
wd.addPromiseChainMethod('addFillerImage', function () {
return module.exports.addFillerImage(this);
});
wd.addPromiseChainMethod('deleteFillerImage', function (testImagePath) {
return module.exports.deleteFillerImage(this, testImagePath);
});