q
library has this neat feature to resolve and spread multiple promises into separate arguments:
If you have a promise for an array, you can use spread as a replacement for then. The spread function “spreads” the values over the arguments of the fulfillment handler.
return getUsername()
.then(function (username) {
return [username, getUser(username)];
})
.spread(function (username, user) {
});
In protractor, we are trying to use the built-in protractor.promise
coming from WebDriverJS
.
The Question:
Is it possible to have the "spread" functionality with protractor.promise
?
Example use case:
We've implemented a custom jasmine matcher to check if an element is focused. Here we need to resolve two promises before making an equality comparison. Currently, we are using protractor.promise.all()
and then()
:
protractor.promise.all([
elm.getId(),
browser.driver.switchTo().activeElement().getId()
]).then(function (values) {
jasmine.matchersUtil.equals(values[0], values[1]);
});
which ideally we'd like to have in a more readable state:
protractor.promise.all([
elm.getId(),
browser.driver.switchTo().activeElement().getId()
]).spread(function (currentElementID, activeElementID) {
return jasmine.matchersUtil.equals(currentElementID, activeElementID);
})
In protractor, it is handled by using then statement. // spec. ts import { browser, element, by } from "protractor"; describe('Promise Demo',()=> { browser. waitForAngularEnabled(false); it('Protractor Promise Chaining', () => { browser.
Calls a function for each element in an array, and if the function returns true adds the element to a new array. If the return value of the filter function is a promise, this function will wait for it to be fulfilled before determining whether to insert the element into the new array.
It may come a bit ugly to use, but you can define an independent helper function, which can be passed to then()
as a parameter and have a callback, which is usually passed to then()
to be passed to it. This function will then convert array value to function arguments:
protractor.promise.all([
elm.getId(),
browser.driver.switchTo().activeElement().getId()
]).then(spread(function (currentElementID, activeElementID) {
// ---^^^----- use helper function to spread args
jasmine.matchersUtil.equals(currentElementID, activeElementID);
}));
// helper function gets a callback
function spread(callback) {
// and returns a new function which will be used by `then()`
return function (array) {
// with a result of calling callback via apply to spread array values
return callback.apply(null, array);
};
}
You can still chain it with another then()
and provide rejection callbacks; it keeps all the behavior of Protractor promises the same, but just converts array of values to arguments.
Drawbacks are that it is does not have a perfect look like in your example (not .all().spread()
but .all().then(spread())
) and you'll probably have to create a module for this helper or define it globally to be able to use it easily in multiple test files.
Update:
With ES2015 it is possible to use destructuring assignment along with then()
:
protractor.promise.all([
elm.getId(),
browser.driver.switchTo().activeElement().getId()
]).then(function (values) {
// Destructure values to separate variables
const [currentElementID, activeElementID] = values;
jasmine.matchersUtil.equals(currentElementID, activeElementID);
}));
TL;DR Apparently, it's not entirely safe to replace protractor.promise
with q
. For instance, I've got a hanging test run once I've decided to extend ElementArrayFinder
:
Old answer:
Here is what I've done to solve it.
I've replaced protractor.promise
with q
on the fly (not sure if it's actually safe to do):
onPrepare: {
protractor.promise = require("q");
},
But, nothing broke so far and now I'm able to use spread()
and other syntactic sugar provided by q
through protractor.promise
:
toBeActive: function() {
return {
compare: function(elm) {
return {
pass: protractor.promise.all([
elm.getId(),
browser.driver.switchTo().activeElement().getId()
]).spread(function (currentElementID, activeElementID) {
return jasmine.matchersUtil.equals(currentElementID, activeElementID);
})
};
}
};
}
Relevant github thread: protractor.promise to use q.
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