| /* 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); |
| }); |