自定义Jasmine Matcher

时间:2015-10-24 00:16:41

标签: javascript selenium jasmine promise protractor

故事:

我们开发了一个定制的茉莉花匹配器,可以完成两件事:

  • 鼠标悬停在给定元素上
  • 检查是否显示了带有所需文字的工具提示

实现:

toHaveTooltip: function() {
    return {
        compare: function(elm, expectedTooltip) {
            var tooltipPage = requirePO("tooltip");

            browser.actions().mouseMove(elm).perform();
            browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible.");

            return {
                pass: tooltipPage.tooltip.getText().then(function(actualTooltip) {
                    return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip);
                }),
                message: "Element does not have the tooltip '" + expectedTooltip + "'."
            };
        }
    };
},

其中tooltipPage是单独定义的页面对象:

var Tooltip = function () {
    this.tooltip = element(by.css(".tooltip"));
};

module.exports = new Tooltip();

使用对我们来说非常方便,并且真正有助于遵循DRY原则,保持我们的测试代码库清洁和可读:

expect(page.fromDateInput).toHaveTooltip("After");

问题和疑问:

现在,我要做的是让匹配器分别处理2个用例:

  • 根本没有显示鼠标上的工具提示(基本上,browser.wait()被拒绝的承诺)
  • 有一个工具提示,但不是所需的工具提示

如何改进匹配器以分别处理这两个问题并报告不同的错误?

我尝试了什么:

toHaveTooltip: function() {
    return {
        compare: function(elm, expectedTooltip) {
            var tooltipPage = requirePO("tooltip");

            browser.actions().mouseMove(elm).perform();

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

在这里,我尝试明确解决browser.wait()并分别处理“成功”和“错误”案例。这导致了Jasmine Spec超时和控制台上的巨大“红色”文本:

Expected ({ ptor_: ({ setFileDetector: Function, ...
5 minutes scrolling here
... InnerHtml: Function, getId: Function, getRawId: Function }) to have tooltip 'After'.

我担心我无法从“比较”功能中回复承诺。

4 个答案:

答案 0 :(得分:4)

根据 jasminewd2 (适用于Jasmine-to-WebDriverJS的适配器。由量角器使用) code -

  

期望解析为实际值和期望值提供的任何承诺,以及pass对象的result属性。

因此,如果根本需要在自定义匹配器/期望中解析异步函数或承诺,则需要将其包装到result.pass值,以便量角器等待承诺解决。

在问题中,遇到jasmine spec timeout错误,因为量角器无法理解在执行特定操作之前需要解析的承诺。要解决此问题,请直接在expect语句中传递async函数,或将其传递给pass对象的result值。这是它的代码 -

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"
              }
      }
  };
},

但是,上述代码的问题是无法制作自定义错误消息。要解决它,我能找到的最好方法是显式返回result对象,以便根据需要为其分配错误消息。这是一个例子 -

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;

注意:如果message对象中没有result属性,则量角器将尝试自己创建一般错误消息,它将包含promise对象< em>(一个以 - { ptor_: ... }开头的冗长消息),如问题所示。

希望它有所帮助。

答案 1 :(得分:2)

好吧,我记得在某个地方读过茉莉花2不支持你想要做的匹配器类型(里面有异步函数),并且返回promises ..我会尝试找到源并在这里更新。此外,您不应该在匹配器内部执行鼠标操作,这不是匹配点。

所以基本上我说的和建议如下: 如果您想要一个干净的代码,请将以下内容导出到一个函数中并调用它。

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

我认为这是一个非常简洁的解决方案,不需要任何自定义匹配器,它是茉莉花的做事方式(不是匹配器中的异步功能),这是我在我的代码中使用的方式,除了那些函数位于我需要的utils.js文件中。

希望我帮助过,我会继续寻找我的第一份声明的来源!

答案 2 :(得分:1)

出于某种原因,我无法在Karma / Angular 2+中使用它。我最终在承诺本身中调用了Jasmine的全局fail方法:

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);
});

在规范中:

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

答案 3 :(得分:0)

基于@Girish Sortur's perfect answer,这里是匹配器的完整代码,它现在可以完美地分别处理缺少的工具提示和不同的工具提示文本案例:

toHaveTooltip: function() {
    return {
        compare: function(elm, expectedTooltip) {
            var tooltipPage = requirePO("tooltip"),
                result = {};

            // mouse over the element
            browser.actions().mouseMove(elm).perform();

            // wait for tooltip to appear and handle errors
            result.pass = browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000).then(function () {
                return tooltipPage.tooltip.getText().then(function(actualTooltip) {
                    result.message = "Expected tooltip: '" + expectedTooltip + "'. Actual tooltip: '" + actualTooltip + "'.";
                    return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip);
                })
            }, function () {
                result.message = "No tooltip shown on mouse over the element";
                return false;
            });
            return result;
        }
    };
},