Skip to content

Commit

Permalink
feat(browser): chain promises in lib/browser.ts and return promise …
Browse files Browse the repository at this point in the history
…from `waitForAngularEnabled`

Minor breaking change since `waitForAngularEnabled` no longer returns a boolean

Closes angular#3904
  • Loading branch information
sjelin committed Jan 26, 2017
1 parent f11fece commit 011eb00
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 111 deletions.
229 changes: 120 additions & 109 deletions lib/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,16 @@ function ptorMixin(to: any, from: any, fnName: string, setupFn?: Function) {
arguments[i] = arguments[i].getWebElement();
}
}
const run = () => {
return from[fnName].apply(from, arguments);
};
if (setupFn) {
setupFn();
const setupResult = setupFn();
if (setupResult && (typeof setupResult.then === 'function')) {
return setupResult.then(run);
}
}
return from[fnName].apply(from, arguments);
return run();
};
};

Expand Down Expand Up @@ -206,13 +212,7 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
* @type {boolean}
*/
set ignoreSynchronization(value) {
this.driver.controlFlow().execute(() => {
if (this.bpClient) {
logger.debug('Setting waitForAngular' + value);
this.bpClient.setSynchronization(!value);
}
}, `Set proxy synchronization to ${value}`);
this.internalIgnoreSynchronization = value;
this.waitForAngularEnabled(!value);
}

