diff --git a/Gulpfile.js b/Gulpfile.js index cca9ad70cca..c298f294768 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -625,7 +625,7 @@ function testFunctional (fixturesDir, testingEnvironmentName, browserProviderNam .pipe(mocha({ ui: 'bdd', reporter: 'spec', - timeout: typeof v8debug === 'undefined' ? 30000 : Infinity // NOTE: disable timeouts in debug + timeout: typeof v8debug === 'undefined' ? 180000 : Infinity // NOTE: disable timeouts in debug })); } diff --git a/src/browser/connection/index.js b/src/browser/connection/index.js index 07611e1aa21..6b0e3d3f4ae 100644 --- a/src/browser/connection/index.js +++ b/src/browser/connection/index.js @@ -10,6 +10,7 @@ import COMMAND from './command'; import STATUS from './status'; import { GeneralError } from '../../errors/runtime'; import MESSAGE from '../../errors/runtime/message'; +import testRunTracker from '../../api/test-run-tracker'; const IDLE_PAGE_TEMPLATE = read('../../client/browser/idle-page/index.html.mustache'); @@ -98,6 +99,8 @@ export default class BrowserConnection extends EventEmitter { try { await this.provider.closeBrowser(this.id); + + this.opened = false; } catch (err) { // NOTE: A warning would be really nice here, but it can't be done while log is stored in a task. @@ -112,9 +115,30 @@ export default class BrowserConnection extends EventEmitter { } } + _restartBrowser () { + this.ready = false; + + this._forceIdle(); + + this._closeBrowser() + .then(() => { + const testRun = this._getActiveTestRun(); + + testRun.stop(new GeneralError(MESSAGE.browserDisconnected, this.userAgent)); + + this._runBrowser(); + }); + } + _waitForHeartbeat () { this.heartbeatTimeout = setTimeout(() => { - this.emit('error', new GeneralError(MESSAGE.browserDisconnected, this.userAgent)); + const needRestartBrowser = true; // option + + if (needRestartBrowser) + this._restartBrowser(); + else + this.emit('error', new GeneralError(MESSAGE.browserDisconnected, this.userAgent)); + }, this.HEARTBEAT_TIMEOUT); } @@ -125,6 +149,12 @@ export default class BrowserConnection extends EventEmitter { return this.pendingTestRunUrl; } + _getActiveTestRun () { + const testRuns = Object.values(testRunTracker.activeTestRuns); + + return testRuns.find(tr => tr.browserConnection.id === this.id); + } + async _popNextTestRunUrl () { while (this.hasQueuedJobs && !this.currentJob.hasQueuedTestRuns) this.jobQueue.shift(); diff --git a/src/runner/test-run-controller.js b/src/runner/test-run-controller.js index 184cb820423..ef4be8f3b5f 100644 --- a/src/runner/test-run-controller.js +++ b/src/runner/test-run-controller.js @@ -91,6 +91,10 @@ export default class TestRunController extends EventEmitter { } _keepInQuarantine () { + this._restart(); + } + + _restart () { this.emit('test-run-restart'); } @@ -135,6 +139,7 @@ export default class TestRunController extends EventEmitter { testRun.once('start', () => this.emit('test-run-start')); testRun.once('done', () => this._testRunDone()); + testRun.once('stop', () => this._restart()); testRun.start(); diff --git a/src/test-run/index.js b/src/test-run/index.js index 247ee5beb8b..7858d1ae3de 100644 --- a/src/test-run/index.js +++ b/src/test-run/index.js @@ -376,6 +376,9 @@ export default class TestRun extends EventEmitter { } _rejectCurrentDriverTask (err) { + if (!this.currentDriverTask) + return; + err.callsite = err.callsite || this.driverTaskQueue[0].callsite; err.isRejectedDriverTask = true; @@ -666,6 +669,11 @@ export default class TestRun extends EventEmitter { return await getLocation(); } + + stop (err) { + this._rejectCurrentDriverTask(err); + this.emit('stop'); + } } diff --git a/test/functional/fixtures/browser-provider/browser-reconnect/pages/index.html b/test/functional/fixtures/browser-provider/browser-reconnect/pages/index.html new file mode 100644 index 00000000000..9fa7bf611d3 --- /dev/null +++ b/test/functional/fixtures/browser-provider/browser-reconnect/pages/index.html @@ -0,0 +1,10 @@ + + +
+ +