Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Commit 3645df4

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 #3899 and #3896
1 parent f4cf277 commit 3645df4

File tree

2 files changed

+133
-9
lines changed

2 files changed

+133
-9
lines changed

lib/browser.ts

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,16 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
236236
params: any;
237237

238238
/**
239+
* Resolved when the browser is ready for use. Resolves to the browser, so
240+
* you can do:
241+
*
242+
* forkedBrowser = await browser.forkNewDriverInstance().ready;
243+
*
239244
* Set by the runner.
240245
*
241-
* @type {q.Promise} Done when the new browser is ready for use
246+
* @type {webdriver.promise.Promise.<ProtractorBrowser>}
242247
*/
243-
ready: wdpromise.Promise<any>;
248+
ready: wdpromise.Promise<ProtractorBrowser>;
244249

245250
/*
246251
* Set by the runner.
@@ -418,7 +423,15 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
418423
/**
419424
* Fork another instance of browser for use in interactive tests.
420425
*
421-
* Set by the runner.
426+
* @example
427+
* // Running with control flow enabled
428+
* var fork = browser.forkNewDriverInstance();
429+
* fork.get('page1'); // 'page1' gotten by forked browser
430+
*
431+
* @example
432+
* // Running with control flow disabled
433+
* var forked = await browser.forkNewDriverInstance().ready;
434+
* await fork.get('page1'); // 'page1' gotten by forked browser
422435
*
423436
* @param {boolean} opt_useSameUrl Whether to navigate to current url on
424437
* creation
@@ -432,11 +445,84 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
432445
}
433446

434447
/**
435-
* Restart the browser instance.
448+
* Restart the browser. This is done by closing this browser instance and creating a new one.
449+
* A promise resolving to the new instance is returned, and if this function was called on the
450+
* global `browser` instance then Protractor will automatically overwrite the global `browser`
451+
* variable.
452+
*
453+
* When restarting a forked browser, it is the caller's job to overwrite references to the old
454+
* instance.
455+
*
456+
* This function behaves slightly differently depending on if the webdriver control flow is
457+
* enabled. If the control flow is enabled, the global `browser` object is synchronously
458+
* replaced. If the control flow is disabled, the global `browser` is replaced asynchronously
459+
* after the old driver quits.
436460
*
437461
* Set by the runner.
462+
*
463+
* @example
464+
* // Running against global browser, with control flow enabled
465+
* browser.get('page1');
466+
* browser.restart();
467+
* browser.get('page2'); // 'page2' gotten by restarted browser
468+
*
469+
* @example
470+
* // Running against global browser, with control flow disabled
471+
* await browser.get('page1');
472+
* await browser.restart();
473+
* await browser.get('page2'); // 'page2' gotten by restarted browser
474+
*
475+
* @example
476+
* // Running against forked browsers, with the control flow enabled
477+
* // In this case, you may prefer `restartSync` (documented below)
478+
* var forked = browser.forkNewDriverInstance();
479+
* fork.get('page1');
480+
* fork.restart().then(function(fork) {
481+
* fork.get('page2'); // 'page2' gotten by restarted fork
482+
* });
483+
*
484+
* @example
485+
* // Running against forked browsers, with the control flow disabled
486+
* var forked = await browser.forkNewDriverInstance().ready;
487+
* await fork.get('page1');
488+
* fork = await fork.restart();
489+
* await fork.get('page2'); // 'page2' gotten by restarted fork
490+
*
491+
* @example
492+
* // Unexpected behavior can occur if you save references to the global `browser`
493+
* var savedBrowser = browser;
494+
* browser.restart().then(function() {
495+
* console.log(browser === savedBrowser); // false
496+
* });
497+
*
498+
* @returns {webdriver.promise.Promise<ProtractorBrowser>} A promise resolving to the restarted
499+
* browser
438500
*/
439-
restart() {
501+
restart(): wdpromise.Promise<ProtractorBrowser> {
502+
return;
503+
}
504+
505+
/**
506+
* Like `restart`, but instead of returning a promise resolving to the new browser instance,
507+
* returns the new browser instance directly. Can only be used when the control flow is enabled.
508+
*
509+
* @example
510+
* // Running against global browser
511+
* browser.get('page1');
512+
* browser.restartSync();
513+
* browser.get('page2'); // 'page2' gotten by restarted browser
514+
*
515+
* @example
516+
* // Running against forked browsers
517+
* var forked = browser.forkNewDriverInstance();
518+
* fork.get('page1');
519+
* fork = fork.restartSync();
520+
* fork.get('page2'); // 'page2' gotten by restarted fork
521+
*
522+
* @throws {TypeError} Will throw an error if the control flow is not enabled
523+
* @returns {ProtractorBrowser} The restarted browser
524+
*/
525+
restartSync(): ProtractorBrowser {
440526
return;
441527
}
442528

@@ -1054,4 +1140,18 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
10541140
};
10551141
this.debugHelper.init(debuggerClientPath, onStartFn, opt_debugPort);
10561142
}
1143+
1144+
/**
1145+
* Determine if the control flow is enabled.
1146+
*
1147+
* @returns true if the control flow is enabled, false otherwise.
1148+
*/
1149+
controlFlowIsEnabled() {
1150+
if ((wdpromise as any).USE_PROMISE_MANAGER !== undefined) {
1151+
return (wdpromise as any).USE_PROMISE_MANAGER;
1152+
} else {
1153+
// True for old versions of `selenium-webdriver`, probably false in >=5.0.0
1154+
return !!wdpromise.ControlFlow;
1155+
}
1156+
}
10571157
}

