appium-uiauto
Advanced tools
Comparing version 1.10.8 to 2.0.0-beta
// Generate a bootstrap for the UIAuto Instruments script containing | ||
// the environment variables we need. | ||
'use strict'; | ||
import path from 'path'; | ||
import _ from 'lodash'; | ||
import crypto from 'crypto'; | ||
import _mkdirp from 'mkdirp'; | ||
import { fs } from 'appium-support'; | ||
import Promise from 'bluebird'; | ||
import buildScript from './build-script'; | ||
import log from './logger'; | ||
var path = require('path'), | ||
_ = require('underscore'), | ||
crypto = require('crypto'), | ||
Q = require('q'), | ||
mkdirp = Q.denodeify(require('mkdirp')), | ||
fs = require('fs'), | ||
buildCollatedScript = require('./resolve-deps.js'), | ||
logger = require('./logger.js'); | ||
let mkdirp = Promise.promisify(_mkdirp); | ||
function getEnv(opts) { | ||
opts = opts || {}; | ||
var bootstrapEnv = { | ||
const BOOTSTRAP_JS_PATH = '../../uiauto/bootstrap.js'; | ||
const COMMAND_PROXY_CLIENT_PATH = './bin/command-proxy-client.js'; | ||
const DEFAULT_INSTRUMENTS_SOCKET = '/tmp/instruments_sock'; | ||
function getEnv (opts = {}) { | ||
// build an object with the required properties for bootstrap | ||
return { | ||
nodePath: process.execPath, | ||
commandProxyClientPath: path.resolve( | ||
__dirname, '../bin/command-proxy-client.js'), | ||
instrumentsSock: opts.sock || '/tmp/instruments_sock', | ||
__dirname, COMMAND_PROXY_CLIENT_PATH), | ||
instrumentsSock: opts.sock || DEFAULT_INSTRUMENTS_SOCKET, | ||
interKeyDelay: opts.interKeyDelay || null, | ||
@@ -28,16 +33,19 @@ justLoopInfinitely: opts.justLoopInfinitely, | ||
}; | ||
return bootstrapEnv; | ||
} | ||
function buildCode(opts) { | ||
async function buildCode (opts) { | ||
// only build the code if it hasn't been done before | ||
if (opts.code) return opts.code; | ||
var env = getEnv(opts); | ||
logger.debug('Dynamic env: ' + JSON.stringify(env)); | ||
var bootstrapJs = path.resolve(__dirname, '../uiauto/bootstrap.js'); | ||
var imports = opts.imports || {}; | ||
imports = imports.pre || []; | ||
var bootstrapCode = buildCollatedScript(bootstrapJs, imports); | ||
let env = getEnv(opts); | ||
log.debug(`Dynamic env: ${JSON.stringify(env)}`); | ||
var lines = []; | ||
let bootstrapJs = path.resolve(__dirname, BOOTSTRAP_JS_PATH); | ||
// if special imports were sent in, make use of them | ||
let imports = (opts.imports && opts.imports.pre) ? opts.imports.pre : []; | ||
let bootstrapCode = await buildScript(bootstrapJs, imports); | ||
// generate the dynamic part of the bootstrap code | ||
// with the environment set up properly | ||
let lines = []; | ||
lines.push('// This file is automatically generated. Do not manually modify!'); | ||
@@ -48,8 +56,10 @@ lines.push(''); | ||
lines.push('bootstrap({'); | ||
_(env).each(function (value, key) { | ||
// add each defined variable to the environment | ||
for (let [key, value] of _.pairs(env)) { | ||
if (!_.isUndefined(value)) { | ||
var quote = typeof value === 'string' ? '\"' : ''; | ||
lines.push(' "' + key + '": ' + quote + value + quote + ','); | ||
let quote = _.isString(value) ? '\"' : ''; | ||
lines.push(` "${key}": ${quote}${value}${quote},`); | ||
} | ||
}); | ||
} | ||
// get rid of the last comma that was added | ||
lines[lines.length - 1] = lines[lines.length - 1].replace(/,$/, ''); | ||
@@ -60,13 +70,13 @@ lines.push('});'); | ||
function computeHash(code) { | ||
return crypto.createHash('md5').update(code) | ||
.digest('hex').substring(0, 16); | ||
function computeHash (code) { | ||
return crypto | ||
.createHash('md5') | ||
.update(code) | ||
.digest('hex') | ||
.substring(0, 16); | ||
} | ||
var prepareBootstrap = function (opts) { | ||
logger.debug('Preparing uiauto bootstrap'); | ||
opts = opts || {}; | ||
function getDynamicBootstrapDir (opts = {}) { | ||
// figuring out where to store dynamic bootstrap | ||
var dynamicBootstrapDir; | ||
let dynamicBootstrapDir; | ||
if (process.env.APPIUM_BOOTSTRAP_DIR) { | ||
@@ -82,34 +92,46 @@ // mainly for test | ||
} | ||
logger.debug('Dynamic bootstrap dir: ' + dynamicBootstrapDir); | ||
return dynamicBootstrapDir; | ||
} | ||
async function writeDynamicBootstrapIfNecessary (dynamicBootstrapDir, dynamicBootstrapPath, code, hash) { | ||
await mkdirp(dynamicBootstrapDir); | ||
// check if there is existing code and it has the same hash | ||
let codeIsGood = true; | ||
try { | ||
let existingCode = await fs.readFile(dynamicBootstrapPath); | ||
codeIsGood = computeHash(existingCode) === hash; | ||
} catch (err) { | ||
codeIsGood = false; | ||
} | ||
// write file if the old code is not the same | ||
if (codeIsGood) { | ||
log.debug(`Reusing dynamic bootstrap: ${dynamicBootstrapPath}`); | ||
} else { | ||
log.debug(`Creating or overwriting dynamic bootstrap: ${dynamicBootstrapPath}`); | ||
await fs.writeFile(dynamicBootstrapPath, code, {flag: 'w+'}); | ||
} | ||
} | ||
async function prepareBootstrap (opts = {}) { | ||
log.debug('Preparing bootstrap code'); | ||
let dynamicBootstrapDir = getDynamicBootstrapDir(opts); | ||
log.debug(`Dynamic bootstrap dir: ${dynamicBootstrapDir}`); | ||
// building code and hash | ||
var code = buildCode(opts); | ||
var hash = computeHash(code); | ||
var dynamicBootstrapPath = path.resolve(dynamicBootstrapDir, | ||
'bootstrap-' + hash + '.js'); | ||
logger.debug('Dynamic bootstrap code: ' + code.split("\n")[0] + '...'); | ||
logger.debug('Dynamic bootstrap path: ' + dynamicBootstrapPath); | ||
let code = await buildCode(opts); | ||
let hash = computeHash(code); | ||
let dynamicBootstrapPath = path.resolve(dynamicBootstrapDir, | ||
`bootstrap-${hash}.js`); | ||
log.debug(`Dynamic bootstrap code: ${code.split('\n')[0]}...`); | ||
log.debug(`Dynamic bootstrap path: ${dynamicBootstrapPath}`); | ||
return mkdirp(dynamicBootstrapDir) | ||
.then(function () { | ||
// check existing code | ||
var codeIsGood = true; | ||
try { | ||
var existingCode = fs.readFileSync(dynamicBootstrapPath); | ||
codeIsGood = computeHash(existingCode) === hash; | ||
} catch (err) { | ||
codeIsGood = false; | ||
} | ||
// write file if necessary | ||
if (codeIsGood) { | ||
logger.debug('Reusing dynamic bootstrap: ' + dynamicBootstrapPath); | ||
} else { | ||
logger.debug('Creating or overwritting dynamic bootstrap: ' + | ||
dynamicBootstrapPath); | ||
fs.writeFileSync(dynamicBootstrapPath, code, { flag: 'w+'}); | ||
} | ||
return dynamicBootstrapPath; | ||
}); | ||
}; | ||
exports.prepareBootstrap = prepareBootstrap; | ||
exports.getEnv = getEnv; | ||
await writeDynamicBootstrapIfNecessary(dynamicBootstrapDir, | ||
dynamicBootstrapPath, code, hash); | ||
return dynamicBootstrapPath; | ||
} | ||
export { prepareBootstrap, getEnv }; |
@@ -1,33 +0,5 @@ | ||
'use strict'; | ||
import { getLogger } from 'appium-logger'; | ||
var winston = require('winston'), | ||
_ = require('underscore'); | ||
const log = getLogger('UIAuto'); | ||
var logger = new (winston.Logger)({ | ||
transports: [ | ||
new (winston.transports.Console)({ level : 'info' }) | ||
] | ||
}); | ||
var loggerWrap = { | ||
init: function (_logger) { | ||
logger = _logger; | ||
}, | ||
}; | ||
_(['info','debug','warn','error']).each(function (level) { | ||
loggerWrap[level] = function () { | ||
var args = Array.prototype.slice.call(arguments, 0); | ||
logger[level].apply(logger, args); | ||
}; | ||
}); | ||
loggerWrap.setConsoleLevel = function (level) { | ||
logger.transports.console.level = level; | ||
}; | ||
loggerWrap.instance = function () { | ||
return logger; | ||
}; | ||
module.exports = loggerWrap; | ||
export default log; |
{ | ||
"name": "appium-uiauto", | ||
"version": "1.10.8", | ||
"description": "appium uiauto ios driver", | ||
"main": "lib/main.js", | ||
"bin": {}, | ||
"scripts": { | ||
"test": "make && make test_uiauto" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/appium/appium-uiauto.git" | ||
}, | ||
"author": "", | ||
"description": "Appium iOS UI Automation driver", | ||
"keywords": [ | ||
"appium", | ||
"ios" | ||
], | ||
"version": "2.0.0-beta", | ||
"author": "appium", | ||
"licenses": [ | ||
@@ -21,25 +16,44 @@ { | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/appium/appium-uiauto.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/appium/appium-uiauto/issues" | ||
}, | ||
"homepage": "https://github.com/appium/appium-uiauto", | ||
"engines": [ | ||
"node" | ||
], | ||
"main": "./build/index.js", | ||
"bin": {}, | ||
"directories": { | ||
"lib": "lib" | ||
}, | ||
"dependencies": { | ||
"argparse": "~0.1.15", | ||
"mkdirp": "~0.5.0", | ||
"q": "~1.1.2", | ||
"winston": "~0.8.3" | ||
"appium-instruments": "^2.0.4", | ||
"appium-logger": "^1.1.7", | ||
"appium-support": "2.0.0-beta13", | ||
"babel-runtime": "=5.5.5", | ||
"bluebird": "^2.9.32", | ||
"lodash": "^3.10.0", | ||
"mkdirp": "^0.5.1", | ||
"source-map-support": "^0.3.1", | ||
"through": "^2.3.8" | ||
}, | ||
"scripts": { | ||
"prepublish": "./node_modules/.bin/gulp prepublish", | ||
"test": "./node_modules/.bin/gulp once", | ||
"watch": "./node_modules/.bin/gulp" | ||
}, | ||
"devDependencies": { | ||
"appium-instruments": "1.5.3", | ||
"underscore": "~1.7.0", | ||
"chai": "~1.10.0", | ||
"sinon-chai": "~2.6.0", | ||
"chai-as-promised": "~4.1.1", | ||
"jscs": "~1.8.1", | ||
"jshint": "~2.5.1", | ||
"mocha": "~2.1.0", | ||
"rimraf": "~2.2.8", | ||
"sinon": "~1.12.2", | ||
"through": "~2.3.4" | ||
"appium-gulp-plugins": "^1.2.12", | ||
"appium-xcode": "^2.0.5", | ||
"chai": "^3.0.0", | ||
"chai-as-promised": "^5.1.0", | ||
"gulp": "^3.8.11", | ||
"mochawait": "^2.0.0", | ||
"q": "^1.4.1", | ||
"sinon": "^1.15.4", | ||
"sinon-chai": "^2.8.0" | ||
} | ||
} |
@@ -1,14 +0,40 @@ | ||
appium-uiauto | ||
============= | ||
# Appium-UIAuto rewrite | ||
ui-auto driver use by Appium for Ios. | ||
`uiauto` portion will remain as is, since it is run on the device, not in our ES6+ environment. What remains consists of three major parts: | ||
## Publishing | ||
1. Command Proxy: | ||
- acts as the bridge between `Appium` and the iOS UI Automation framework | ||
- will require significant async rewriting | ||
- two ends of the equation | ||
- `lib/uiauto-client.js` - should export `UIAutoClient` | ||
- `uiauto/lib/commands.js` - server. should be named more appropriately | ||
``` | ||
npm version [patch|minor|major] | ||
# make sure everything is commited | ||
git push origin master | ||
git push --tags | ||
npm publish | ||
``` | ||
2. Dynamic Bootstrap: | ||
- creates the bootstrap environment to be pushed onto the device | ||
- currently synchronous, but uses synchronized fs functions, and returns a Promise, so modify to async so we handle things in the same way | ||
3. Dependency Resolver: | ||
- collates a dependency tree for the bootstrap app | ||
- needs to be renamed according to its functionality - `createScript` or somesuch | ||
- currently synchronous, but uses synchronized fs functions, so rewrite to async | ||
- the logic can remain the same since the code parsed is old-style | ||
And, of course... tests. | ||
This rewrite will pull in most of the iOS behavioural tests from Appium. This seems a good time to revisit our tests both in terms of organization and coverage. | ||
Dependencies for moving tests out of Appium: | ||
1. Requires a way to start/stop simulator and load app (appium-ios-simulator?). | ||
2. Requires a way to start instruments with the bootstrap code (appium-instruments?). | ||
3. Rethink our test organization | ||
- move from app-based organization to functionality-based. Find, action, etc. While it seems unlikely that we will have a new test app to test against by the time this happens, idealy at some point everything would be tested against a single app, and some of the initial complexity (e.g., having one test run against one app, and another test run against another) would go away. | ||
- This would be a good time to keep track of the desiderata for a new test app. What tests do we have that have no assertions because there is nothing appropriate to check for? What tests require going through a long setup to get to the place where the feature can be tested? Where is there unnecessary complexity that makes false negatives likely? | ||
- We currently have some cases where we test the same feature multiple times, and other features not at all. Reorganizing will allow us to merge tests, and make the holes more obvious. |
@@ -0,8 +1,10 @@ | ||
// transpile:mocha | ||
/* globals $, rootPage, alerts */ | ||
'use strict'; | ||
var base = require('./base'); | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
describe('alarm', function () { | ||
var imports = { post: [ | ||
instrumentsInstanceInit; | ||
describe('alarm', async function () { | ||
let imports = { post: [ | ||
'uiauto/lib/alerts.js', | ||
@@ -17,11 +19,12 @@ 'uiauto/lib/status.js', | ||
]}; | ||
base.globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
before(async () => { | ||
await globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
}); | ||
describe("textfields", function () { | ||
var ctx; | ||
base.instrumentsInstanceInit() | ||
.then(function (_ctx) { ctx = _ctx; }).done(); | ||
describe('textfields', async function () { | ||
let ctx; | ||
before(function () { | ||
return ctx.execFunc( | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
await ctx.execFunc( | ||
function () { | ||
@@ -33,4 +36,4 @@ alerts.configure(); | ||
afterEach(function () { | ||
return ctx.execFunc( | ||
afterEach(async () => { | ||
await ctx.execFunc( | ||
function () { | ||
@@ -41,6 +44,7 @@ $('#UICatalog').first().tap(); | ||
); | ||
await killAll(ctx); | ||
}); | ||
it('should retrieve alert text and then accept alert', function () { | ||
return ctx.execFunc( | ||
it('should retrieve alert text and then accept alert', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -55,8 +59,6 @@ rootPage.clickMenuItem('Alert Views'); | ||
} | ||
).then(function (res) { | ||
console.warn('res -->', res); | ||
res.should.include('A Short Title Is Best'); | ||
}); | ||
); | ||
res.should.include('A Short Title Is Best'); | ||
}); | ||
}); | ||
}); |
@@ -1,14 +0,14 @@ | ||
'use strict'; | ||
// 'use strict'; | ||
var chai = require('chai'), | ||
chaiAsPromised = require("chai-as-promised"), | ||
Q = require('q'), | ||
CommandProxy = require('../../lib/command-proxy'), | ||
instrumentsUtils = require('appium-instruments').utils, | ||
getEnv = require('../../lib/dynamic-bootstrap').getEnv, | ||
_ = require('underscore'), | ||
path = require('path'), | ||
fs = require('fs'), | ||
logger = require('../../lib/logger'); | ||
import chai from 'chai'; | ||
import chaiAsPromised from 'chai-as-promised'; | ||
import Promise from 'bluebird'; | ||
import { UIAutoClient, prepareBootstrap } from '../..'; | ||
import Instruments from 'appium-instruments'; | ||
import { getEnv } from '../../lib/dynamic-bootstrap'; | ||
import _ from 'lodash'; | ||
import path from 'path'; | ||
import { fs } from 'appium-support'; | ||
import logger from '../../lib/logger'; | ||
@@ -18,2 +18,3 @@ chai.use(chaiAsPromised); | ||
process.env.APPIUM_BOOTSTRAP_DIR = '/tmp/appium-uiauto/test/functional/bootstrap'; | ||
@@ -23,17 +24,17 @@ | ||
var prepareBootstrap = function (opts) { | ||
async function localPrepareBootstrap (opts) { | ||
opts = opts || {}; | ||
var rootDir = path.resolve(__dirname, '../..'); | ||
let rootDir = path.resolve(__dirname, '../../..'); | ||
if (opts.bootstrap === 'basic') { | ||
var env = getEnv(); | ||
var postImports = []; | ||
let env = getEnv(); | ||
let postImports = []; | ||
if (opts.imports && opts.imports.post) { | ||
postImports = opts.imports.post; | ||
} | ||
postImports = _(postImports).map(function (item) { | ||
return '#import "' + path.resolve( rootDir , item) + '"'; | ||
postImports = postImports.map(function (item) { | ||
return '#import "' + path.resolve(rootDir , item) + '"'; | ||
}); | ||
var code = fs.readFileSync(path.resolve( | ||
__dirname, '../../test/assets/base-bootstrap.js'), 'utf8'); | ||
_({ | ||
let code = await fs.readFile(path.resolve( | ||
__dirname, '../../../test/assets/base-bootstrap.js'), 'utf8'); | ||
let vars = { | ||
'<ROOT_DIR>': rootDir, | ||
@@ -44,6 +45,7 @@ '"<POST_IMPORTS>"': postImports.join('\n'), | ||
'<instrumentsSock>': env.instrumentsSock | ||
}).each(function (value, key) { | ||
}; | ||
for (let [key, value] of _.pairs(vars)) { | ||
code = code.replace(new RegExp(key, 'g'), value); | ||
}); | ||
return require('../../lib/dynamic-bootstrap').prepareBootstrap({ | ||
} | ||
return prepareBootstrap({ | ||
code: code, | ||
@@ -60,141 +62,101 @@ isVerbose: true | ||
delete opts.chai; | ||
return require('../../lib/dynamic-bootstrap') | ||
.prepareBootstrap(opts); | ||
return prepareBootstrap(opts); | ||
} | ||
}; | ||
} | ||
var newInstruments = function (bootstrapFile) { | ||
return instrumentsUtils.quickInstrument({ | ||
app: path.resolve(__dirname, '../assets/UICatalog.app'), | ||
async function newInstruments (bootstrapFile) { | ||
return Instruments.utils.quickInstrument({ | ||
app: path.resolve(__dirname, '../../../test/assets/UICatalog.app'), | ||
bootstrap: bootstrapFile, | ||
logger: logger.instance(), | ||
simulatorSdkAndDevice: 'iPhone 6 (8.1 Simulator)', | ||
launchTries: 2 | ||
}); | ||
}; | ||
} | ||
var init = function (bootstrapFile, opts) { | ||
var deferred = Q.defer(); | ||
var proxy = new CommandProxy(opts); | ||
var instruments; | ||
proxy.start( | ||
// first connection | ||
function (err) { | ||
instruments.launchHandler(err); | ||
if (err) return deferred.reject(err); | ||
deferred.resolve({proxy: proxy, instruments: instruments}); | ||
}, | ||
// regular cb | ||
function (err) { | ||
if (err) return deferred.reject(err); | ||
newInstruments(bootstrapFile).then(function (_instruments) { | ||
instruments = _instruments; | ||
instruments.start(null, function () { | ||
proxy.safeShutdown(function () {}); | ||
}); | ||
}) | ||
.catch(function (err) { deferred.reject(err); }) | ||
.done(); | ||
} | ||
); | ||
return deferred.promise; | ||
}; | ||
async function init (bootstrapFile, sock) { | ||
let proxy = new UIAutoClient(sock); | ||
let instruments = await newInstruments(bootstrapFile); | ||
instruments.start(null, async () => { | ||
await proxy.safeShutdown(); | ||
throw new Error('Unexpected shutdown of instruments'); | ||
}); | ||
var killAll = function (ctx) { | ||
return Q.nfcall(ctx.instruments.shutdown.bind(ctx.instruments)) | ||
.then(function () { | ||
return instrumentsUtils.killAllInstruments(); | ||
}).catch(function () {}) | ||
.then(function () { | ||
return ctx.proxy.safeShutdown(function () {}); | ||
}); | ||
}; | ||
try { | ||
await proxy.start(); | ||
instruments.launchHandler(); | ||
} catch (err) { | ||
// need to make sure instruments handles business | ||
instruments.launchHandler(err); | ||
throw err; | ||
} | ||
return {proxy, instruments}; | ||
} | ||
async function killAll (ctx) { | ||
let asyncShutdown = Promise.promisify(ctx.instruments.shutdown, ctx.instruments); | ||
try { | ||
await asyncShutdown(); | ||
} catch (e) { | ||
// pass | ||
console.log(e); | ||
} | ||
await Instruments.utils.killAllInstruments(); | ||
await ctx.proxy.safeShutdown(); | ||
} | ||
var bootstrapFile; | ||
exports.globalInit = function (ctx, opts) { | ||
ctx.timeout(180000); | ||
async function globalInit (ctx, opts) { | ||
ctx.timeout(20000); | ||
before(function () { | ||
return prepareBootstrap(opts).then(function (_bootstrapFile) { | ||
bootstrapFile = _bootstrapFile; | ||
}); | ||
}); | ||
}; | ||
bootstrapFile = await localPrepareBootstrap(opts); | ||
} | ||
exports.instrumentsInstanceInit = function (opts) { | ||
var deferred = Q.defer(); | ||
async function instrumentsInstanceInit (opts = {}) { | ||
let ctx = await init(bootstrapFile, opts.sock); | ||
ctx.sendCommand = async (cmd) => { | ||
return ctx.proxy.sendCommand(cmd); | ||
}; | ||
ctx.exec = ctx.sendCommand; | ||
var ctx; | ||
before(function () { | ||
return init(bootstrapFile, opts) | ||
.then(function (_ctx) { | ||
ctx = _ctx; | ||
ctx.sendCommand = function (cmd) { | ||
var deferred = Q.defer(); | ||
ctx.proxy.sendCommand(cmd, function (result) { | ||
if (result.status === 0) { | ||
deferred.resolve(result.value); | ||
} else { | ||
deferred.reject(JSON.stringify(result)); | ||
} | ||
}); | ||
return deferred.promise; | ||
}; | ||
ctx.execFunc = async (func, params) => { | ||
params = params || []; | ||
let script = | ||
'(function (){' + | ||
' var params = JSON.parse(\'' + JSON.stringify(params) + '\');\n' + | ||
' return (' + func.toString() + ').apply(null, params);' + | ||
'})();'; | ||
return ctx.exec(script); | ||
}; | ||
ctx.exec = ctx.sendCommand; | ||
let cmd = '$.isVerbose = ' + (process.env.VERBOSE ? true : false) + ';\n'; | ||
await ctx.exec(cmd); | ||
ctx.execFunc = function (func, params) { | ||
params = params || []; | ||
var script = | ||
'(function (){' + | ||
' var params = JSON.parse(\'' + JSON.stringify(params) + '\');\n' + | ||
' return (' + func.toString() + ').apply(null, params);' + | ||
'})();'; | ||
return ctx.exec(script); | ||
}; | ||
}).then(function () { | ||
var cmd = ''; | ||
cmd += '$.isVerbose = ' + (process.env.VERBOSE ? true : false) + ';\n'; | ||
return ctx.exec(cmd); | ||
}) | ||
.then(function () { | ||
// some uiauto helpers | ||
return ctx.execFunc(function () { | ||
/* global rootPage:true */ | ||
rootPage = {}; | ||
// click item in root page menu | ||
rootPage.clickMenuItem = function (partialText) { | ||
$.each($('tableview').children(), function (idx, child) { | ||
if (child.name().indexOf(partialText) >= 0 ){ | ||
$(child).tap(); | ||
return false; | ||
} | ||
}); | ||
}; | ||
}); | ||
}).then(function () { | ||
return ctx.execFunc(function () { | ||
/* global $ */ | ||
$.delay(500); | ||
while (!$('tableview').isVisible()) { | ||
$.warn('waiting for page to load'); | ||
$.delay(500); | ||
} | ||
}).then( | ||
function () { | ||
deferred.resolve(ctx); | ||
}, | ||
function (err) { | ||
deferred.reject(err); | ||
} | ||
); | ||
// some uiauto helpers | ||
await ctx.execFunc(function () { | ||
/* global rootPage:true */ | ||
rootPage = {}; | ||
// click item in root page menu | ||
rootPage.clickMenuItem = function (partialText) { | ||
$.each($('tableview').children(), function (idx, child) { | ||
if (child.name().indexOf(partialText) >= 0 ){ | ||
$(child).tap(); | ||
return false; | ||
} | ||
}); | ||
}; | ||
}); | ||
after(function () { | ||
return killAll(ctx); | ||
await ctx.execFunc(function () { | ||
/* global $ */ | ||
$.delay(500); | ||
while (!$('tableview').isVisible()) { | ||
$.warn('waiting for page to load'); | ||
$.delay(500); | ||
} | ||
}); | ||
return deferred.promise; | ||
}; | ||
return ctx; | ||
} | ||
export { instrumentsInstanceInit, globalInit, killAll }; |
@@ -1,32 +0,39 @@ | ||
'use strict'; | ||
// transpile:mocha | ||
var base = require('./base'); | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
describe('bootstrap', function () { | ||
describe('basic test bootstrap', function () { | ||
let ctx; | ||
before(async function () { | ||
await globalInit(this, {bootstrap: 'basic'}); | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
describe("basic test bootstrap", function () { | ||
base.globalInit(this, {bootstrap: 'basic'}); | ||
var ctx; | ||
base.instrumentsInstanceInit().then(function (_ctx) { ctx = _ctx; }); | ||
it('should start and execute one command', function () { | ||
return ctx.sendCommand("'123'") | ||
.should.become('123') | ||
.then(function () { return ctx.sendCommand("typeof $.lookup"); }) | ||
.should.become('undefined') | ||
.then(function () { return ctx.sendCommand("typeof chai"); }) | ||
.should.become('object'); | ||
it('should start and execute one command', async () => { | ||
(await ctx.sendCommand("'123'")).should.equal('123'); | ||
(await ctx.sendCommand('typeof $.lookup')).should.equal('undefined'); | ||
(await ctx.sendCommand('typeof chai')).should.equal('object'); | ||
}); | ||
}); | ||
describe("regular bootstrap without chai", function () { | ||
base.globalInit(this); | ||
var ctx; | ||
base.instrumentsInstanceInit().then(function (_ctx) { ctx = _ctx; }); | ||
describe('regular bootstrap without chai', function () { | ||
let ctx; | ||
before(async function () { | ||
await globalInit(this); | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
if (ctx) { | ||
await killAll(ctx); | ||
} | ||
}); | ||
it('should start and execute one command', function () { | ||
return ctx.sendCommand("'123'") | ||
.should.become('123') | ||
.then(function () { return ctx.sendCommand("typeof chai"); }) | ||
.should.become('undefined'); | ||
it('should start and execute one command', async () => { | ||
(await ctx.sendCommand("'123'")).should.equal('123'); | ||
(await ctx.sendCommand('typeof chai')).should.equal('undefined'); | ||
}); | ||
@@ -36,11 +43,15 @@ }); | ||
describe("regular bootstrap with chai", function () { | ||
base.globalInit(this, {chai: true}); | ||
var ctx; | ||
base.instrumentsInstanceInit().then(function (_ctx) { ctx = _ctx; }); | ||
let ctx; | ||
before(async function () { | ||
await globalInit(this, {bootstrap: 'basic'}); | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx | ||
); | ||
}); | ||
it('should start and execute one command', function () { | ||
return ctx.sendCommand("'123'") | ||
.should.become('123') | ||
.then(function () { return ctx.sendCommand("typeof chai"); }) | ||
.should.become('object'); | ||
it('should start and execute one command', async () => { | ||
(await ctx.sendCommand("'123'")).should.equal('123'); | ||
(await ctx.sendCommand('typeof chai')).should.equal('object'); | ||
}); | ||
@@ -47,0 +58,0 @@ }); |
@@ -0,42 +1,55 @@ | ||
// transpile:mocha | ||
/* globals $ */ | ||
'use strict'; | ||
var base = require('./base'), | ||
_ = require('underscore'), | ||
Q = require('q'); | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
import { getVersion } from 'appium-xcode'; | ||
import _ from 'lodash'; | ||
import Promise from 'bluebird'; | ||
describe('commands', function () { | ||
base.globalInit(this, {bootstrap: 'basic'}); | ||
let numCommands = 100; | ||
before(async () => { | ||
await globalInit(this, {bootstrap: 'basic'}); | ||
describe("simple sequences", function () { | ||
var ctx; | ||
base.instrumentsInstanceInit().then(function (_ctx) { ctx = _ctx; }); | ||
// xcode 7 is a bit slow. | ||
let xcodeVersion = await getVersion(); | ||
if (xcodeVersion[0] >= 7) { | ||
numCommands = 50; | ||
} | ||
}); | ||
it('should send one valid command returning a value', function () { | ||
return ctx.sendCommand("'123'") | ||
.should.become('123'); | ||
describe('simple sequences', function () { | ||
let ctx; | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
it('should send one valid command returning empty value', function () { | ||
return ctx.sendCommand("$.warn('starting')") | ||
.should.become(''); | ||
it('should send one valid command returning a value', async () => { | ||
(await ctx.sendCommand("'123'")).should.equal('123'); | ||
}); | ||
it('should respond to invalid command and not die', function () { | ||
return ctx.sendCommand("i_am_invalid()") | ||
.should.be.rejectedWith(/"status":17/) | ||
.then(function () { | ||
return ctx.sendCommand("$.warn('still alive')"); | ||
}); | ||
it('should send one valid command returning empty value', async () => { | ||
(await ctx.sendCommand("$.warn('starting')")).should.equal(''); | ||
}); | ||
it('should repond to 10 commands in a row', function () { | ||
var seq = []; | ||
_(10).times(function (i) { | ||
seq.push(function () { | ||
return ctx.sendCommand('(function () { return ' + i + '})()') | ||
.should.become(i); | ||
it('should respond to invalid command and not die', async () => { | ||
await ctx.sendCommand('i_am_invalid()').should.be.rejectedWith(/Can't find variable: i_am_invalid/); | ||
await ctx.sendCommand("$.warn('still alive')"); | ||
}); | ||
it('should repond to 10 commands in a row', async () => { | ||
let seq = []; | ||
_.times(10, function (i) { | ||
seq.push(async () => { | ||
(await ctx.sendCommand(`(function () { return ${i}})()`)).should.equal(i); | ||
}); | ||
}); | ||
return seq.reduce(Q.when, new Q()); | ||
await Promise.reduce(seq, async (res, task) => { | ||
await res; | ||
return task(); | ||
}, null); | ||
}); | ||
@@ -46,47 +59,54 @@ | ||
describe("sending 100 valid commands", function () { | ||
var ctx; | ||
base.instrumentsInstanceInit().then(function (_ctx) { ctx = _ctx; }); | ||
describe(`sending ${numCommands} valid commands`, () => { | ||
let ctx; | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
it('should work', function () { | ||
var seq = []; | ||
_(100).times(function (i) { | ||
seq.push(function () { | ||
return ctx.sendCommand('(function () { return "' + i + '"})()') | ||
.should.become("" + i) | ||
.then(function () { | ||
if ((i+1)%10 === 0) console.log('sent:', (i+1)); | ||
}); | ||
it('should work', async () => { | ||
let seq = []; | ||
_.times(numCommands, (i) => { | ||
seq.push(async () => { | ||
(await ctx.sendCommand(`(function () { return "${i}"})()`)).should.equal(i.toString()); | ||
// if ((i+1)%10 === 0) console.log('sent:', (i+1)); | ||
}); | ||
}); | ||
return seq.reduce(Q.when, new Q()); | ||
await Promise.reduce(seq, async (res, task) => { | ||
await res; | ||
return task(); | ||
}, null); | ||
}); | ||
}); | ||
describe("sending 100 alternating valid and invalid", function () { | ||
var ctx; | ||
base.instrumentsInstanceInit().then(function (_ctx) { ctx = _ctx; }); | ||
describe(`sending ${numCommands} alternating valid and invalid`, () => { | ||
let ctx; | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
it('should work', function () { | ||
var seq = []; | ||
_(100).times(function (i) { | ||
it('should work', async () => { | ||
let seq = []; | ||
_.times(numCommands, (i) => { | ||
if (i%2 === 0) | ||
seq.push(function () { | ||
return ctx.sendCommand('(function () { return "' + i + '"})()') | ||
.should.become("" + i) | ||
.then(function () { | ||
if ((i+1)%10 === 0) console.log('sent:', (i+1)); | ||
}); | ||
seq.push(async () => { | ||
(await ctx.sendCommand(`(function () { return "${i}"})()`)).should.equal(i.toString()); | ||
// if ((i+1)%10 === 0) console.log('sent:', (i+1)); | ||
}); | ||
else | ||
seq.push(function () { | ||
return ctx.sendCommand('(ffffunction () { return "' + i + '"})()') | ||
.should.be.rejectedWith(/"status":17/) | ||
.then(function () { | ||
if ((i+1)%10 === 0) console.log('sent:', (i+1)); | ||
}); | ||
seq.push(async () => { | ||
await ctx.sendCommand('(ffffunction () { return "' + i + '"})()') | ||
.should.be.rejectedWith(/Unexpected token/); | ||
// if ((i+1)%10 === 0) console.log('sent:', (i+1)); | ||
}); | ||
}); | ||
return seq.reduce(Q.when, new Q()); | ||
await Promise.reduce(seq, async (res, task) => { | ||
await res; | ||
return task(); | ||
}, null); | ||
}); | ||
@@ -96,8 +116,13 @@ | ||
describe("command with big result", function () { | ||
var ctx; | ||
base.instrumentsInstanceInit().then(function (_ctx) { ctx = _ctx; }); | ||
describe('command with big result', () => { | ||
let ctx; | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
// UIAuto code | ||
var configureUIAuto = function () { | ||
let configureUIAuto = () => { | ||
$.extend($, { | ||
@@ -120,3 +145,2 @@ oneMamaLongString: function (n, mapping) { | ||
oneMamaHugeTree: function (n, d) { | ||
//var root = {name: 'root'}; | ||
function addChildren(root, depth) { | ||
@@ -139,17 +163,15 @@ if (depth === d) return; | ||
before(function () { | ||
return ctx.execFunc(configureUIAuto); | ||
before(async () => { | ||
await ctx.execFunc(configureUIAuto); | ||
}); | ||
var testN = function (n) { | ||
return ctx.sendCommand('$.oneMamaLongString(' + n + ')') | ||
.then(function (s) { | ||
s.should.have.length(n); | ||
_(n).times(function (i) { | ||
parseInt(s[i] , 10).should.equal(i%10); | ||
}); | ||
}); | ||
let testN = async (n) => { | ||
let s = await ctx.sendCommand(`$.oneMamaLongString(${n})`); | ||
s.should.have.length(n); | ||
_.times(n, function (i) { | ||
parseInt(s[i] , 10).should.equal(i%10); | ||
}); | ||
}; | ||
it('should work a small string', function () { | ||
it('should work a small string', () => { | ||
return testN(1000); | ||
@@ -166,14 +188,12 @@ }); | ||
var testNWithSpaces = function (n) { | ||
return ctx.sendCommand("$.oneMamaLongString(" + n + ",[0,1,2,3,4,' ',6,7,8,9])") | ||
.then(function (s) { | ||
s.should.have.length(n); | ||
_(n).times(function (i) { | ||
if (i%10 === 5){ | ||
s[i].should.equal(' '); | ||
} else { | ||
parseInt(s[i] , 10).should.equal(i%10); | ||
} | ||
}); | ||
}); | ||
let testNWithSpaces = async (n) => { | ||
let s = await ctx.sendCommand(`$.oneMamaLongString(${n}, [0,1,2,3,4,' ',6,7,8,9])`); | ||
s.should.have.length(n); | ||
_.times(n, function (i) { | ||
if (i%10 === 5){ | ||
s[i].should.equal(' '); | ||
} else { | ||
parseInt(s[i] , 10).should.equal(i%10); | ||
} | ||
}); | ||
}; | ||
@@ -185,17 +205,20 @@ | ||
var getHugeTree = function (n,d) { | ||
return ctx.sendCommand('$.oneMamaHugeTree(' + n + ', ' + d + ')'); | ||
let getHugeTree = async (n, d) => { | ||
return ctx.sendCommand(`$.oneMamaHugeTree(${n}, ${d})`); | ||
}; | ||
it('should work with a medium tree', function () { | ||
return getHugeTree(5,3); | ||
it('should work with a medium tree', async () => { | ||
let res = await getHugeTree(5, 3); | ||
res.name.should.equal('root'); | ||
res.children.c1.children.c2.children | ||
.c3.name.should.equal('child 3'); | ||
JSON.stringify(res).length.should.be.above(4000); | ||
}); | ||
it('should work with a huge tree', function () { | ||
return getHugeTree(5,7).then(function (res) { | ||
res.name.should.equal('root'); | ||
res.children.c1.children.c2.children | ||
.c3.children.c2.name.should.equal('child 2'); | ||
JSON.stringify(res).length.should.be.above(2000000); | ||
}); | ||
it('should work with a huge tree', async () => { | ||
let res = await getHugeTree(5, 7); | ||
res.name.should.equal('root'); | ||
res.children.c1.children.c2.children | ||
.c3.children.c2.name.should.equal('child 2'); | ||
JSON.stringify(res).length.should.be.above(2000000); | ||
}); | ||
@@ -202,0 +225,0 @@ }); |
@@ -1,35 +0,35 @@ | ||
'use strict'; | ||
// transpile:mocha | ||
var base = require('./base'), | ||
path = require('path'), | ||
rimraf = require('rimraf'); | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
import path from'path'; | ||
import { rimraf } from 'appium-support'; | ||
describe('config', function () { | ||
describe("custom sock", function () { | ||
var altSockDir = '/tmp/abcd'; | ||
var altSock = path.resolve(altSockDir, 'sock'); | ||
describe('custom socket', () => { | ||
let altSockDir = '/tmp/abcd'; | ||
let altSock = path.resolve(altSockDir, 'sock'); | ||
before(function () { | ||
rimraf.sync('/tmp/abcd'); | ||
let ctx; | ||
before(async function () { | ||
await rimraf(altSockDir); | ||
await globalInit(this, { chai: true, sock: altSock }); | ||
ctx = await instrumentsInstanceInit({ sock: altSock }); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
base.globalInit(this, { chai: true, sock: altSock }); | ||
var ctx; | ||
base.instrumentsInstanceInit({ sock: altSock }) | ||
.then(function (_ctx) { ctx = _ctx; }).done(); | ||
it('should use the alternate sock', function () { | ||
ctx.proxy.should.exist; | ||
ctx.proxy.getSock().should.equal(altSock); | ||
ctx.proxy.sock.should.equal(altSock); | ||
}); | ||
it('should work', function () { | ||
return ctx.execFunc( | ||
it('should work', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
return "OK Boss"; | ||
return 'OK Boss'; | ||
} | ||
).then(function (res) { | ||
res.should.equal("OK Boss"); | ||
}); | ||
); | ||
res.should.equal('OK Boss'); | ||
}); | ||
@@ -36,0 +36,0 @@ |
@@ -0,16 +1,22 @@ | ||
// transpile:mocha | ||
/* globals $, rootPage */ | ||
'use strict'; | ||
var base = require('./base'); | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
describe('find', function () { | ||
base.globalInit(this, { chai: true }); | ||
describe('find', async () => { | ||
before(async function () { | ||
await globalInit(this, { chai: true }); | ||
}); | ||
describe("textfields", function () { | ||
var ctx; | ||
base.instrumentsInstanceInit() | ||
.then(function (_ctx) { ctx = _ctx; }).done(); | ||
describe("textfields", async function () { | ||
let ctx; | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
afterEach(function () { | ||
return ctx.execFunc( | ||
afterEach(async () => { | ||
await ctx.execFunc( | ||
function () { | ||
@@ -23,4 +29,4 @@ $('#UICatalog').first().tap(); | ||
it('should not return duplicate UIATextField', function () { | ||
return ctx.execFunc( | ||
it('should not return duplicate UIATextField', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -34,10 +40,8 @@ rootPage.clickMenuItem('Text Fields'); | ||
} | ||
).then(function (res) { | ||
console.warn('res -->', res); | ||
res.should.have.length(1); | ||
}); | ||
); | ||
res.should.have.length(1); | ||
}); | ||
it('should not return duplicate UIASecureTextField', function () { | ||
return ctx.execFunc( | ||
it('should not return duplicate UIASecureTextField', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -51,5 +55,4 @@ rootPage.clickMenuItem('Text Fields'); | ||
} | ||
).then(function (res) { | ||
res.should.have.length(1); | ||
}); | ||
); | ||
res.should.have.length(1); | ||
}); | ||
@@ -59,9 +62,13 @@ | ||
describe("byUIAutomation", function () { | ||
var ctx; | ||
base.instrumentsInstanceInit() | ||
.then(function (_ctx) { ctx = _ctx; }).done(); | ||
describe('byUIAutomation', () => { | ||
let ctx; | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
afterEach(function () { | ||
return ctx.execFunc( | ||
afterEach(async () => { | ||
await ctx.execFunc( | ||
function () { | ||
@@ -74,4 +81,4 @@ $('#UICatalog').first().tap(); | ||
it('should use global context by default', function () { | ||
return ctx.execFunc( | ||
it('should use global context by default', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -83,10 +90,9 @@ rootPage.clickMenuItem('Text Fields'); | ||
} | ||
).then(function (res) { | ||
res.should.have.length(5); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
); | ||
res.should.have.length(5); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
it('should eval the raw code if it doesn\'t start with a dot', function () { | ||
return ctx.execFunc( | ||
it('should eval the raw code if it does not start with a dot', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -98,10 +104,9 @@ rootPage.clickMenuItem('Text Fields'); | ||
} | ||
).then(function (res) { | ||
res.should.have.length(1); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
); | ||
res.should.have.length(1); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
it('should retrieve context from cache when ctx param is a string', function () { | ||
return ctx.execFunc( | ||
it('should retrieve context from cache when ctx param is a string', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -118,10 +123,9 @@ rootPage.clickMenuItem('Text Fields'); | ||
} | ||
).then(function (res) { | ||
res.should.have.length(1); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
); | ||
res.should.have.length(1); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
it('should use context when ctx param is an object', function () { | ||
return ctx.execFunc( | ||
it('should use context when ctx param is an object', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -137,10 +141,9 @@ rootPage.clickMenuItem('Text Fields'); | ||
} | ||
).then(function (res) { | ||
res.should.have.length(1); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
); | ||
res.should.have.length(1); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
it('should work when retrieving only one element', function () { | ||
return ctx.execFunc( | ||
it('should work when retrieving only one element', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -175,6 +178,5 @@ rootPage.clickMenuItem('Text Fields'); | ||
} | ||
).then(function (res) { | ||
res.should.have.length(4); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
); | ||
res.should.have.length(4); | ||
res[0].ELEMENT.should.exist; | ||
}); | ||
@@ -181,0 +183,0 @@ |
@@ -0,30 +1,47 @@ | ||
// transpile:mocha | ||
/* globals $ */ | ||
'use strict'; | ||
var base = require('./base'); | ||
describe('grace period', function () { | ||
var imports = { post: [ | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
import { getVersion } from 'appium-xcode'; | ||
describe('grace period', async () => { | ||
let imports = { post: [ | ||
'uiauto/lib/mechanic-ext/gesture-ext.js', | ||
'uiauto/lib/mechanic-ext/keyboard-ext.js', | ||
]}; | ||
base.globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
before(async function () { | ||
await globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
}); | ||
describe("looking for unexistant object", function () { | ||
var ctx; | ||
base.instrumentsInstanceInit() | ||
.then(function (_ctx) { ctx = _ctx; }).done(); | ||
describe('looking for non-existant object', async function () { | ||
let expectedTime = 2000; | ||
let ctx; | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
it('should be quick when grace period is not set', function () { | ||
var refMs = Date.now(); | ||
return ctx.execFunc( | ||
// xcode 7 is a bit slow. | ||
let xcodeVersion = await getVersion(); | ||
if (xcodeVersion[0] >= 7) { | ||
expectedTime = 4000; | ||
} | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
it('should be quick when grace period is not set', async () => { | ||
let refMs = Date.now(); | ||
let res = await ctx.execFunc( | ||
function () { | ||
return $('#not exist'); | ||
} | ||
).should.eventually.have.length(0) | ||
.then(function () { (Date.now() - refMs).should.be.below(1000); }); | ||
); | ||
(Date.now() - refMs).should.be.below(expectedTime); | ||
res.should.have.length(0); | ||
}); | ||
it('should be quick when pushing and poping 0 timeout', function () { | ||
var refMs = Date.now(); | ||
return ctx.execFunc( | ||
it('should be quick when pushing and popping 0 timeout', async () => { | ||
let refMs = Date.now(); | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -36,10 +53,11 @@ $.target().pushTimeout(0); | ||
} | ||
).should.eventually.have.length(0) | ||
.then(function () { (Date.now() - refMs).should.be.below(1000); }); | ||
); | ||
res.should.have.length(0); | ||
(Date.now() - refMs).should.be.below(expectedTime); | ||
}); | ||
// Skipping because of bug, it takes more than 25 second! | ||
it.skip('should be quick when grace period is set to 1', function () { | ||
var refMs = Date.now(); | ||
return ctx.execFunc( | ||
it.skip('should be quick when grace period is set to 1', async () => { | ||
let refMs = Date.now(); | ||
let res = await ctx.execFunc( | ||
function () { | ||
@@ -52,10 +70,7 @@ $.target().setTimeout(1); | ||
} | ||
).should.eventually.have.length(0) | ||
.then(function () { | ||
(Date.now() - refMs).should.be.below(5000); | ||
}); | ||
); | ||
res.should.have.length(0) | ||
(Date.now() - refMs).should.be.below(5000); | ||
}); | ||
}); | ||
}); |
@@ -0,9 +1,10 @@ | ||
// transpile:mocha | ||
/* globals $ */ | ||
'use strict'; | ||
var base = require('./base'), | ||
_ = require('underscore'); | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
import _ from 'lodash'; | ||
describe('keyboard', function () { | ||
var imports = { post: [ | ||
describe('keyboard', async () => { | ||
let imports = { post: [ | ||
'uiauto/lib/mechanic-ext/gesture-ext.js', | ||
@@ -13,12 +14,18 @@ 'uiauto/lib/mechanic-ext/keyboard-ext.js', | ||
]}; | ||
base.globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
before(async function () { | ||
await globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
}); | ||
describe("hide keyboard", function () { | ||
describe('hide keyboard', async function () { | ||
/* globals rootPage: true */ | ||
var ctx; | ||
base.instrumentsInstanceInit() | ||
.then(function (_ctx) { ctx = _ctx; }).done(); | ||
let ctx; | ||
before(async () => { | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
afterEach(function () { | ||
return ctx.execFunc( | ||
afterEach(async () => { | ||
await ctx.execFunc( | ||
function () { | ||
@@ -31,6 +38,5 @@ $('#UICatalog').first().tap(); | ||
_(['pressKey', 'press']).each(function (strategy) { | ||
it('should hide the keyboard by pressing the done key (' + | ||
strategy + ')', function () { | ||
return ctx.execFunc( | ||
_.each(['pressKey', 'press'], function (strategy) { | ||
it(`should hide the keyboard by pressing the done key (${strategy})`, async () => { | ||
await ctx.execFunc( | ||
function (strategy) { | ||
@@ -47,6 +53,5 @@ rootPage.clickMenuItem('Text Fields'); | ||
_(['tapOutside', 'tapOut']).each(function (strategy) { | ||
it('should hide the keyboard by tapping outside(' + | ||
strategy + ')', function () { | ||
return ctx.execFunc( | ||
_.each(['tapOutside', 'tapOut'], function (strategy) { | ||
it(`should hide the keyboard by tapping outside (${strategy})`, async () => { | ||
await ctx.execFunc( | ||
function (strategy) { | ||
@@ -63,4 +68,4 @@ rootPage.clickMenuItem('Web View'); | ||
it('should hide the keyboard with the default strategy', function () { | ||
return ctx.execFunc( | ||
it('should hide the keyboard with the default strategy', async () => { | ||
await ctx.execFunc( | ||
function () { | ||
@@ -75,5 +80,3 @@ rootPage.clickMenuItem('Web View'); | ||
}); | ||
}); | ||
}); |
@@ -0,19 +1,23 @@ | ||
// transpile:mocha | ||
/* globals $ */ | ||
'use strict'; | ||
var base = require('./base'); | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
describe('nil', function () { | ||
var imports = { post: [ | ||
describe('nil', async function () { | ||
let imports = { post: [ | ||
'uiauto/lib/element-patch/nil-patch.js', | ||
'uiauto/lib/mechanic-ext/basics-ext.js' | ||
]}; | ||
base.globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
let ctx; | ||
before(async function () { | ||
await globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
var ctx; | ||
base.instrumentsInstanceInit() | ||
.then(function (_ctx) { ctx = _ctx; }).done(); | ||
afterEach(function () { | ||
return ctx.execFunc( | ||
afterEach(async () => { | ||
await ctx.execFunc( | ||
function () { | ||
@@ -26,36 +30,28 @@ $('#UICatalog').first().tap(); | ||
it('isNil should return true for not nil elements', function () { | ||
return ctx.execFunc( | ||
it('isNil should return true for not nil elements', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
return $('cell')[0].isNil(); | ||
} | ||
).then(function (res) { | ||
console.warn('res -->', res); | ||
res.should.not.be.ok; | ||
}); | ||
); | ||
res.should.be.false; | ||
}); | ||
it('isNil should return true for nil elements', function () { | ||
return ctx.execFunc( | ||
it('isNil should return true for nil elements', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
return $('cell')[0].images().isNil(); | ||
} | ||
).then(function (res) { | ||
console.warn('res -->', res); | ||
res.should.be.ok; | ||
}); | ||
); | ||
res.should.be.true; | ||
}); | ||
it('isNil should return true for manually created UIAElementNil', function () { | ||
return ctx.execFunc( | ||
it('isNil should return true for manually created UIAElementNil', async () => { | ||
let res = await ctx.execFunc( | ||
function () { | ||
return $.nil.isNil(); | ||
} | ||
).then(function (res) { | ||
console.warn('res -->', res); | ||
res.should.be.ok; | ||
}); | ||
); | ||
res.should.be.true; | ||
}); | ||
}); |
@@ -0,21 +1,25 @@ | ||
// transpile:mocha | ||
/* globals $, env */ | ||
'use strict'; | ||
var base = require('./base'), | ||
_ = require('underscore'); | ||
import { instrumentsInstanceInit, globalInit, killAll } from './base'; | ||
import _ from 'lodash'; | ||
describe('sendKey', function () { | ||
var imports = { post: [ | ||
describe('sendKey', async function () { | ||
let imports = { post: [ | ||
'uiauto/lib/mechanic-ext/keyboard-ext.js', | ||
'uiauto/lib/element-patch/helper-patch.js' | ||
]}; | ||
base.globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
/* globals rootPage: true */ | ||
var ctx; | ||
base.instrumentsInstanceInit() | ||
.then(function (_ctx) { ctx = _ctx; }).done(); | ||
let ctx; | ||
before(async function () { | ||
await globalInit(this, { imports: imports, bootstrap: 'basic'}); | ||
ctx = await instrumentsInstanceInit(); | ||
}); | ||
after(async () => { | ||
await killAll(ctx); | ||
}); | ||
afterEach(function () { | ||
return ctx.execFunc( | ||
afterEach(async () => { | ||
await ctx.execFunc( | ||
function () { | ||
@@ -28,5 +32,6 @@ $('#UICatalog').first().tap(); | ||
_([undefined,'oneByOne', 'grouped', 'setValue']).each(function (sendKeyStrategy) { | ||
it('should work with strategy: ' + sendKeyStrategy, function () { | ||
return ctx.execFunc( | ||
let keyStrategies = [undefined, 'oneByOne', 'grouped', 'setValue']; | ||
_.each(keyStrategies, function (sendKeyStrategy) { | ||
it(`should work with strategy: ${sendKeyStrategy}`, async () => { | ||
await ctx.execFunc( | ||
function (sendKeyStrategy) { | ||
@@ -46,3 +51,2 @@ env.sendKeyStrategy = sendKeyStrategy; | ||
}); | ||
}); |
@@ -1,11 +0,9 @@ | ||
'use strict'; | ||
// transpile:mocha | ||
var chai = require('chai'), | ||
fs = require('fs'), | ||
prepareBootstrap = require('../../lib/dynamic-bootstrap').prepareBootstrap, | ||
logger = require('../../lib/logger'), | ||
Q = require('q'), | ||
rimraf = Q.denodeify(require('rimraf')), | ||
sinon = require('sinon'), | ||
sinonChai = require("sinon-chai"); | ||
import { prepareBootstrap } from '../..'; | ||
import log from '../../lib/logger'; | ||
import chai from 'chai'; | ||
import { fs, rimraf } from 'appium-support'; | ||
import sinon from 'sinon'; | ||
import sinonChai from 'sinon-chai'; | ||
@@ -17,3 +15,3 @@ chai.should(); | ||
function envFromCode(code) { | ||
// let's pick out the dynamic env from the new bootrap file with this | ||
// let's pick out the dynamic env from the new bootsrap file with this | ||
// regex so we can be sure it matches what we expect | ||
@@ -26,3 +24,3 @@ var envRe = /^bootstrap\((\{[^]+})\);$/m; | ||
function checkCode(code) { | ||
async function checkCode (code) { | ||
var env = envFromCode(code); | ||
@@ -32,3 +30,3 @@ env.nodePath.should.equal(process.execPath); | ||
env.instrumentsSock.should.exist; | ||
fs.existsSync(env.commandProxyClientPath).should.be.ok; | ||
(await fs.exists(env.commandProxyClientPath)).should.be.true; | ||
return env; | ||
@@ -38,53 +36,38 @@ } | ||
before(function () { | ||
sinon.spy(logger, "debug"); | ||
sinon.spy(log, 'debug'); | ||
}); | ||
after(function () { | ||
logger.debug.restore(); | ||
log.debug.restore(); | ||
}); | ||
it('should generate dynamic bootstrap', function (done) { | ||
it('should generate dynamic bootstrap', async () => { | ||
process.env.APPIUM_BOOTSTRAP_DIR = '/tmp/appium-uiauto/test/unit/bootstrap'; | ||
rimraf(process.env.APPIUM_BOOTSTRAP_DIR) | ||
await rimraf(process.env.APPIUM_BOOTSTRAP_DIR); | ||
// first call: should create new bootstrap file | ||
.then(function () { return prepareBootstrap(); }) | ||
.then(function (bootstrapFile) { | ||
bootstrapFile.should.match(/\/tmp\/appium-uiauto\/test\/unit\/bootstrap\/bootstrap\-.*\.js/); | ||
var code = fs.readFileSync(bootstrapFile, 'utf8'); | ||
checkCode(code); | ||
}) | ||
.then(function () { | ||
logger.debug.calledWithMatch(/Creating or overwritting dynamic bootstrap/).should.be.ok; | ||
logger.debug.reset(); | ||
}) | ||
let bootstrapFile = await prepareBootstrap(); | ||
bootstrapFile.should.match(/\/tmp\/appium-uiauto\/test\/unit\/bootstrap\/bootstrap\-.*\.js/); | ||
let code = await fs.readFile(bootstrapFile, 'utf8'); | ||
await checkCode(code); | ||
log.debug.calledWithMatch(/Creating or overwriting dynamic bootstrap/).should.be.true; | ||
log.debug.reset(); | ||
// second call: should reuse bootstrap file | ||
.then(function () { return prepareBootstrap(); }) | ||
.then(function (bootstrapFile) { | ||
bootstrapFile.should.match(/\/tmp\/appium-uiauto\/test\/unit\/bootstrap\/bootstrap\-.*\.js/); | ||
var code = fs.readFileSync(bootstrapFile, 'utf8'); | ||
checkCode(code); | ||
}).then(function () { | ||
logger.debug.calledWithMatch(/Reusing dynamic bootstrap/).should.be.ok; | ||
logger.debug.reset(); | ||
}) | ||
// second call: should reuse bootstrap file | ||
bootstrapFile = await prepareBootstrap(); | ||
bootstrapFile.should.match(/\/tmp\/appium-uiauto\/test\/unit\/bootstrap\/bootstrap\-.*\.js/); | ||
code = await fs.readFile(bootstrapFile, 'utf8'); | ||
await checkCode(code); | ||
log.debug.calledWithMatch(/Reusing dynamic bootstrap/).should.be.true; | ||
log.debug.reset(); | ||
// fourth call using custom socket path: should create different bootstrap file | ||
.then(function () { | ||
return prepareBootstrap({sock: '/tmp/abcd/sock'}); | ||
}).then(function (bootstrapFile) { | ||
bootstrapFile.should.match(/\/tmp\/appium-uiauto\/test\/unit\/bootstrap\/bootstrap\-.*\.js/); | ||
var code = fs.readFileSync(bootstrapFile, 'utf8'); | ||
var env = checkCode(code, {isVerbose: true, gracePeriod: 5}); | ||
env.instrumentsSock.should.equal('/tmp/abcd/sock'); | ||
}) | ||
.then(function () { | ||
logger.debug.calledWithMatch(/Creating or overwritting dynamic bootstrap/).should.be.ok; | ||
logger.debug.reset(); | ||
}) | ||
.nodeify(done); | ||
// third call using custom socket path: should create different bootstrap file | ||
bootstrapFile = await prepareBootstrap({sock: '/tmp/abcd/sock'}); | ||
bootstrapFile.should.match(/\/tmp\/appium-uiauto\/test\/unit\/bootstrap\/bootstrap\-.*\.js/); | ||
code = await fs.readFile(bootstrapFile, 'utf8'); | ||
let env = await checkCode(code, {isVerbose: true, gracePeriod: 5}); | ||
env.instrumentsSock.should.equal('/tmp/abcd/sock'); | ||
log.debug.calledWithMatch(/Creating or overwriting dynamic bootstrap/).should.be.ok; | ||
log.debug.reset(); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
13613288
9
181
6893
1
0
41
9
2
18
106
+ Addedappium-instruments@^2.0.4
+ Addedappium-logger@^1.1.7
+ Addedappium-support@2.0.0-beta13
+ Addedbabel-runtime@=5.5.5
+ Addedbluebird@^2.9.32
+ Addedlodash@^3.10.0
+ Addedsource-map-support@^0.3.1
+ Addedthrough@^2.3.8
+ Added@babel/runtime@7.26.0(transitive)
+ Added@types/bluebird@3.5.42(transitive)
+ AddedMD5@1.3.0(transitive)
+ Addedamdefine@1.0.1(transitive)
+ Addedansi@0.3.1(transitive)
+ Addedappium-instruments@2.0.6(transitive)
+ Addedappium-logger@1.1.7(transitive)
+ Addedappium-support@1.1.22.0.0-beta13(transitive)
+ Addedappium-xcode@2.0.5(transitive)
+ Addedare-we-there-yet@1.0.6(transitive)
+ Addedasync@0.9.2(transitive)
+ Addedasyncbox@2.9.4(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbase64-js@0.0.8(transitive)
+ Addedbluebird@2.11.03.7.2(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedbuffer-from@1.1.2(transitive)
+ Addedcharenc@0.0.2(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addedcrypt@0.0.2(transitive)
+ Addeddelegates@1.0.0(transitive)
+ Addeddenodeify@1.2.1(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedgauge@1.2.7(transitive)
+ Addedglob@7.2.3(transitive)
+ Addedhas-unicode@2.0.1(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedlodash@3.10.14.17.21(transitive)
+ Addedlodash.pad@4.5.1(transitive)
+ Addedlodash.padend@4.6.1(transitive)
+ Addedlodash.padstart@4.6.1(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedncp@2.0.0(transitive)
+ Addednpmlog@1.2.1(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedplist@1.2.0(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedq@1.5.1(transitive)
+ Addedreadable-stream@2.3.8(transitive)
+ Addedregenerator-runtime@0.14.1(transitive)
+ Addedrimraf@2.2.82.7.1(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedshell-quote@1.8.2(transitive)
+ Addedsource-map@0.1.320.6.1(transitive)
+ Addedsource-map-support@0.2.100.3.30.5.21(transitive)
+ Addedstring_decoder@1.1.1(transitive)
+ Addedteen_process@1.16.0(transitive)
+ Addedthrough@2.3.8(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addeduuid-js@0.7.5(transitive)
+ Addedwhich@2.0.2(transitive)
+ Addedwrappy@1.0.2(transitive)
+ Addedxmlbuilder@4.0.0(transitive)
+ Addedxmldom@0.1.31(transitive)
- Removedargparse@~0.1.15
- Removedq@~1.1.2
- Removedwinston@~0.8.3
- Removedargparse@0.1.16(transitive)
- Removedq@1.1.2(transitive)
- Removedunderscore.string@2.4.0(transitive)
Updatedmkdirp@^0.5.1