diff --git a/lib/expectedConditions.ts b/lib/expectedConditions.ts index 9f006aea4..c92eaa3c6 100644 --- a/lib/expectedConditions.ts +++ b/lib/expectedConditions.ts @@ -388,7 +388,15 @@ export class ProtractorExpectedConditions { * representing whether the element is visible. */ visibilityOf(elementFinder: ElementFinder): Function { - return this.and(this.presenceOf(elementFinder), elementFinder.isDisplayed.bind(elementFinder)); + return this.and(this.presenceOf(elementFinder), () => { + return elementFinder.isDisplayed().then((displayed: boolean) => displayed, (err: any) => { + if (err instanceof wderror.NoSuchElementError) { + return false; + } else { + throw err; + } + }); + }); } /** diff --git a/spec/basic/expected_conditions_spec.js b/spec/basic/expected_conditions_spec.js index 048087a6f..d110ded1d 100644 --- a/spec/basic/expected_conditions_spec.js +++ b/spec/basic/expected_conditions_spec.js @@ -45,6 +45,26 @@ describe('expected conditions', function() { expect(visibilityOfHideable.call()).toBe(false); }); + it('should have visibilityOf (handling race conditions)', function() { + var disabledButton = $('#disabledButton[disabled="disabled"]'); + + // toggle presence (of .ng-hide) between visibility evaluation to simulate race condition + var originalIsDisplayedFn = disabledButton.isDisplayed; + disabledButton.isDisplayed = function () { + element(by.model('disabled')).click(); + return originalIsDisplayedFn.call(this); + }; + + var visibilityOfDisabledButtonWithInterceptor = EC.visibilityOf(disabledButton); + + element(by.model('disabled')).click(); + + expect(originalIsDisplayedFn.call(disabledButton)).toBe(true); + expect(disabledButton.isPresent()).toBe(true); + + expect(visibilityOfDisabledButtonWithInterceptor.call()).toBe(false); + }); + it('should have invisibilityOf', function() { var invisibilityOfInvalid = EC.invisibilityOf($('#INVALID')); var invisibilityOfHideable = EC.invisibilityOf($('#shower'));