alecxe
alecxe

Reputation: 473873

Custom jasmine matchers and promises

The Story:

We've been using a custom jasmine matcher to expect an element to have a hand/pointer cursor:

beforeEach(function() {
    jasmine.addMatchers({
        toHaveHandCursor: function() {
            return {
                compare: function(actual) {
                    return {
                        pass: actual.getCssValue("cursor").then(function(cursor) {
                            return cursor === "pointer";
                        })
                    };
                }
            };
        },
    });
});

It works great and makes the tests readable:

expect(queuePage.sortByButton).toHaveHandCursor();

The problem:

When the expectation fails, currently we get a completely unreadable huge chunk of red text on the console in a form:

  • Expected ElementFinder({ ptor_: Protractor({ getProcessedConfig: Function, forkNewDriverInstance: Function, restart: Function, controlFlow: Function, schedule: Function, setFileDetector: Function, getSession: Function, getCapabilities: Function, quit: Function, actions: Function, touchActions: Function, executeScript: Function, executeAsyncScript: Function, call: Function, wait: Function, sleep: Function, getWindowHandle: Function, getAllWindowHandles: Function, getPageSource: Function, close: Function, getCurrentUrl: Function, getTitle: Function, findElementInternal_: Function, findElementsInternal_ ... 10 minutes of scrolling ... , click: Function, sendKeys: Function, getTagName: Function, getCssValue: Function, getAttribute: Function, getText: Function, getSize: Function, getLocation: Function, isEnabled: Function, isSelected: Function, submit: Function, clear: Function, isDisplayed: Function, getOuterHtml: Function, getInnerHtml: Function, getId: Function, getRawId: Function, serialize: Function, takeScreenshot: Function }) to have hand cursor.

The Question:

Why is it happening? How can we improve the matcher and output a user-friendly error instead? Something like:

Expected 'auto' to be equal to 'pointer' cursor value.

From what I understand, we would need to provide a message value for a custom matcher, but I'm not completely sure how to pass an actual element's cursor CSS value into the message. Here is what I have so far:

toHaveHandCursor: function() {
    return {
        compare: function(actual) {
            return actual.getCssValue("cursor").then(function(cursor) {
                return {
                    pass: cursor === "pointer",
                    message: "Expected '" + cursor + "' to be equal to 'pointer' cursor value."
                };
            });
        }
    };
},

I expect this to work, but, for some reason, I see the same error message on the console after a test run.

Upvotes: 1

Views: 765

Answers (2)

alecxe
alecxe

Reputation: 473873

What we were getting on the console was the auto-generated jasmine matcher fail message which consisted of the ElementFinder instance string representation - which is, it appears, quite huge for some reason.

Now, to improve the failing message, we would need to use the message key alongside with pass. We should though take into account that getCssValue() returns a promise and it needs to be resolved for an actual cursor value to be used in a custom error message:

toHaveHandCursor: function() {
    return {
        compare: function(actual) {
            var result = {};

            result.pass = actual.getCssValue("cursor").then(function(cursor) {
                result.message = "Expected '" + cursor + "' to be equal to 'pointer' cursor value.";
                return cursor === "pointer";
            });

            return result;
        }
    };
},

Now, if the expectation fails, we get a nice error message:

- Expected 'auto' to be equal to 'pointer' cursor value.

Upvotes: 5

TimoStaudinger
TimoStaudinger

Reputation: 42460

You are correct, to set a custom message, you should set the message attribute of the returned object.

The message is just a regular string. You can use the value of your cursor variable if you put together the result object after your promise has been fulfilled.

See the following example how it could be done:

beforeEach(function(){
  jasmine.addMatchers({
    toBeDeactivated: function() {
      return {
        compare: function(account){
          var accountStatusCode = account.get('status').statusCode;
          var result = { pass: accountStatusCode === 5 };
          if(result.pass) {
            result.message =  "Expected account with status code '" + accountStatusCode + " NOT to be deactivated.";
          } else {
            result.message =  "Expected account with status code '" + accountStatusCode + "' to be deactivated.";
          }
          return result;
        }
      }
    }
  }
});

Source

Replacing the account.get() with a Promise should be simple.

Upvotes: 3

Related Questions