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

Commit 741e412

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 d48392c commit 741e412

File tree

2 files changed

+167
-148
lines changed

2 files changed

+167
-148
lines changed

Diff for: lib/browser.ts

+165-146
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,16 @@ function ptorMixin(to: any, from: any, fnName: string, setupFn?: Function) {
134134
arguments[i] = arguments[i].getWebElement();
135135
}
136136
}
137+
const run = () => {
138+
return from[fnName].apply(from, arguments);
139+
};
137140
if (setupFn) {
138-
setupFn();
141+
const setupResult = setupFn();
142+
if (setupResult && (typeof setupResult.then === 'function')) {
143+
return setupResult.then(run);
144+
}
139145
}
140-
return from[fnName].apply(from, arguments);
146+
return run();
141147
};
142148
};
143149

@@ -252,13 +258,7 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
252258
* @type {boolean}
253259
*/
254260
set ignoreSynchronization(value) {
255-
this.driver.controlFlow().execute(() => {
256-
if (this.bpClient) {
257-
logger.debug('Setting waitForAngular' + value);
258-
this.bpClient.setSynchronization(!value);
259-
}
260-
}, `Set proxy synchronization to ${value}`);
261-
this.internalIgnoreSynchronization = value;
261+
this.waitForAngularEnabled(!value);
262262
}
263263