lib/runner.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export class Runner extends EventEmitter {
113113
this.frameworkUsesAfterEach = true;
114114
if (this.config_.restartBrowserBetweenTests) {
115115
// TODO(sjelin): remove the `|| q()` once `restart()` returns a promise
116-
this.restartPromise = this.restartPromise || protractor.browser.restart() || q();
116+
this.restartPromise = this.restartPromise || q(protractor.browser.restart());
117117
ret = this.restartPromise;
118118
this.restartPromise = undefined;
119119
}
@@ -246,7 +246,8 @@ export class Runner extends EventEmitter {
246246
browser_.ng12Hybrid = config.ng12Hybrid;
247247
}
248248

249-
browser_.ready = driver.manage().timeouts().setScriptTimeout(config.allScriptsTimeout);
249+
browser_.ready =
250+
driver.manage().timeouts().setScriptTimeout(config.allScriptsTimeout).then(() => browser_);
250251

251252
browser_.getProcessedConfig = () => {
252253
return wdpromise.fulfilled(config);
@@ -265,12 +266,35 @@ export class Runner extends EventEmitter {
265266
return newBrowser;
266267
};
267268

269+
let replaceBrowser = () => {
270+
let newBrowser = browser_.forkNewDriverInstance(false, true);
271+
if (browser_ === protractor.browser) {
272+
this.setupGlobals_(newBrowser);
273+
}
274+
return newBrowser;
275+
};
276+
268277
browser_.restart = () => {
269278
// Note: because tests are not paused at this point, any async
270279
// calls here are not guaranteed to complete before the tests resume.
280+
281+
// Seperate solutions depending on if the control flow is enabled (see lib/browser.ts)
282+
if (browser_.controlFlowIsEnabled()) {
283+
return browser_.restartSync().ready;
284+
} else {
285+
return this.driverprovider_.quitDriver(browser_.driver)
286+
.then(replaceBrowser)
287+
.then(newBrowser => newBrowser.ready);
288+
}
289+
};
290+
291+
browser_.restartSync = () => {
292+
if (!browser_.controlFlowIsEnabled()) {
293+
throw TypeError('Unable to use `browser.restartSync()` when the control flow is disabled');
294+
}
295+
271296
this.driverprovider_.quitDriver(browser_.driver);
272-
browser_ = browser_.forkNewDriverInstance(false, true);
273-
this.setupGlobals_(browser_);
297+
return replaceBrowser();
274298
};
275299

276300
return browser_;

0 commit comments

Comments
 (0)