Skip to content

Commit 2650767

Browse files
committed
feat(restart): browser.restart should return a promise
Also allows `browser.restart` to work when the control flow is disabled, and fixes it for forked browsers. Closes angular#3899 and angular#3896
1 parent e68dcf1 commit 2650767

File tree

3 files changed

+80
-32
lines changed

3 files changed

+80
-32
lines changed

lib/browser.ts

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,12 @@ export class AbstractExtendedWebDriver extends AbstractWebDriver {
7676
* `ElementFinder` will be unwrapped to their underlying `WebElement` instance
7777
*
7878
* @private
79-
* @param {Object} to
80-
* @param {Object} from
81-
* @param {string} fnName
82-
* @param {function=} setupFn
79+
* @param {Object} to The object to copy a function to
80+
* @param {string|Object} from Where to get the function from. If an object, the object containing
81+
* the function to copy over. If a string, the name of a property on `to` which is an object
82+
* containing the function.
83+
* @param {string} fnName The name of the function to copy
84+
* @param {function=} setupFn An optional function to run before the copied function
8385
*/
8486
function ptorMixin(to: any, from: any, fnName: string, setupFn?: Function) {
8587
to[fnName] = function() {
@@ -91,7 +93,8 @@ function ptorMixin(to: any, from: any, fnName: string, setupFn?: Function) {
9193
if (setupFn) {
9294
setupFn();
9395
}
94-
return from[fnName].apply(from, arguments);
96+
let fromObj = typeof from === 'string' ? to[from] : from;
97+
return fromObj[fnName].apply(fromObj, arguments);
9598
};
9699
};
97100

@@ -236,9 +239,11 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
236239
params: any;
237240

238241
/**
239-
* Set by the runner.
242+
* Resolved when the browser is ready for use
243+
*
244+
* Set partially by `setDriver_()`, partially by the runner.
240245
*
241-
* @type {q.Promise} Done when the new browser is ready for use
246+
* @type {webdriver.promise.Promise}
242247
*/
243248
ready: wdpromise.Promise<any>;
244249

@@ -306,31 +311,24 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
306311
webdriverInstance: WebDriver, opt_baseUrl?: string, opt_rootElement?: string,
307312
opt_untrackOutstandingTimeouts?: boolean, opt_blockingProxyUrl?: string) {
308313
super();
314+
this.setDriver_(webdriverInstance);
315+
309316
// These functions should delegate to the webdriver instance, but should
310317
// wait for Angular to sync up before performing the action. This does not
311318
// include functions which are overridden by protractor below.
312319
let methodsToSync = ['getCurrentUrl', 'getPageSource', 'getTitle'];
313-
let extendWDInstance: ExtendedWebDriver;
314-
try {
315-
extendWDInstance = extendWD(webdriverInstance);
316-
} catch (e) {
317-
// Probably not a driver that can be extended (e.g. gotten using
318-
// `directConnect: true` in the config)
319-
extendWDInstance = webdriverInstance as ExtendedWebDriver;
320-
}
321320

322321
// Mix all other driver functionality into Protractor.
323322
Object.getOwnPropertyNames(WebDriver.prototype).forEach(method => {
324-
if (!this[method] && typeof(extendWDInstance as any)[method] === 'function') {
323+
if (!this[method] && typeof(this.driver as any)[method] === 'function') {
325324
if (methodsToSync.indexOf(method) !== -1) {
326-
ptorMixin(this, extendWDInstance, method, this.waitForAngular.bind(this));
325+
ptorMixin(this, 'driver', method, this.waitForAngular.bind(this));
327326
} else {
328-
ptorMixin(this, extendWDInstance, method);
327+
ptorMixin(this, 'driver', method);
329328
}
330329
}
331330
});
332331

333-
this.driver = extendWDInstance;
334332
if (opt_blockingProxyUrl) {
335333
logger.info('Starting BP client for ' + opt_blockingProxyUrl);
336334
this.bpClient = new BPClient(opt_blockingProxyUrl);
@@ -343,7 +341,6 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
343341
this.ignoreSynchronization = false;
344342
this.getPageTimeout = DEFAULT_GET_PAGE_TIMEOUT;
345343
this.params = {};
346-
this.ready = null;
347344
this.plugins_ = new Plugins({});
348345
this.resetUrl = DEFAULT_RESET_URL;
349346
this.debugHelper = new DebugHelper(this);
@@ -365,7 +362,33 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
365362
ng12Hybrid_ = ng12Hybrid;
366363
}
367364
});
368-
this.driver.getCapabilities().then((caps: Capabilities) => {
365+
this.trackOutstandingTimeouts_ = !opt_untrackOutstandingTimeouts;
366+
this.mockModules_ = [];
367+
this.addBaseMockModules_();
368+
369+
// set up expected conditions
370+
this.ExpectedConditions = new ProtractorExpectedConditions(this);
371+
}
372+
373+
/**
374+
* Sets the `WebDriver` instance which `ProtractorBrowser` wraps around
375+
*
376+
* @param {WebDriver} webdriverInstance The WebDriver instance to wrap around
377+
*/
378+
setDriver_(webdriverInstance: WebDriver) {
379+
// First extend the driver
380+
let extendWDInstance: ExtendedWebDriver;
381+
try {
382+
extendWDInstance = extendWD(webdriverInstance);
383+
} catch (e) {
384+
// Probably not a driver that can be extended (e.g. gotten using
385+
// `directConnect: true` in the config)
386+
extendWDInstance = webdriverInstance as ExtendedWebDriver;
387+
}
388+
389+
this.driver = extendWDInstance;
390+
391+
this.ready = this.driver.getCapabilities().then((caps) => {
369392
// Internet Explorer does not accept data URLs, which are the default
370393
// reset URL for Protractor.
371394
// Safari accepts data urls, but SafariDriver fails after one is used.
@@ -376,13 +399,6 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
376399
this.resetUrl = 'about:blank';
377400
}
378401
});
379-
380-
this.trackOutstandingTimeouts_ = !opt_untrackOutstandingTimeouts;
381-
this.mockModules_ = [];
382-
this.addBaseMockModules_();
383-
384-
// set up expected conditions
385-
this.ExpectedConditions = new ProtractorExpectedConditions(this);
386402
}
387403

388404
/**
@@ -434,7 +450,17 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
434450
/**
435451
* Restart the browser instance.
436452
*
453+
* Replaces a browser's `.driver` property, so if you've saved a reference directly to
454+
* `browser.driver`, make sure to update it.
455+
*
456+
* Behaves slightly differently depending on if the webdriver control flow is enabled. If the
457+
* control flow is enabled, `browser.driver` is synchronously replaced. If the control flow is
458+
* disabled, `browser.driver` is replaced asynchronously after the old driver quits.
459+
*
437460
* Set by the runner.
461+
*
462+
* @returns {webdriver.promise.Promise.<void>} A promise which resolves once the browser has
463+
* restarted.
438464
*/
439465
restart() {
440466
return;

lib/runner.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {Logger} from './logger';
1010
import {Plugins} from './plugins';
1111
import {protractor} from './ptor';
1212
import * as helper from './util';
13+
import * as wdpromiseUtil from './wdpromiseUtil';
1314

1415
declare let global: any;
1516
declare let process: any;
@@ -234,7 +235,9 @@ export class Runner extends EventEmitter {
234235
browser_.ng12Hybrid = config.ng12Hybrid;
235236
}
236237

237-
browser_.ready = driver.manage().timeouts().setScriptTimeout(config.allScriptsTimeout);
238+
browser_.ready = browser_.ready.then(() => {
239+
return driver.manage().timeouts().setScriptTimeout(config.allScriptsTimeout);
240+
});
238241

239242
browser_.getProcessedConfig = () => {
240243
return wdpromise.fulfilled(config);
@@ -256,9 +259,19 @@ export class Runner extends EventEmitter {
256259
browser_.restart = () => {
257260
// Note: because tests are not paused at this point, any async
258261
// calls here are not guaranteed to complete before the tests resume.
259-
this.driverprovider_.quitDriver(browser_.driver);
260-
browser_ = browser_.forkNewDriverInstance(false, true);
261-
this.setupGlobals_(browser_);
262+
263+
// Seperate solutions for if the control flow is on or off, see
264+
// https://github.com/angular/protractor/issues/3899
265+
if (wdpromiseUtil.usePromiseManager()) {
266+
this.driverprovider_.quitDriver(browser_.driver);
267+
browser_.setDriver_(this.driverprovider_.getNewDriver());
268+
return browser_.ready;
269+
} else {
270+
return this.driverprovider_.quitDriver(browser_.driver).then(() => {
271+
browser_.setDriver_(this.driverprovider_.getNewDriver());
272+
return browser_.ready;
273+
});
274+
}
262275
};
263276

264277
return browser_;

lib/wdpromiseUtil.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {promise as wdpromise} from 'selenium-webdriver';
2+
3+
export function usePromiseManager() {
4+
if ((wdpromise as any).USE_PROMISE_MANAGER !== undefined) {
5+
return (wdpromise as any).USE_PROMISE_MANAGER;
6+
} else {
7+
return !!wdpromise.ControlFlow;
8+
}
9+
}

0 commit comments

Comments
 (0)