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

Commit 9654e03

Browse files
committed
feat(browser): chain promises in lib/browser.ts and return promise from waitForAngularEnabled
Minor breaking change since `waitForAngularEnabled` no longer returns a boolean Closes #3904 Also fixed a minor bug in `lib/clientsidescripts.js` while debuging
1 parent f11fece commit 9654e03

File tree

2 files changed

+167
-148
lines changed

2 files changed

+167
-148
lines changed

lib/browser.ts

+165-146
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,16 @@ function ptorMixin(to: any, from: any, fnName: string, setupFn?: Function) {
8888
arguments[i] = arguments[i].getWebElement();
8989
}
9090
}
91+
const run = () => {
92+
return from[fnName].apply(from, arguments);
93+
};
9194
if (setupFn) {
92-
setupFn();
95+
const setupResult = setupFn();
96+
if (setupResult && (typeof setupResult.then === 'function')) {
97+
return setupResult.then(run);
98+
}
9399
}
94-
return from[fnName].apply(from, arguments);
100+
return run();
95101
};
96102
};
97103

@@ -206,13 +212,7 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
206212
* @type {boolean}
207213
*/
208214
set ignoreSynchronization(value) {
209-
this.driver.controlFlow().execute(() => {
210-
if (this.bpClient) {
211-
logger.debug('Setting waitForAngular' + value);
212-
this.bpClient.setSynchronization(!value);
213-
}
214-
}, `Set proxy synchronization to ${value}`);
215-
this.internalIgnoreSynchronization = value;
215+
this.waitForAngularEnabled(!value);
216216
}
217217

