4

Is there a way to have an expectation that an element is eventually on the page? e.g. a way for

browser.wait(protractor.ExpectedConditions.presenceOf(element(by.partialLinkText('Continue'))), 1000, 'Unable to find continue link');

to fail with an expectation error instead of a timeout? Essentially a way to have a isEventuallyPresent() instead of isPresent() in the line below

expect(element(by.partialLinkText('Continue')).isPresent()).toBe(true);

For reference, I'm using browser.ignoreSynchronization = true even though it's an Angular app, and using Jasmine (at least for now).

Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
  • Not sure I'm fully understanding but I'll post as a comment for now -- would anything under `Expected Conditions` [link here](https://angular.github.io/protractor/#/api?view=ExpectedConditions) solve your problem? Specifically `presenceOf` and `visibilityOf` -- I use these to wait for Modals (since they are dynamically appended to HTML only after they are triggered) before beginning test execution. – Gunderson May 10 '16 at 15:49
  • Adding to that - you can use `stalenessOf` to make sure an element is **not** present in the DOM, then use the others listed above make sure it is there later. – Gunderson May 10 '16 at 15:56

2 Answers2

5

Using the facts that

  • browser.wait returns a promise that is resolved once the condition function returns truthy, or rejected if it times out.

  • If expect is passed a promise, it only runs the expectation when the promise is resolved

You can make a function that wraps a call to browser.wait

function eventual(expectedCondition) {
  return browser.wait(expectedCondition, 2000).then(function() {
    return true;
  }, function() {
    return false;
  });
}

and then create an expectation

expect(eventual(protractor.ExpectedConditions.presenceOf(element(by.partialLinkText('Continue'))))).toBe(true);

Or, to make it work on any browser instance, you can monkey-patch the Protractor prototype

protractor.Protractor.prototype.eventual = function(expectedCondition) {
  return this.wait(expectedCondition, 2000).then(function() {
    return true;
  }, function() {
    return false;
  });
}

and can be used as

expect(browser.eventual(protractor.ExpectedConditions.presenceOf(element(by.partialLinkText('Continue'))))).toBe(true);

To avoid timeouts, you must make sure that the timeout passed to browser.wait is less than the the Jasmine async test timeout, which is specified as jasmineNodeOpts: {defaultTimeoutInterval: timeout_in_millis} in the protractor configuration file

Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
2

The presenceOf expected condition used with a browser.wait() would allow to have a single line in the test:

var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(element(by.partialLinkText('Continue'))), 1000, 'Unable to find continue link');

where EC is protractor.ExpectedConditions - I usually make it global in onPrepare() through the global namespace.

Note that in case of a failure, you would have a Timeout Error, but with the Unable to find continue link error description.


As a side note, it is important to provide a meaningful custom error description, as you've already did. If you want to enforce it, there is a eslint-plugin-protractor plugin to ESLint static code analysis tool that would warn you if there is a browser.wait() used without an explicit error description text.

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • Thanks for the answer... However I realise I a) missed "presenceOf" in my original question, and b) Didn't really ask the right question to get an answer I wanted. Not exactly sure if it's the done thing, but I have edited the question. – Michal Charemza May 11 '16 at 08:34