The Story:
We've developed a custom jasmine matcher that does 2 main things:
Implementation:
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 + "'."
};
}
};
},
where tooltipPage
is a Page Object defined separately:
var Tooltip = function () {
this.tooltip = element(by.css(".tooltip"));
};
module.exports = new Tooltip();
The usage is quite convenient for us and really helps to follow the DRY principle keeping our test code base clean and readable:
expect(page.fromDateInput).toHaveTooltip("After");
The Problem and the Question:
Now, what I'm trying to do is to have the matcher handle 2 use cases separately:
browser.wait()
rejected promise)How can I improve the matcher to be able to handle these two problems separately and report different errors?
What I've tried:
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"
}
});
}
};
},
Here I've tried to resolve browser.wait()
explicitly and handle the "success" and "error" cases separately. This resulted into a Jasmine Spec timeout and a huge "red" text on the console:
Expected ({ ptor_: ({ setFileDetector: Function, ...
5 minutes scrolling here
... InnerHtml: Function, getId: Function, getRawId: Function }) to have tooltip 'After'.
I'm afraid I cannot return a promise from the "compare" function.
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 theresult
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With