218218
get ignoreSynchronization() {
@@ -403,11 +403,20 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
403403
* Call waitForAngularEnabled() without passing a value to read the current
404404
* state without changing it.
405405
*/
406-
waitForAngularEnabled(enabled: boolean = null): boolean {
406+
waitForAngularEnabled(enabled: boolean = null): wdpromise.Promise<boolean> {
407407
if (enabled != null) {
408-
this.ignoreSynchronization = !enabled;
408+
const ret = this.driver.controlFlow().execute(() => {
409+
if (this.bpClient) {
410+
logger.debug('Setting waitForAngular' + !enabled);
411+
return this.bpClient.setSynchronization(enabled).then(() => {
412+
return enabled;
413+
});
414+
}
415+
}, `Set proxy synchronization enabled to ${enabled}`);
416+
this.internalIgnoreSynchronization = !enabled;
417+
return ret;
409418
}
410-
return !this.ignoreSynchronization;
419+
return wdpromise.when(!this.ignoreSynchronization);
411420
}
412421

413422
/**
@@ -613,7 +622,9 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
613622

614623
let runWaitForAngularScript: () => wdpromise.Promise<any> = () => {
615624
if (this.plugins_.skipAngularStability() || this.bpClient) {
616-
return wdpromise.fulfilled();
625+
return this.driver.controlFlow().execute(() => {
626+
return true;
627+
}, 'A plugin has set skipAngularStability');
617628
} else {
618629
return this.executeAsyncScript_(
619630
clientSideScripts.waitForAngular, 'Protractor.waitForAngular()' + description,
@@ -833,131 +844,139 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
833844
get(destination: string, timeout = this.getPageTimeout) {
834845
destination = this.baseUrl.indexOf('file://') === 0 ? this.baseUrl + destination :
835846
url.resolve(this.baseUrl, destination);
836-
let msg = (str: string) => {
837-
return 'Protractor.get(' + destination + ') - ' + str;
838-
};
839-
840-
if (this.bpClient) {
841-
this.driver.controlFlow().execute(() => {
842-
return this.bpClient.setSynchronization(false);
843-
});
844-
}
845847

846848
if (this.ignoreSynchronization) {
847-
this.driver.get(destination);
848-
return this.driver.controlFlow().execute(() => this.plugins_.onPageLoad()).then(() => {});
849+
return this.driver.get(destination)
850+
.then(() => this.driver.controlFlow().execute(() => this.plugins_.onPageLoad()))
851+
.then(() => null);
849852
}
850853

851-
let deferred = wdpromise.defer<void>();
852-
853-
this.driver.get(this.resetUrl).then(null, deferred.reject);
854-
this.executeScriptWithDescription(
855-
'window.name = "' + DEFER_LABEL + '" + window.name;' +
856-
'window.location.replace("' + destination + '");',
857-
msg('reset url'))
858-
.then(null, deferred.reject);
859-
860-
// We need to make sure the new url has loaded before
861-
// we try to execute any asynchronous scripts.
862-
this.driver
863-
.wait(
864-
() => {
865-
return this
866-
.executeScriptWithDescription('return window.location.href;', msg('get url'))
867-
.then(
868-
(url: any) => {
869-
return url !== this.resetUrl;
870-
},
871-
(err: IError) => {
872-
if (err.code == 13) {
873-
// Ignore the error, and continue trying. This is
874-
// because IE driver sometimes (~1%) will throw an
875-
// unknown error from this execution. See
876-
// https://github.com/angular/protractor/issues/841
877-
// This shouldn't mask errors because it will fail
878-
// with the timeout anyway.
879-
return false;
880-
} else {
881-
throw err;
882-
}
883-
});
884-
},
885-
timeout, 'waiting for page to load for ' + timeout + 'ms')
886-
.then(null, deferred.reject);
887-
888-
this.driver.controlFlow().execute(() => {
889-
return this.plugins_.onPageLoad();
890-
});
854+
let msg = (str: string) => {
855+
return 'Protractor.get(' + destination + ') - ' + str;
856+
};
891857

892-
// Make sure the page is an Angular page.
893-
this.executeAsyncScript_(
894-
clientSideScripts.testForAngular, msg('test for angular'), Math.floor(timeout / 1000),
895-
this.ng12Hybrid)
896-
.then(
897-
(angularTestResult: {ver: number, message: string}) => {
898-
let angularVersion = angularTestResult.ver;
899-
if (!angularVersion) {
900-
let message = angularTestResult.message;
901-
logger.error(`Could not find Angular on page ${destination} : ${message}`);
902-
throw new Error(
903-
`Angular could not be found on the page ${destination}. If this is not an ` +
904-
`Angular application, you may need to turn off waiting for Angular. Please ` +
905-
`see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load`);
906-
}
907-
return angularVersion;
908-
},
909-
(err: Error) => {
910-
throw new Error('Error while running testForAngular: ' + err.message);
911-
})
912-
.then(loadMocks, deferred.reject);
913-
914-
let self = this;
915-
function loadMocks(angularVersion: number) {
916-
if (angularVersion === 1) {
917-
// At this point, Angular will pause for us until angular.resumeBootstrap is called.
918-
let moduleNames: string[] = [];
919-
for (const {name, script, args} of self.mockModules_) {
920-
moduleNames.push(name);
921-
let executeScriptArgs = [script, msg('add mock module ' + name), ...args];
922-
self.executeScriptWithDescription.apply(self, executeScriptArgs)
858+
return this.driver.controlFlow()
859+
.execute(() => {
860+
return wdpromise.when(null);
861+
})
862+
.then(() => {
863+
if (this.bpClient) {
864+
return this.driver.controlFlow().execute(() => {
865+
return this.bpClient.setSynchronization(false);
866+
});
867+
}
868+
})
869+
.then(() => {
870+
// Go to reset url
871+
return this.driver.get(this.resetUrl);
872+
})
873+
.then(() => {
874+
// Set defer label and navigate
875+
return this.executeScriptWithDescription(
876+
'window.name = "' + DEFER_LABEL + '" + window.name;' +
877+
'window.location.replace("' + destination + '");',
878+
msg('reset url'));
879+
})
880+
.then(() => {
881+
// We need to make sure the new url has loaded before
882+
// we try to execute any asynchronous scripts.
883+
return this.driver.wait(() => {
884+
return this.executeScriptWithDescription('return window.location.href;', msg('get url'))
885+
.then(
886+
(url: any) => {
887+
return url !== this.resetUrl;
888+
},
889+
(err: IError) => {
890+
if (err.code == 13) {
891+
// Ignore the error, and continue trying. This is
892+
// because IE driver sometimes (~1%) will throw an
893+
// unknown error from this execution. See
894+
// https://github.com/angular/protractor/issues/841
895+
// This shouldn't mask errors because it will fail
896+
// with the timeout anyway.
897+
return false;
898+
} else {
899+
throw err;
900+
}
901+
});
902+
}, timeout, 'waiting for page to load for ' + timeout + 'ms');
903+
})
904+
.then(() => {
905+
// Run Plugins
906+
return this.driver.controlFlow().execute(() => {
907+
return this.plugins_.onPageLoad();
908+
});
909+
})
910+
.then(() => {
911+
// Make sure the page is an Angular page.
912+
return this
913+
.executeAsyncScript_(
914+
clientSideScripts.testForAngular, msg('test for angular'),
915+
Math.floor(timeout / 1000), this.ng12Hybrid)
923916
.then(
924-
null,
917+
(angularTestResult: {ver: number, message: string}) => {
918+
let angularVersion = angularTestResult.ver;
919+
if (!angularVersion) {
920+
let message = angularTestResult.message;
921+
logger.error(`Could not find Angular on page ${destination} : ${message}`);
922+
throw new Error(
923+
'Angular could not be found on the page ${destination}.' +
924+
`If this is not an Angular application, you may need to turn off waiting for Angular.
925+
Please see
926+
https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load`);
927+
}
928+
return angularVersion;
929+
},
925930
(err: Error) => {
926-
throw new Error(
927-
'Error while running module script ' + name + ': ' + err.message);
928-
})
929-
.then(null, deferred.reject);
930-
}
931-
932-
self.executeScriptWithDescription(
933-
'window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' +
934-
'angular.resumeBootstrap(arguments[0]);',
935-
msg('resume bootstrap'), moduleNames)
936-
.then(null, deferred.reject);
937-
} else {
938-
// TODO: support mock modules in Angular2. For now, error if someone
939-
// has tried to use one.
940-
if (self.mockModules_.length > 1) {
941-
deferred.reject(
942-
'Trying to load mock modules on an Angular2 app ' +
943-
'is not yet supported.');
944-
}
945-
}
946-
}
947-
948-
if (this.bpClient) {
949-
this.driver.controlFlow().execute(() => {
950-
return this.bpClient.setSynchronization(!this.internalIgnoreSynchronization);
951-
});
952-
}
953-
954-
this.driver.controlFlow().execute(() => {
955-
return this.plugins_.onPageStable().then(() => {
956-
deferred.fulfill();
957-
}, deferred.reject);
958-
});
931+
throw new Error('Error while running testForAngular: ' + err.message);
932+
});
959933

960-
return deferred.promise;
934+
})
935+
.then((angularVersion) => {
936+
// Load Angular Mocks
937+
if (angularVersion === 1) {
938+
// At this point, Angular will pause for us until angular.resumeBootstrap is called.
939+
let moduleNames: string[] = [];
940+
let modulePromise: wdpromise.Promise<void> = wdpromise.when(null);
941+
for (const {name, script, args} of this.mockModules_) {
942+
moduleNames.push(name);
943+
let executeScriptArgs = [script, msg('add mock module ' + name), ...args];
944+
modulePromise = modulePromise.then(
945+
() => this.executeScriptWithDescription.apply(this, executeScriptArgs)
946+
.then(null, (err: Error) => {
947+
throw new Error(
948+
'Error while running module script ' + name + ': ' + err.message);
949+
}));
950+
}
951+
952+
return modulePromise.then(
953+
() => this.executeScriptWithDescription(
954+
'window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' +
955+
'angular.resumeBootstrap(arguments[0]);',
956+
msg('resume bootstrap'), moduleNames));
957+
} else {
958+
// TODO: support mock modules in Angular2. For now, error if someone
959+
// has tried to use one.
960+
if (this.mockModules_.length > 1) {
961+
throw 'Trying to load mock modules on an Angular2 app is not yet supported.';
962+
}
963+
}
964+
})
965+
.then(() => {
966+
// Reset bpClient sync
967+
if (this.bpClient) {
968+
return this.driver.controlFlow().execute(() => {
969+
return this.bpClient.setSynchronization(!this.internalIgnoreSynchronization);
970+
});
971+
}
972+
})
973+
.then(() => {
974+
// Run Plugins
975+
return this.driver.controlFlow().execute(() => {
976+
return this.plugins_.onPageStable();
977+
});
978+
})
979+
.then(() => null);
961980
}
962981

963982
/**
@@ -1007,15 +1026,15 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
10071026
* page has been changed.
10081027
*/
10091028
setLocation(url: string): wdpromise.Promise<any> {
1010-
this.waitForAngular();
1011-
return this
1012-
.executeScriptWithDescription(
1013-
clientSideScripts.setLocation, 'Protractor.setLocation()', this.rootEl, url)
1014-
.then((browserErr: Error) => {
1015-
if (browserErr) {
1016-
throw 'Error while navigating to \'' + url + '\' : ' + JSON.stringify(browserErr);
1017-
}
1018-
});
1029+
return this.waitForAngular().then(
1030+
() => this.executeScriptWithDescription(
1031+
clientSideScripts.setLocation, 'Protractor.setLocation()', this.rootEl, url)
1032+
.then((browserErr: Error) => {
1033+
if (browserErr) {
1034+
throw 'Error while navigating to \'' + url + '\' : ' +
1035+
JSON.stringify(browserErr);
1036+
}
1037+
}));
10191038
}
10201039

10211040
/**
@@ -1029,9 +1048,9 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
10291048
* AngularJS.
10301049
*/
10311050
getLocationAbsUrl(): wdpromise.Promise<any> {
1032-
this.waitForAngular();
1033-
return this.executeScriptWithDescription(
1034-
clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', this.rootEl);
1051+
return this.waitForAngular().then(
1052+
() => this.executeScriptWithDescription(
1053+
clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', this.rootEl));
10351054
}
10361055

10371056
/**
@@ -1056,10 +1075,10 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
10561075
*/
10571076
debugger() {
10581077
// jshint debug: true
1059-
this.driver.executeScript(clientSideScripts.installInBrowser);
1060-
wdpromise.controlFlow().execute(() => {
1061-
debugger;
1062-
}, 'add breakpoint to control flow');
1078+
return this.driver.executeScript(clientSideScripts.installInBrowser)
1079+
.then(() => wdpromise.controlFlow().execute(() => {
1080+
debugger;
1081+
}, 'add breakpoint to control flow'));
10631082
}
10641083

10651084
/**

lib/clientsidescripts.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ function getNg1Hooks(selector, injectorPlease) {
109109
return {$injector: $injector, $$testability: $$testability};
110110
} else {
111111
return tryEl(document.body) ||
112-
trySelector('[ng-app]') || trySelector('[ng:app]') ||
113-
trySelector('[ng-controller]') || trySelector('[ng:controller]');
112+
trySelector('[ng-app]') || trySelector('[ng\\:app]') ||
113+
trySelector('[ng-controller]') || trySelector('[ng\\:controller]');
114114
}
115115
}
116116

0 commit comments

Comments
 (0)