Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 9 commits
  • 6 files changed
  • 0 comments
  • 2 contributors
25 app/android.js
@@ -47,6 +47,25 @@ Android.prototype.fastReset = function(cb) {
47 47 this.adb.runFastReset(function(err) { if (err) return cb(err); return cb(null); });
48 48 };
49 49
  50 +Android.prototype.keyevent = function(keycode, cb) {
  51 + // keycode must be an int.
  52 + var cmd = 'adb shell input keyevent ' + parseInt(keycode, 10);
  53 + logger.info(cmd);
  54 + exec(cmd, {}, function(err, stdout, stderr) {
  55 + if (err) {
  56 + logger.warn(stderr);
  57 + return cb(null, {
  58 + status: status.codes.UnknownError.code
  59 + , value: null
  60 + });
  61 + }
  62 + cb(null, {
  63 + status: status.codes.Success.code
  64 + , value: null
  65 + });
  66 + });
  67 +};
  68 +
50 69 Android.prototype.start = function(cb, onDie) {
51 70 if (typeof onDie === "function") {
52 71 this.onStop = onDie;
@@ -461,7 +480,7 @@ Android.prototype.swipe = function(startX, startY, endX, endY, duration, touchCo
461 480 , startY: startY
462 481 , endX: endX
463 482 , endY: endY
464   - , steps: (duration * 1000) / 5
  483 + , steps: Math.round((duration * 1000) / 5)
465 484 };
466 485 if (elId !== null) {
467 486 swipeOpts.elementId = elId;
@@ -499,6 +518,10 @@ Android.prototype.setWindow = function(name, cb) {
499 518 cb(new NotYetImplementedError(), null);
500 519 };
501 520
  521 +Android.prototype.closeWindow = function(cb) {
  522 + cb(new NotYetImplementedError(), null);
  523 +};
  524 +
502 525 Android.prototype.clearWebView = function(cb) {
503 526 cb(new NotYetImplementedError(), null);
504 527 };
10 app/controller.js
@@ -344,6 +344,11 @@ exports.getPageIndex = function(req, res) {
344 344 req.device.getPageIndex(elementId, getResponseHandler(req, res));
345 345 };
346 346
  347 +exports.keyevent = function(req, res) {
  348 + var keycode = req.body.keycode;
  349 + req.device.keyevent(keycode, getResponseHandler(req, res));
  350 +};
  351 +
347 352 exports.keys = function(req, res) {
348 353 var keys = req.body.value.join('');
349 354
@@ -536,6 +541,10 @@ exports.setWindow = function(req, res) {
536 541 }
537 542 };
538 543
  544 +exports.closeWindow = function(req, res) {
  545 + req.device.closeWindow(getResponseHandler(req, res));
  546 +};
  547 +
539 548 exports.getWindowHandles = function(req, res) {
540 549 req.device.getWindowHandles(getResponseHandler(req, res));
541 550 };
@@ -623,6 +632,7 @@ var mobileCmdMap = {
623 632 , 'findAndAct': exports.findAndAct
624 633 , 'setValue' : exports.setValueImmediate
625 634 , 'reset' : exports.reset
  635 + , 'keyevent' : exports.keyevent
626 636 };
627 637
628 638 exports.produceError = function(req, res) {
28 app/hybrid/ios/remote-debugger.js
@@ -41,7 +41,7 @@ var RemoteDebugger = function(onDisconnect) {
41 41 this.pageIdKey = null;
42 42 this.pageLoading = false;
43 43 this.curMsgId = 0;
44   - this.dataCbs = [];
  44 + this.dataCbs = {};
45 45 this.onAppDisconnect = onDisconnect || noop;
46 46 this.pageChangeCb = noop;
47 47 this.specialCbs = {
@@ -77,7 +77,8 @@ RemoteDebugger.prototype.connect = function(cb, pageChangeCb) {
77 77 };
78 78
79 79 RemoteDebugger.prototype.disconnect = function() {
80   - this.socket.close();
  80 + logger.info("Disconnecting from remote debugger");
  81 + this.socket.destroy();
81 82 };
82 83
83 84 RemoteDebugger.prototype.setConnectionKey = function(cb) {
@@ -103,6 +104,7 @@ RemoteDebugger.prototype.selectApp = function(appIdKey, cb) {
103 104 logger.info("Selecting app");
104 105 this.send(connectToApp, _.bind(function(pageDict) {
105 106 cb(this.pageArrayFromDict(pageDict));
  107 + this.specialCbs['_rpc_forwardGetListing:'] = _.bind(this.onPageChange, this);
106 108 }, this));
107 109 };
108 110
@@ -113,6 +115,7 @@ RemoteDebugger.prototype.pageArrayFromDict = function(pageDict) {
113 115 id: dict.WIRPageIdentifierKey
114 116 , title: dict.WIRTitleKey
115 117 , url: dict.WIRURLKey
  118 + , isKey: typeof dict.WIRConnectionIdentifierKey !== "undefined"
116 119 });
117 120 });
118 121 return newPageArray;
@@ -133,9 +136,8 @@ RemoteDebugger.prototype.selectPage = function(pageIdKey, cb) {
133 136 if (err || res.result.value == 'loading') {
134 137 me.pageUnload();
135 138 }
136   - me.specialCbs['_rpc_forwardGetListing:'] = _.bind(me.onPageChange, me);
137 139 cb();
138   - }, 0);
  140 + });
139 141 });
140 142 });
141 143 };
@@ -242,6 +244,7 @@ RemoteDebugger.prototype.handleSpecialMessage = function(specialCb) {
242 244 if (fn) {
243 245 if (specialCb != "_rpc_forwardGetListing:") {
244 246 this.specialCbs[specialCb] = null;
  247 + } else {
245 248 }
246 249 fn.apply(this, _.rest(arguments));
247 250 }
@@ -268,6 +271,9 @@ RemoteDebugger.prototype.setHandlers = function() {
268 271 , msgId = dataKey.id
269 272 , result = dataKey.result
270 273 , error = dataKey.error || null;
  274 + if (msgId !== null && typeof msgId !== "undefined") {
  275 + msgId = msgId.toString();
  276 + }
271 277 if (dataKey.method == "Profiler.resetProfiles") {
272 278 logger.info("Device is telling us to reset profiles. Should probably " +
273 279 "do some kind of callback here");
@@ -277,13 +283,17 @@ RemoteDebugger.prototype.setHandlers = function() {
277 283 me.pageLoad();
278 284 } else if (typeof me.dataCbs[msgId] === "function") {
279 285 me.dataCbs[msgId](error, result);
  286 + me.dataCbs[msgId] = null;
  287 + } else if (me.dataCbs[msgId] === null) {
  288 + logger.error("Debugger returned data for message " + msgId +
  289 + "but we already ran that callback! WTF??");
280 290 } else {
281 291 if (!msgId && !result && !error) {
282 292 logger.info("Got a blank data response from debugger");
283 293 } else {
284 294 logger.error("Debugger returned data for message " + msgId +
285 295 " but we weren't waiting for that message! " +
286   - " result: " + result +
  296 + " result: " + JSON.stringify(result) +
287 297 " error: " + error);
288 298 }
289 299 }
@@ -309,8 +319,14 @@ RemoteDebugger.prototype.send = function (data, cb, cb2) {
309 319 this.specialCbs.connect = cb2;
310 320 }
311 321 } else if( data.__argument && data.__argument.WIRSocketDataKey ) {
  322 + //console.log("MsgId was " + this.curMsgId);
312 323 this.curMsgId += 1;
313   - this.dataCbs[this.curMsgId] = cb;
  324 + //console.log("Giving message new id of " + this.curMsgId);
  325 + this.dataCbs[this.curMsgId.toString()] = cb;
  326 + //_.each(this.dataCbs, function(cb, msgId) {
  327 + //console.log(msgId + ": " + cb);
  328 + //});
  329 + //console.log(JSON.stringify(this.dataCbs));
314 330 data.__argument.WIRSocketDataKey.id = this.curMsgId;
315 331 data.__argument.WIRSocketDataKey = new
316 332 Buffer(JSON.stringify(data.__argument.WIRSocketDataKey));
193 app/ios.js
@@ -39,11 +39,13 @@ var IOS = function(args) {
39 39 this.cbForCurrentCmd = null;
40 40 this.remote = null;
41 41 this.curWindowHandle = null;
  42 + this.selectingNewPage = false;
42 43 this.windowHandleCache = [];
43 44 this.webElementIds = [];
44 45 this.implicitWaitMs = 0;
45 46 this.curCoords = null;
46 47 this.curWebCoords = null;
  48 + this.onPageChangeCb = null;
47 49 this.capabilities = {
48 50 version: '6.0'
49 51 , webStorageEnabled: false
@@ -117,11 +119,13 @@ IOS.prototype.start = function(cb, onDie) {
117 119 me.cbForCurrentCmd(error, null);
118 120 code = 1; // this counts as an error even if instruments doesn't think so
119 121 }
120   - this.instruments = null;
121   - this.curCoords = null;
  122 + me.instruments = null;
  123 + me.curCoords = null;
122 124 try {
123   - this.stopRemote();
124   - } catch(e) {}
  125 + me.stopRemote();
  126 + } catch(e) {
  127 + logger.info("Error stopping remote: " + e.name + ": " + e.message);
  128 + }
125 129 var nexts = 0;
126 130 var next = function() {
127 131 nexts++;
@@ -263,29 +267,41 @@ IOS.prototype.listWebFrames = function(cb, exitCb) {
263 267 logger.error("Can't enter web frame without a bundle ID");
264 268 throw new Error("Tried to enter web frame without a bundle ID");
265 269 }
266   - this.remote = rd.init(exitCb);
267   - this.remote.connect(function(appDict) {
268   - if(!_.has(appDict, me.bundleId)) {
269   - logger.error("Remote debugger did not list " + me.bundleId + " among " +
270   - "its available apps");
271   - if(_.has(appDict, "com.apple.mobilesafari")) {
272   - logger.info("Using mobile safari instead");
273   - me.remote.selectApp("com.apple.mobilesafari", cb);
  270 + if (this.remote !== null && this.bundleId !== null) {
  271 + this.remote.selectApp(this.bundleId, cb);
  272 + } else {
  273 + this.remote = rd.init(exitCb);
  274 + this.remote.connect(function(appDict) {
  275 + if(!_.has(appDict, me.bundleId)) {
  276 + logger.error("Remote debugger did not list " + me.bundleId + " among " +
  277 + "its available apps");
  278 + if(_.has(appDict, "com.apple.mobilesafari")) {
  279 + logger.info("Using mobile safari instead");
  280 + me.remote.selectApp("com.apple.mobilesafari", cb);
  281 + } else {
  282 + cb([]);
  283 + }
274 284 } else {
275   - cb([]);
  285 + me.remote.selectApp(me.bundleId, cb);
276 286 }
277   - } else {
278   - me.remote.selectApp(me.bundleId, cb);
279   - }
280   - }, _.bind(me.onPageChange, me));
  287 + }, _.bind(me.onPageChange, me));
  288 + }
281 289 };
282 290
283 291 IOS.prototype.onPageChange = function(pageArray) {
284 292 logger.info("Remote debugger notified us of a new page listing");
  293 + if (this.selectingNewPage) {
  294 + logger.info("We're in the middle of selecting a page, ignoring");
  295 + return;
  296 + }
285 297 var newIds = []
  298 + , keyId = null
286 299 , me = this;
287 300 _.each(pageArray, function(page) {
288 301 newIds.push(page.id.toString());
  302 + if (page.isKey) {
  303 + keyId = page.id.toString();
  304 + }
289 305 });
290 306 var newPages = [];
291 307 _.each(newIds, function(id) {
@@ -293,19 +309,42 @@ IOS.prototype.onPageChange = function(pageArray) {
293 309 newPages.push(id);
294 310 }
295 311 });
  312 + var newPage = null;
296 313 if (this.curWindowHandle === null) {
297 314 logger.info("We don't appear to have window set yet, ignoring");
298 315 } else if (newPages.length) {
299 316 logger.info("We have new pages, going to select page " + newPages[0]);
300   - this.remote.selectPage(newPages[0], function() {
301   - me.curWindowHandle = newPages[0];
302   - });
303   - } else if (!_.contains(me.windowHandleCache, me.curWindowHandle.toString())) {
304   - logger.error("New page listing from remote debugger doesn't contain " +
305   - "current window, not sure how to proceed");
  317 + newPage = parseInt(newPages[0], 10);
  318 + } else if (!_.contains(newIds, me.curWindowHandle.toString())) {
  319 + logger.info("New page listing from remote debugger doesn't contain " +
  320 + "current window, let's assume it's closed");
  321 + if (keyId !== null) {
  322 + logger.info("Debugger already selected page " + keyId + ", " +
  323 + "confirming that choice.");
  324 + } else {
  325 + logger.error("Don't have our current window anymore, and there " +
  326 + "aren't any more to load! Doing nothing...");
  327 + }
  328 + me.curWindowHandle = keyId;
  329 + me.remote.pageIdKey = parseInt(keyId, 10);
306 330 } else {
307 331 logger.info("New page listing is same as old, doing nothing");
308 332 }
  333 +
  334 + if (newPage !== null) {
  335 + this.selectingNewPage = true;
  336 + this.remote.selectPage(newPage, function() {
  337 + me.selectingNewPage = false;
  338 + me.curWindowHandle = newPage;
  339 + if (me.onPageChangeCb !== null) {
  340 + me.onPageChangeCb();
  341 + me.onPageChangeCb = null;
  342 + }
  343 + });
  344 + } else if (this.onPageChangeCb !== null) {
  345 + this.onPageChangeCb();
  346 + this.onPageChangeCb = null;
  347 + }
309 348 this.windowHandleCache = newIds;
310 349 };
311 350
@@ -379,6 +418,13 @@ IOS.prototype.push = function(elem) {
379 418 var me = this;
380 419
381 420 var next = function() {
  421 + if (me.selectingNewPage && me.curWindowHandle) {
  422 + logger.info("We're in the middle of selecting a new page, " +
  423 + "waiting to run next command until done");
  424 + setTimeout(next, 500);
  425 + return;
  426 + }
  427 +
382 428 if (me.queue.length <= 0 || me.progress > 0) {
383 429 return;
384 430 }
@@ -529,7 +575,7 @@ IOS.prototype.findAndAct = function(strategy, selector, index, action, actionPar
529 575 // app/uiauto/appium/app.js:elemForAction
530 576 , supportedActions = ["tap", "isEnabled", "isValid", "isVisible",
531 577 "value", "name", "label", "setValue", "click",
532   - "selectPage"]
  578 + "selectPage", "rect"]
533 579 , many = index > 0;
534 580
535 581 if (action === "click") { action = "tap"; }
@@ -545,10 +591,15 @@ IOS.prototype.findAndAct = function(strategy, selector, index, action, actionPar
545 591 cmd += strParams.join(', ');
546 592 cmd += ")";
547 593 me.proxy(cmd, function(err, res) {
548   - if (!err && res.value !== null) {
549   - findCb(true, err, res);
550   - } else {
  594 + if (err || res.status === status.codes.NoSuchElement.code) {
551 595 findCb(false, err, res);
  596 + } else if (many && res.value === []) {
  597 + findCb(false, err, {
  598 + status: status.codes.NoSuchElement.code
  599 + , value: "Could not find element in findAndAct"
  600 + });
  601 + } else {
  602 + findCb(true, err, res);
552 603 }
553 604 });
554 605 };
@@ -713,6 +764,10 @@ IOS.prototype.submit = function(elementId, cb) {
713 764 }
714 765 };
715 766
  767 +IOS.prototype.keyevent = function(keycode, cb) {
  768 + cb(new NotImplementedError(), null);
  769 +};
  770 +
716 771 IOS.prototype.complexTap = function(tapCount, touchCount, duration, x, y, elementId, cb) {
717 772 var command
718 773 , options = {
@@ -1167,29 +1222,44 @@ IOS.prototype.setWindow = function(name, cb) {
1167 1222 next();
1168 1223 //}
1169 1224 } else {
1170   - cb(status.codes.NoSuchWindow.code, null);
  1225 + cb(null, {
  1226 + status: status.codes.NoSuchWindow.code
  1227 + , value: null
  1228 + });
  1229 + }
  1230 +};
  1231 +
  1232 +IOS.prototype.closeWindow = function(cb) {
  1233 + var me = this;
  1234 + if (this.curWindowHandle) {
  1235 + var closeFn = _.bind(this.closeIPhoneWindow, this);
  1236 + if (this.deviceType === "ipad") {
  1237 + closeFn = _.bind(this.closeIPadWindow, this);
  1238 + }
  1239 + closeFn(function(err, res) {
  1240 + // wait for page change callback to happen before we return
  1241 + // control to the client
  1242 + //me.onPageChangeCb = function() {
  1243 + cb(err, res);
  1244 + //};
  1245 + });
  1246 + } else {
  1247 + cb(new NotImplementedError(), null);
1171 1248 }
1172 1249 };
1173 1250
1174 1251 IOS.prototype.setSafariWindow = function(windowId, cb) {
1175 1252 var me = this;
1176   - var success = function(err, res, cb) {
1177   - if (err || res.status !== status.codes.Success.code) {
1178   - cb(err, res);
1179   - return false;
1180   - }
1181   - return true;
1182   - };
1183 1253
1184 1254 me.findAndAct('name', 'Pages', 0, 'value', [], function(err, res) {
1185   - if (success(err, res, cb)) {
  1255 + if (me.checkSuccess(err, res, cb)) {
1186 1256 if (res.value === "") {
1187 1257 cb(err, res);
1188 1258 } else {
1189 1259 me.findAndAct('name', 'Pages', 0, 'tap', [], function(err, res) {
1190   - if (success(err, res, cb)) {
  1260 + if (me.checkSuccess(err, res, cb)) {
1191 1261 me.findAndAct('tag name', 'pageIndicator', 0, 'selectPage', [windowId], function(err, res) {
1192   - if (success(err, res, cb)) {
  1262 + if (me.checkSuccess(err, res, cb)) {
1193 1263 me.findAndAct('name', 'Done', 0, 'tap', [], cb);
1194 1264 }
1195 1265 });
@@ -1200,6 +1270,53 @@ IOS.prototype.setSafariWindow = function(windowId, cb) {
1200 1270 });
1201 1271 };
1202 1272
  1273 +IOS.prototype.checkSuccess = function(err, res, cb) {
  1274 + if (err || res.status !== status.codes.Success.code) {
  1275 + cb(err, res);
  1276 + return false;
  1277 + }
  1278 + return true;
  1279 +};
  1280 +
  1281 +IOS.prototype.closeIPadWindow = function(cb) {
  1282 + var me = this;
  1283 + me.findAndAct('xpath', '//button[contains(@name, "Close tab for")]',
  1284 + 0, 'tap', [], cb);
  1285 +};
  1286 +
  1287 +IOS.prototype.closeIPhoneWindow = function(cb) {
  1288 + var me = this;
  1289 + me.findAndAct('name', 'Pages', 0, 'tap', [], function(err, res) {
  1290 + if (me.checkSuccess(err, res, cb)) {
  1291 + me.findAndAct('name', 'Close page', 0, 'click', [], function(err, res) {
  1292 + if (me.checkSuccess(err, res, cb)) {
  1293 + var oldImplicitWait = me.implicitWaitMs;
  1294 + me.implicitWaitMs = 0;
  1295 + me.findUIElementOrElements('name', 'Done', null, false, function(err, res) {
  1296 + me.implicitWaitMs = oldImplicitWait;
  1297 + if (res.status === status.codes.Success.code) {
  1298 + var command = ["au.getElement('", res.value.ELEMENT,
  1299 + "').tap()"].join('');
  1300 + // don't care if it fails
  1301 + me.proxy(command, function() {
  1302 + cb(null, {
  1303 + status: status.codes.Success.code
  1304 + , value: null
  1305 + });
  1306 + });
  1307 + } else {
  1308 + cb(null, {
  1309 + status: status.codes.Success.code
  1310 + , value: null
  1311 + });
  1312 + }
  1313 + });
  1314 + }
  1315 + });
  1316 + }
  1317 + });
  1318 +};
  1319 +
1203 1320 IOS.prototype.clearWebView = function(cb) {
1204 1321 if (this.curWindowHandle === null) {
1205 1322 cb(new NotImplementedError(), null);
2  app/routing.js
@@ -58,6 +58,7 @@ module.exports = function(appium) {
58 58 rest.get('/wd/hub/session/:sessionId?/window_handle', controller.getWindowHandle);
59 59 rest.get('/wd/hub/session/:sessionId?/window_handles', controller.getWindowHandles);
60 60 rest.post('/wd/hub/session/:sessionId?/window', controller.setWindow);
  61 + rest.delete('/wd/hub/session/:sessionId?/window', controller.closeWindow);
61 62 rest.get('/wd/hub/session/:sessionId?/window/:windowhandle?/size', controller.getWindowSize);
62 63 rest.post('/wd/hub/session/:sessionId?/execute', controller.execute);
63 64 rest.get('/wd/hub/session/:sessionId?/title', controller.title);
@@ -102,7 +103,6 @@ var routeNotYetImplemented = function(rest) {
102 103 rest.get('/wd/hub/session/:sessionId?/ime/activated', controller.notYetImplemented);
103 104 rest.post('/wd/hub/session/:sessionId?/ime/deactivate', controller.notYetImplemented);
104 105 rest.post('/wd/hub/session/:sessionId?/ime/activate', controller.notYetImplemented);
105   - rest.delete('/wd/hub/session/:sessionId?/window', controller.notYetImplemented);
106 106 rest.post('/wd/hub/session/:sessionId?/window/:windowhandle/size', controller.notYetImplemented);
107 107 rest.post('/wd/hub/session/:sessionId?/window/:windowhandle/position', controller.notYetImplemented);
108 108 rest.get('/wd/hub/session/:sessionId?/window/:windowhandle/position', controller.notYetImplemented);
71 test/functional/safari/safari.js
... ... @@ -1,7 +1,7 @@
1 1 /*global it:true */
2 2 "use strict";
3 3
4   -var describeWd = require("../../helpers/driverblock.js").describeForSafari()
  4 +var desc = require("../../helpers/driverblock.js").describeForSafari()
5 5 , wvHelpers = require("../../helpers/webview.js")
6 6 , webviewTests = wvHelpers.buildTests
7 7 , loadWebView = wvHelpers.loadWebView
@@ -9,49 +9,54 @@ var describeWd = require("../../helpers/driverblock.js").describeForSafari()
9 9 , _ = require('underscore')
10 10 , should = require('should');
11 11
12   -describeWd('safari init', function(h) {
13   - it('getting current window should work initially', function(done) {
14   - h.driver.windowHandle(function(err, handleId) {
15   - should.not.exist(err);
16   - handleId.should.eql(1);
17   - done();
  12 +
  13 +var devices = ["iPhone", "iPad"];
  14 +//var devices = ["iPad", "iPhone"];
  15 +_.each(devices, function(sim) {
  16 +
  17 + desc('windows and frames (' + sim + ')', function(h) {
  18 +
  19 + it('getting current window should work initially', function(done) {
  20 + h.driver.windowHandle(function(err, handleId) {
  21 + should.not.exist(err);
  22 + parseInt(handleId, 10).should.be.above(0);
  23 + done();
  24 + });
18 25 });
19   - });
20   -});
21 26
22   -// todo: write window manipulation test for iphone version
23   -
24   -describeWd('safari ipad', function(h) {
25   - it('should be able to close tabs', function(done) {
26   - h.driver.frame(null, function() {
27   - h.driver.elementByTagName("window", function(err, win) {
28   - win.elementsByXPath("//button[contains(@name, 'Close tab for')]", function(err, els) {
29   - els.length.should.be.above(0);
30   - var closeTab = function(idx) {
31   - els[idx].click(function() {
32   - if (idx+1 === els.length) {
33   - done();
34   - } else {
35   - closeTab(idx+1);
36   - }
37   - });
38   - };
39   - closeTab(0);
  27 + it("should throw nosuchwindow if there's not one", function(done) {
  28 + loadWebView("safari", h.driver, function() {
  29 + h.driver.window('noexistman', function(err) {
  30 + should.exist(err);
  31 + err.status.should.eql(23);
  32 + done();
40 33 });
41 34 });
42 35 });
43   - });
44   -}, null, null, {device: 'iPad Simulator'});
45 36
46   -_.each(["iPhone", "iPad"], function(sim) {
47   - describeWd('windows and frames', function(h) {
48   - it("should automate a new window if one opens (" + sim + ")", function(done) {
  37 + it("should be able to open and close windows", function(done) {
49 38 loadWebView("safari", h.driver, function() {
50 39 h.driver.elementById('blanklink', function(err, link) {
51 40 link.click(function() {
52 41 spinTitle("I am another page title", h.driver, function(err) {
53 42 should.not.exist(err);
54   - done();
  43 + h.driver.windowHandles(function(err, handles) {
  44 + var handles1 = handles.length;
  45 + h.driver.close(function(err) {
  46 + // wait for safari to write window status
  47 + setTimeout(function() {
  48 + should.not.exist(err);
  49 + h.driver.windowHandles(function(err, handles) {
  50 + var handles2 = handles.length;
  51 + handles1.should.be.above(handles2);
  52 + spinTitle("I am a page title", h.driver, function(err) {
  53 + should.not.exist(err);
  54 + done();
  55 + });
  56 + });
  57 + }, 3000);
  58 + });
  59 + });
55 60 });
56 61 });
57 62 });

No commit comments for this range

Something went wrong with that request. Please try again.