Resolved and rejected promises in a custom Jasmine Matcher Resolved and rejected promises in a custom Jasmine Matcher selenium selenium

Resolved and rejected promises in a custom Jasmine Matcher


As per jasminewd2 (An adapter for Jasmine-to-WebDriverJS. Used by Protractor) code -

An expectation resolves any promises given for actual and expected values, as well as the pass property of the result object.

So if at all there is an async function or a promise that needs to be resolved in a custom matcher/expectation then it needs to be wrapped to the result.pass value, so that protractor waits for the promise to be resolved.

In the question, a jasmine spec timeout error is encountered because protractor couldn't understand that there is a promise that needs to be resolved before performing that particular operation. In order to resolve it, either pass the async function in the expect statement directly or pass it to the pass value of the result object. Here's the code for it -

toHaveTooltip: function() {  return {      compare: function(elm, expectedTooltip) {          var tooltipPage = requirePO("tooltip");          browser.actions().mouseMove(elm).perform();              return {                  pass: browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible.").then(function () {                            tooltipPage.tooltip.getText().then(function(actualTooltip) {                                return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip);                            }),                        }, function () {                            return false;                        }),                  message: "Error Occured"              }      }  };},

However, the problem with the above code is that a custom error message cannot be crafted. To resolve it, the best method I could find was to return the result object explicitly, so that an error message can be assigned to it as required. Here's an example -

var result = {};result.pass = browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible.").then(function () {                  tooltipPage.tooltip.getText().then(function(actualTooltip) {                      result.message = "Element does not have the tooltip '" + expectedTooltip + "'.";                      return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip);                  }),              }, function () {                  result.message = "No tooltip shown on mouse over the element";                  return false;              });return result;

Note: If there is no message property in the result object, then protractor will try to create a generic error message itself and it will contain the promise object (A lengthy message starting with - { ptor_: ... }) as shown in the question.

Hope it helps.


Well, i remember reading somewhere that jasmine 2 does not support the type of matcher you are trying to do (with async function inside), and returning promises.. i will try to find the source and update here. Also you shouldn't do the mouse actions inside of the matcher, that's not the point of matchers.

So basically what im saying and suggesting is the following:If you want a clean code, export the following into a function and call it.

var checkToolTipVisibility (elm, expectedTooltip) {    browser.actions().mouseMove(elm).perform();    browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible.");//optional then here if you want to fail with a timeout or something...    expect(tooltipPage.tooltip.getText()).toEqual(expectedTooltip);}checkToolTipVisibility(page.fromDateInput, "After");//usage

I think it's a very clean and simple solution, that doesn't require any custom matchers, and it is the jasmine way of doing things (not async functions in matchers), that's the way i use in my code, except those functions sit in a utils.js file which i require when needed .

Hope i helped, and i will continue looking for the source of my first statement!


For some reason, I couldn't get this to work in Karma/Angular 2+. I ended up calling Jasmine's global fail method within the promise itself:

const _global: any = (typeof window === 'undefined' ? global : window);const customMatchers: jasmine.CustomMatcherFactories = {  toPassA11y: () => {    return {      compare: (el: any): any => {        const axe = require('axe-core');        const result: any = {          message: '',          pass: true        };        axe.run((error: Error, results: any) => {          if (error) throw error;          if (results.violations.length > 0) {            _global.fail('Expected element to pass accessibility checks.');          }        });        return result;      }    };  }};_global.beforeEach(() => {  jasmine.addMatchers(customMatchers);});

And in the spec:

describe('Home component', () => {  it('should check accessibility', async(() => {    expect(document).toPassA11y();  }));});