264264
get ignoreSynchronization() {
@@ -449,11 +449,20 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
449449
* Call waitForAngularEnabled() without passing a value to read the current
450450
* state without changing it.
451451
*/
452-
waitForAngularEnabled(enabled: boolean = null): boolean {
452+
waitForAngularEnabled(enabled: boolean = null): wdpromise.Promise<boolean> {
453453
if (enabled != null) {
454-
this.ignoreSynchronization = !enabled;
454+
const ret = this.driver.controlFlow().execute(() => {
455+
if (this.bpClient) {
456+
logger.debug('Setting waitForAngular' + !enabled);
457+
return this.bpClient.setSynchronization(enabled).then(() => {
458+
return enabled;
459+
});
460+
}
461+
}, `Set proxy synchronization enabled to ${enabled}`);
462+
this.internalIgnoreSynchronization = !enabled;
463+
return ret;
455464
}
456-
return !this.ignoreSynchronization;
465+
return wdpromise.when(!this.ignoreSynchronization);
457466
}
458467

459468
/**
@@ -659,7 +668,9 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
659668

660669
let runWaitForAngularScript: () => wdpromise.Promise<any> = () => {
661670
if (this.plugins_.skipAngularStability() || this.bpClient) {
662-
return wdpromise.when(null);
671+
return this.driver.controlFlow().execute(() => {
672+
return wdpromise.when(null);
673+
}, 'bpClient or plugin stability override');
663674
} else {
664675
return this.executeAsyncScript_(
665676
clientSideScripts.waitForAngular, 'Protractor.waitForAngular()' + description,
@@ -879,131 +890,139 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
879890
get(destination: string, timeout = this.getPageTimeout) {
880891
destination = this.baseUrl.indexOf('file://') === 0 ? this.baseUrl + destination :
881892
url.resolve(this.baseUrl, destination);
882-
let msg = (str: string) => {
883-
return 'Protractor.get(' + destination + ') - ' + str;
884-
};
885-
886-
if (this.bpClient) {
887-
this.driver.controlFlow().execute(() => {
888-
return this.bpClient.setSynchronization(false);
889-
});
890-
}
891893

892894
if (this.ignoreSynchronization) {
893-
this.driver.get(destination);
894-
return this.driver.controlFlow().execute(() => this.plugins_.onPageLoad()).then(() => {});
895+
return this.driver.get(destination)
896+
.then(() => this.driver.controlFlow().execute(() => this.plugins_.onPageLoad()))
897+
.then(() => null);
895898
}
896899

897-
let deferred = wdpromise.defer<void>();
898-
899-
this.driver.get(this.resetUrl).then(null, deferred.reject);
900-
this.executeScriptWithDescription(
901-
'window.name = "' + DEFER_LABEL + '" + window.name;' +
902-
'window.location.replace("' + destination + '");',
903-
msg('reset url'))
904-
.then(null, deferred.reject);
905-
906-
// We need to make sure the new url has loaded before
907-
// we try to execute any asynchronous scripts.
908-
this.driver
909-
.wait(
910-
() => {
911-
return this
912-
.executeScriptWithDescription('return window.location.href;', msg('get url'))
913-
.then(
914-
(url: any) => {
915-
return url !== this.resetUrl;
916-
},
917-
(err: IError) => {
918-
if (err.code == 13) {
919-
// Ignore the error, and continue trying. This is
920-
// because IE driver sometimes (~1%) will throw an
921-
// unknown error from this execution. See
922-
// https://github.com/angular/protractor/issues/841
923-
// This shouldn't mask errors because it will fail
924-
// with the timeout anyway.
925-
return false;
926-
} else {
927-
throw err;
928-
}
929-
});
930-
},
931-
timeout, 'waiting for page to load for ' + timeout + 'ms')
932-
.then(null, deferred.reject);
933-
934-
this.driver.controlFlow().execute(() => {
935-
return this.plugins_.onPageLoad();
936-
});
900+
let msg = (str: string) => {
901+
return 'Protractor.get(' + destination + ') - ' + str;
902+
};
937903

938-
// Make sure the page is an Angular page.
939-
this.executeAsyncScript_(
940-
clientSideScripts.testForAngular, msg('test for angular'), Math.floor(timeout / 1000),
941-
this.ng12Hybrid)
942-
.then(
943-
(angularTestResult: {ver: number, message: string}) => {
944-
let angularVersion = angularTestResult.ver;
945-
if (!angularVersion) {
946-
let message = angularTestResult.message;
947-
logger.error(`Could not find Angular on page ${destination} : ${message}`);
948-
throw new Error(
949-
`Angular could not be found on the page ${destination}. If this is not an ` +
950-
`Angular application, you may need to turn off waiting for Angular. Please ` +
951-
`see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load`);
952-
}
953-
return angularVersion;
954-
},
955-
(err: Error) => {
956-
throw new Error('Error while running testForAngular: ' + err.message);
957-
})
958-
.then(loadMocks, deferred.reject);
959-
960-
let self = this;
961-
function loadMocks(angularVersion: number) {
962-
if (angularVersion === 1) {
963-
// At this point, Angular will pause for us until angular.resumeBootstrap is called.
964-
let moduleNames: string[] = [];
965-
for (const {name, script, args} of self.mockModules_) {
966-
moduleNames.push(name);
967-
let executeScriptArgs = [script, msg('add mock module ' + name), ...args];
968-
self.executeScriptWithDescription.apply(self, executeScriptArgs)
904+
return this.driver.controlFlow()
905+
.execute(() => {
906+
return wdpromise.when(null);
907+
})
908+
.then(() => {
909+
if (this.bpClient) {
910+
return this.driver.controlFlow().execute(() => {
911+
return this.bpClient.setSynchronization(false);
912+
});
913+
}
914+
})
915+
.then(() => {
916+
// Go to reset url
917+
return this.driver.get(this.resetUrl);
918+
})
919+
.then(() => {
920+
// Set defer label and navigate
921+
return this.executeScriptWithDescription(
922+
'window.name = "' + DEFER_LABEL + '" + window.name;' +
923+
'window.location.replace("' + destination + '");',
924+
msg('reset url'));
925+
})
926+
.then(() => {
927+
// We need to make sure the new url has loaded before
928+
// we try to execute any asynchronous scripts.
929+
return this.driver.wait(() => {
930+
return this.executeScriptWithDescription('return window.location.href;', msg('get url'))
931+
.then(
932+
(url: any) => {
933+
return url !== this.resetUrl;
934+
},
935+
(err: IError) => {
936+
if (err.code == 13) {
937+
// Ignore the error, and continue trying. This is
938+
// because IE driver sometimes (~1%) will throw an
939+
// unknown error from this execution. See
940+
// https://github.com/angular/protractor/issues/841
941+
// This shouldn't mask errors because it will fail
942+
// with the timeout anyway.
943+
return false;
944+
} else {
945+
throw err;
946+
}
947+
});
948+
}, timeout, 'waiting for page to load for ' + timeout + 'ms');
949+
})
950+
.then(() => {
951+
// Run Plugins
952+
return this.driver.controlFlow().execute(() => {
953+
return this.plugins_.onPageLoad();
954+
});
955+
})
956+
.then(() => {
957+
// Make sure the page is an Angular page.
958+
return this
959+
.executeAsyncScript_(
960+
clientSideScripts.testForAngular, msg('test for angular'),
961+
Math.floor(timeout / 1000), this.ng12Hybrid)
969962
.then(
970-
null,
963+
(angularTestResult: {ver: number, message: string}) => {
964+
let angularVersion = angularTestResult.ver;
965+
if (!angularVersion) {
966+
let message = angularTestResult.message;
967+
logger.error(`Could not find Angular on page ${destination} : ${message}`);
968+
throw new Error(
969+
'Angular could not be found on the page ${destination}.' +
970+
`If this is not an Angular application, you may need to turn off waiting for Angular.
971+
Please see
972+
https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load`);
973+
}
974+
return angularVersion;
975+
},
971976
(err: Error) => {
972-
throw new Error(
973-
'Error while running module script ' + name + ': ' + err.message);
974-
})
975-
.then(null, deferred.reject);
976-
}
977-
978-
self.executeScriptWithDescription(
979-
'window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' +
980-
'angular.resumeBootstrap(arguments[0]);',
981-
msg('resume bootstrap'), moduleNames)
982-
.then(null, deferred.reject);
983-
} else {
984-
// TODO: support mock modules in Angular2. For now, error if someone
985-
// has tried to use one.
986-
if (self.mockModules_.length > 1) {
987-
deferred.reject(
988-
'Trying to load mock modules on an Angular2 app ' +
989-
'is not yet supported.');
990-
}
991-
}
992-
}
993-
994-
if (this.bpClient) {
995-
this.driver.controlFlow().execute(() => {
996-
return this.bpClient.setSynchronization(!this.internalIgnoreSynchronization);
997-
});
998-
}
999-
1000-
this.driver.controlFlow().execute(() => {
1001-
return this.plugins_.onPageStable().then(() => {
1002-
deferred.fulfill();
1003-
}, deferred.reject);
1004-
});
977+
throw new Error('Error while running testForAngular: ' + err.message);
978+
});
1005979

1006-
return deferred.promise;
980+
})
981+
.then((angularVersion) => {
982+
// Load Angular Mocks
983+
if (angularVersion === 1) {
984+
// At this point, Angular will pause for us until angular.resumeBootstrap is called.
985+
let moduleNames: string[] = [];
986+
let modulePromise: wdpromise.Promise<void> = wdpromise.when(null);
987+
for (const {name, script, args} of this.mockModules_) {
988+
moduleNames.push(name);
989+
let executeScriptArgs = [script, msg('add mock module ' + name), ...args];
990+
modulePromise = modulePromise.then(
991+
() => this.executeScriptWithDescription.apply(this, executeScriptArgs)
992+
.then(null, (err: Error) => {
993+
throw new Error(
994+
'Error while running module script ' + name + ': ' + err.message);
995+
}));
996+
}
997+
998+
return modulePromise.then(
999+
() => this.executeScriptWithDescription(
1000+
'window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' +
1001+
'angular.resumeBootstrap(arguments[0]);',
1002+
msg('resume bootstrap'), moduleNames));
1003+
} else {
1004+
// TODO: support mock modules in Angular2. For now, error if someone
1005+
// has tried to use one.
1006+
if (this.mockModules_.length > 1) {
1007+
throw 'Trying to load mock modules on an Angular2 app is not yet supported.';
1008+
}
1009+
}
1010+
})
1011+
.then(() => {
1012+
// Reset bpClient sync
1013+
if (this.bpClient) {
1014+
return this.driver.controlFlow().execute(() => {
1015+
return this.bpClient.setSynchronization(!this.internalIgnoreSynchronization);
1016+
});
1017+
}
1018+
})
1019+
.then(() => {
1020+
// Run Plugins
1021+
return this.driver.controlFlow().execute(() => {
1022+
return this.plugins_.onPageStable();
1023+
});
1024+
})
1025+
.then(() => null);
10071026
}
10081027

10091028
/**
@@ -1053,15 +1072,15 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
10531072
* page has been changed.
10541073
*/
10551074
setLocation(url: string): wdpromise.Promise<any> {
1056-
this.waitForAngular();
1057-
return this
1058-
.executeScriptWithDescription(
1059-
clientSideScripts.setLocation, 'Protractor.setLocation()', this.rootEl, url)
1060-
.then((browserErr: Error) => {
1061-
if (browserErr) {
1062-
throw 'Error while navigating to \'' + url + '\' : ' + JSON.stringify(browserErr);
1063-
}
1064-
});
1075+
return this.waitForAngular().then(
1076+
() => this.executeScriptWithDescription(
1077+
clientSideScripts.setLocation, 'Protractor.setLocation()', this.rootEl, url)
1078+
.then((browserErr: Error) => {
1079+
if (browserErr) {
1080+
throw 'Error while navigating to \'' + url + '\' : ' +
1081+
JSON.stringify(browserErr);
1082+
}
1083+
}));
10651084
}
10661085

10671086
/**
@@ -1075,9 +1094,9 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
10751094
* AngularJS.
10761095
*/
10771096
getLocationAbsUrl(): wdpromise.Promise<any> {
1078-
this.waitForAngular();
1079-
return this.executeScriptWithDescription(
1080-
clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', this.rootEl);
1097+
return this.waitForAngular().then(
1098+
() => this.executeScriptWithDescription(
1099+
clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', this.rootEl));
10811100
}
10821101

10831102
/**
@@ -1102,10 +1121,10 @@ export class ProtractorBrowser extends AbstractExtendedWebDriver {
11021121
*/
11031122
debugger() {
11041123
// jshint debug: true
1105-
this.driver.executeScript(clientSideScripts.installInBrowser);
1106-
wdpromise.controlFlow().execute(() => {
1107-
debugger;
1108-
}, 'add breakpoint to control flow');
1124+
return this.driver.executeScript(clientSideScripts.installInBrowser)
1125+
.then(() => wdpromise.controlFlow().execute(() => {
1126+
debugger;
1127+
}, 'add breakpoint to control flow'));
11091128
}
11101129

11111130
/**

Diff for: 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)