get ignoreSynchronization() {
Expand Down Expand Up @@ -403,11 +403,20 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
* Call waitForAngularEnabled() without passing a value to read the current
* state without changing it.
*/
waitForAngularEnabled(enabled: boolean = null): boolean {
waitForAngularEnabled(enabled: boolean = null): wdpromise.Promise<boolean> {
if (enabled != null) {
this.ignoreSynchronization = !enabled;
const ret = this.driver.controlFlow().execute(() => {
if (this.bpClient) {
logger.debug('Setting waitForAngular' + !enabled);
return this.bpClient.setSynchronization(enabled).then(() => {
return enabled;
});
}
}, `Set proxy synchronization enabled to ${enabled}`);
this.internalIgnoreSynchronization = !enabled;
return ret;
}
return !this.ignoreSynchronization;
return wdpromise.when(!this.ignoreSynchronization);
}

/**
Expand Down Expand Up @@ -613,7 +622,9 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {

let runWaitForAngularScript: () => wdpromise.Promise<any> = () => {
if (this.plugins_.skipAngularStability() || this.bpClient) {
return wdpromise.fulfilled();
return this.driver.controlFlow().execute(() => {
return true;
}, 'A plugin has set skipAngularStability');
} else {
return this.executeAsyncScript_(
clientSideScripts.waitForAngular, 'Protractor.waitForAngular()' + description,
Expand Down Expand Up @@ -837,79 +848,84 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
return 'Protractor.get(' + destination + ') - ' + str;
};

let retPromise: wdpromise.Promise<any>;
let then = (fn: () => wdpromise.Promise<any>) => {
if (retPromise) {
return (retPromise = retPromise.then(fn));
} else {
return (retPromise = fn());
}
};

if (this.bpClient) {
this.driver.controlFlow().execute(() => {
then(() => this.driver.controlFlow().execute(() => {
return this.bpClient.setSynchronization(false);
});
}));
}

if (this.ignoreSynchronization) {
this.driver.get(destination);
return this.driver.controlFlow().execute(() => this.plugins_.onPageLoad()).then(() => {});
then(() => this.driver.get(destination));
return then(() => this.driver.controlFlow().execute(() => this.plugins_.onPageLoad()))
.then(() => {});
}

let deferred = wdpromise.defer<void>();

this.driver.get(this.resetUrl).then(null, deferred.reject);
this.executeScriptWithDescription(
then(() => this.driver.get(this.resetUrl));
then(
() => this.executeScriptWithDescription(
'window.name = "' + DEFER_LABEL + '" + window.name;' +
'window.location.replace("' + destination + '");',
msg('reset url'))
.then(null, deferred.reject);
msg('reset url')));

// We need to make sure the new url has loaded before
// we try to execute any asynchronous scripts.
this.driver
.wait(
() => {
return this
.executeScriptWithDescription('return window.location.href;', msg('get url'))
.then(
(url: any) => {
return url !== this.resetUrl;
},
(err: IError) => {
if (err.code == 13) {
// Ignore the error, and continue trying. This is
// because IE driver sometimes (~1%) will throw an
// unknown error from this execution. See
// https://github.com/angular/protractor/issues/841
// This shouldn't mask errors because it will fail
// with the timeout anyway.
return false;
} else {
throw err;
}
});
},
timeout, 'waiting for page to load for ' + timeout + 'ms')
.then(null, deferred.reject);
then(() => this.driver.wait(() => {
return this.executeScriptWithDescription('return window.location.href;', msg('get url'))
.then(
(url: any) => {
return url !== this.resetUrl;
},
(err: IError) => {
if (err.code == 13) {
// Ignore the error, and continue trying. This is
// because IE driver sometimes (~1%) will throw an
// unknown error from this execution. See
// https://github.com/angular/protractor/issues/841
// This shouldn't mask errors because it will fail
// with the timeout anyway.
return false;
} else {
throw err;
}
});
}, timeout, 'waiting for page to load for ' + timeout + 'ms'));

this.driver.controlFlow().execute(() => {
then(() => this.driver.controlFlow().execute(() => {
return this.plugins_.onPageLoad();
});
}));

// Make sure the page is an Angular page.
this.executeAsyncScript_(
clientSideScripts.testForAngular, msg('test for angular'), Math.floor(timeout / 1000),
this.ng12Hybrid)
.then(
(angularTestResult: {ver: number, message: string}) => {
let angularVersion = angularTestResult.ver;
if (!angularVersion) {
let message = angularTestResult.message;
logger.error(`Could not find Angular on page ${destination} : ${message}`);
throw new Error(
`Angular could not be found on the page ${destination}. If this is not an ` +
`Angular application, you may need to turn off waiting for Angular. Please ` +
`see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load`);
}
return angularVersion;
},
(err: Error) => {
throw new Error('Error while running testForAngular: ' + err.message);
})
.then(loadMocks, deferred.reject);
then(
() =>
this.executeAsyncScript_(
clientSideScripts.testForAngular, msg('test for angular'),
Math.floor(timeout / 1000), this.ng12Hybrid)
.then(
(angularTestResult: {ver: number, message: string}) => {
let angularVersion = angularTestResult.ver;
if (!angularVersion) {
let message = angularTestResult.message;
logger.error(`Could not find Angular on page ${destination} : ${message}`);
throw new Error(
`Angular could not be found on the page ${destination
}. If this is not an ` +
`Angular application, you may need to turn off waiting for Angular. Please ` +
`see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load`);
}
return angularVersion;
},
(err: Error) => {
throw new Error('Error while running testForAngular: ' + err.message);
}));

let self = this;
function loadMocks(angularVersion: number) {
Expand All @@ -919,45 +935,40 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
for (const {name, script, args} of self.mockModules_) {
moduleNames.push(name);
let executeScriptArgs = [script, msg('add mock module ' + name), ...args];
self.executeScriptWithDescription.apply(self, executeScriptArgs)
.then(
null,
(err: Error) => {
throw new Error(
'Error while running module script ' + name + ': ' + err.message);
})
.then(null, deferred.reject);
then(
() => self.executeScriptWithDescription.apply(self, executeScriptArgs)
.then(null, (err: Error) => {
throw new Error(
'Error while running module script ' + name + ': ' + err.message);
}));
}

self.executeScriptWithDescription(
then(
() => self.executeScriptWithDescription(
'window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' +
'angular.resumeBootstrap(arguments[0]);',
msg('resume bootstrap'), moduleNames)
.then(null, deferred.reject);
msg('resume bootstrap'), moduleNames));
} else {
// TODO: support mock modules in Angular2. For now, error if someone
// has tried to use one.
if (self.mockModules_.length > 1) {
deferred.reject(
'Trying to load mock modules on an Angular2 app ' +
'is not yet supported.');
then(() => {
throw 'Trying to load mock modules on an Angular2 app is not yet supported.';
});
}
}
}

if (this.bpClient) {
this.driver.controlFlow().execute(() => {
then(() => this.driver.controlFlow().execute(() => {
return this.bpClient.setSynchronization(!this.internalIgnoreSynchronization);
});
}));
}

this.driver.controlFlow().execute(() => {
return this.plugins_.onPageStable().then(() => {
deferred.fulfill();
}, deferred.reject);
});

return deferred.promise;
return then(() => this.driver.controlFlow().execute(() => {
return this.plugins_.onPageStable();
}))
.then(() => {});
}

/**
Expand Down Expand Up @@ -1007,15 +1018,15 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
* page has been changed.
*/
setLocation(url: string): wdpromise.Promise<any> {
this.waitForAngular();
return this
.executeScriptWithDescription(
clientSideScripts.setLocation, 'Protractor.setLocation()', this.rootEl, url)
.then((browserErr: Error) => {
if (browserErr) {
throw 'Error while navigating to \'' + url + '\' : ' + JSON.stringify(browserErr);
}
});
return this.waitForAngular().then(
() => this.executeScriptWithDescription(
clientSideScripts.setLocation, 'Protractor.setLocation()', this.rootEl, url)
.then((browserErr: Error) => {
if (browserErr) {
throw 'Error while navigating to \'' + url + '\' : ' +
JSON.stringify(browserErr);
}
}));
}

/**
Expand All @@ -1029,9 +1040,9 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
* AngularJS.
*/
getLocationAbsUrl(): wdpromise.Promise<any> {
this.waitForAngular();
return this.executeScriptWithDescription(
clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', this.rootEl);
return this.waitForAngular().then(
() => this.executeScriptWithDescription(
clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', this.rootEl));
}

/**
Expand All @@ -1056,10 +1067,10 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
*/
debugger() {
// jshint debug: true
this.driver.executeScript(clientSideScripts.installInBrowser);
wdpromise.controlFlow().execute(() => {
debugger;
}, 'add breakpoint to control flow');
return this.driver.executeScript(clientSideScripts.installInBrowser)
.then(() => wdpromise.controlFlow().execute(() => {
debugger;
}, 'add breakpoint to control flow'));
}

/**
Expand Down
4 changes: 2 additions & 2 deletions lib/clientsidescripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ function getNg1Hooks(selector, injectorPlease) {
return {$injector: $injector, $$testability: $$testability};
} else {
return tryEl(document.body) ||
trySelector('[ng-app]') || trySelector('[ng:app]') ||
trySelector('[ng-controller]') || trySelector('[ng:controller]');
trySelector('[ng-app]') || trySelector('[ng\\:app]') ||
trySelector('[ng-controller]') || trySelector('[ng\\:controller]');
}
}

Expand Down

0 comments on commit 011eb00

Please sign in to comment.