Решено и отклонено promises в пользовательском Jasmine Matcher

История:

Мы разработали индивидуальный набор жасмина, который выполняет две основные функции:

  • мышь над определенным элементом
  • проверьте, есть ли всплывающая подсказка с нужным текстом

Реализация:

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'.

Боюсь, я не могу вернуть обещание от функции "сравнить".

Ответ 1

В соответствии с jasminewd2 (адаптер для Jasmine-to-WebDriverJS. Используется в Protractor) code -

Ожидание разрешает любой promises для действительных и ожидаемых значений, а также свойство pass объекта result.

Итак, если вообще есть функция асинхронного вызова или обещание, которое необходимо разрешить в пользовательском совпадении/ожидании, тогда его необходимо обернуть в значение result.pass, чтобы транспортир ожидал, что обещание будет разрешено.

В этом вопросе встречается ошибка jasmine spec timeout, поскольку транспортир не может понять, что перед выполнением этой конкретной операции необходимо решить проблему. Чтобы решить эту проблему, либо передайте функцию 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;

Примечание. Если в объекте result нет свойства message, тогда транспортитор попытается создать общее сообщение об ошибке, и оно будет содержать объект обещания (длительное сообщение, начинающееся с - { ptor_: ... }), как показано в вопросе.

Надеюсь, что это поможет.

Ответ 2

Ну, я помню, где-то читал, что жасмин 2 не поддерживает тип помощника, который вы пытаетесь сделать (с функцией async внутри) и возвращающий 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

Я думаю, что это очень чистое и простое решение, которое не требует каких-либо пользовательских совпадений, и это способ жасмина делать вещи (а не функции async в состязаниях), что я использую в своем коде, кроме тех, функции находятся в файле utils.js, который я требую при необходимости.

Надеюсь, я помог, и я продолжу искать источник моего первого заявления!

Ответ 3

Основываясь на @Girish Sortur идеальном ответе, вот полный код матчи, который теперь отлично работает как с отсутствующей подсказкой, так и с различными текстовыми случаями всплывающей подсказки отдельно

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