Dispensing guarantees with Protractor

q library offers a unique feature that allows resolving and spreading multiple promises into separate arguments:

If you have a promise for an array, you can utilize spread instead of then. The spread function distributes the values as arguments in the fulfillment handler.

return getUsername()
    .then(function (username) {
        return [username, getUser(username)];
    })
    .spread(function (username, user) {

    });

In our work with protractor, we are exploring the capabilities of the built-in protractor.promise from WebDriverJS.

The Query:

Is it feasible to implement the "spread" functionality using protractor.promise?

A Practical Example:

We have developed a custom jasmine matcher to verify if an element is focused. This requires resolving two promises before conducting an equality comparison. Currently, we rely on 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]);
});

Ideally, we would prefer a more readable approach such as:

protractor.promise.all([
    elm.getId(),
    browser.driver.switchTo().activeElement().getId()
]).spread(function (currentElementID, activeElementID) {
    return jasmine.matchersUtil.equals(currentElementID, activeElementID);
})

Answer №1

If you find it a bit messy to use, there is a solution. You can create a separate helper function that can be passed as a parameter to then() and contain a callback which is typically passed to then(). This helper function will then convert the array value into function arguments:

protractor.promise.all([
    elm.getId(),
    browser.driver.switchTo().activeElement().getId()
]).then(spread(function (currentElementID, activeElementID) {
    // Use the helper function to spread arguments
    jasmine.matchersUtil.equals(currentElementID, activeElementID);
}));


// The helper function takes a callback
function spread(callback) {
    // It returns a new function to be used by `then()`
    return function (array) {
        // Call the callback using apply to spread the array values 
        return callback.apply(null, array);
    };
}

You can still chain it with another then() and provide rejection callbacks; this maintains the behavior of Protractor promises while converting an array of values into arguments.

A downside is that it doesn't look exactly like your example (not .all().spread(), but .all().then(spread())) and you may need to create a module for this helper or define it globally for easier use in multiple test files.

Update:

With ES2015, you can now utilize destructuring assignment along with then():

protractor.promise.all([
    elm.getId(),
    browser.driver.switchTo().activeElement().getId()
]).then(function (values) {
    // Use destructuring assignment to separate variables
    const [currentElementID, activeElementID] = values; 
    jasmine.matchersUtil.equals(currentElementID, activeElementID);
}));

Answer №2

TL;DR: It seems that simply replacing protractor.promise with q may not be completely safe. When extending ElementArrayFinder, I encountered a hanging test run.

  • Take elements while a condition evaluates to true (extending ElementArrayFinder)

Previous solution:

Here is what I did to address this issue.

I decided to switch from protractor.promise to q on the go, although I wasn't sure about the safety of this approach:

onPrepare: {
    protractor.promise = require("q");
},

Surprisingly, nothing has broken so far and now I can utilize spread() and other helpful features offered by q using 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);
                })
            };
        }
    };
}

Check out the relevant github thread: protractor.promise to use q.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing JavaScript files within Angular2 components: A guide

I need to insert a widget that runs on load. Typically, in a regular HTML page, I would include the script: <script src="rectangleDrawing.js"></script> Then, I would add a div as a placeholder: <div name="rectangle></div> The is ...

I am unable to halt the relentless stream of asynchronous events

Struggling to retrieve an array using a loop in the asynchronous environment of nodejs. Check out my code below: getDevices(userIDs, function(result) { if (result) { sendNotification(messageUser, messageText, result); res.send("Success"); } else { ...

Ways to personalize the onSubmit function within tinacms

Having an issue with my Tina project. I am trying to develop my own submit button in Tinacms project, rather than using the sidebar or top bar provided by tinacms. I want to customize a button for onSubmit functionality. Any suggestions on how to achieve ...

What is the best way to conceal the HTML video controls for multiple videos displayed on a single webpage?

I have a web page that displays a collection of movies generated using a PHP foreach loop. The code snippet looks like this: foreach ($movies as $movie) { $pos = strrpos($movie, '/'); $id = $pos === false ? $movie : substr($movie, $pos ...

Retrieve and dynamically load an entire webpage using AJAX

Although it's typically not recommended, I am interested in displaying the progress of a download on a heavy page to show the user when it's ready. Is there a way for me to track and display the actual progress of the download? Can I monitor how ...

Customized settings saved in local storage using JavaScript

Here is the script I am currently using for my app: <script> if (localStorage.getItem("0") === null) { //do nothing } else if(localStorage.getItem("1")===null{ } else if(localStorage.getItem("2")===null{ } else if(localStorage.getItem("3")===null ...

Setting up your own local Jquery Mobile Angular Adapter Todo app is easy and seamless

Currently, I'm in the process of familiarizing myself with the jQuery Mobile Angular adapter by configuring a local repository for the Todo app JSFiddle that is linked on the Github repository: http://jsfiddle.net/ocdemo/UM5Mr/ I am utilizing the "s ...

Is it possible to modify @page directive(CSS) values from the code-behind(C#) or JavaScript?

Using the @page directive, you can define the printer margins for a page separately from regular CSS margins: <style type="text/css" media="print"> @page { size: auto; /* auto is the current printer page size */ margin ...

Verify if spacebar is pressed and then use jQuery to add a hashtag with multi-language support

I am attempting to use jQuery to add a hashtag (#) after the user types and presses space. I have created a demonstration on CodePen. In this demo, when you type something like (how are you), the JavaScript code will change it to (#how #are #you). To ach ...

JavaScript's Use of Brackets

I’ve been working on a new project that involves adding links. To do this, users can input both the link URL and the text they want displayed. I used a prompt to gather this information. Here’s the code snippet I wrote: document.getElementById(ev.targ ...

Completes a form on a separate website which then populates information onto a different website

Creating a website that allows users to login and view various complaint forms from government websites or other sources. When a user clicks on a link to file a complaint, they will be redirected to the respective page. However, I am looking for a way to ...

Is it possible to test an Angular application using Protractor within an iframe that is hosted by a non-Angular

While trying to migrate a legacy app to Angular, we encountered an issue where the legacy app loads the new app in an iframe. Testing this integration with Protractor has proven challenging due to the fact that the legacy app is not built on Angular. If t ...

Enhancing the capabilities of my search and select filter beyond the boundaries of ng-app in AngularJS

In the controller, I have a directive that lists items using data from an API call. The HTML code for this is: <div ng-app="WWnetworkEvents"> <ul ng-controller ="networkEventsCtrl"> <networkevent-directive></networkevent-directiv ...

Guide to importing an AngularJS controller into an Express file (routes.js)

Currently, I am in the process of developing a restful service and my goal is to organize my callbacks within controllers in order to avoid cluttering my routes.js file. Previously, I had been using controller = require(path.to.controller); This enabled ...

Error: Material-UI prop type validation failed - Please specify either `children`, `image`, `src`, or `component` prop

Despite having an image prop in CardMedia, I kept encountering this error. Here is a snippet of the code: Error description: const Post = ({ post, setter }) => { const classes = useStyles(); return( <Card className={classes.card} ...

Is Cookie-session the most ideal choice for React applications?

My NodeJS application currently authenticates users through a third-party app. Once the app retrieves user data, a cookie is generated and sent to the client for React to access the user data from that Cookie. Should I stick with using cookies or consi ...

Three.js experiences a memory leak issue

We are currently working on a single page app where users can switch between multiple Three.js apps. However, we have observed a continuous increase in memory usage by the tab. There is no memory leakage in our app and it appears that Three.js variables ar ...

The requested 'Pagination' component (imported as 'Pagination') could not be located within the 'swiper' library. Possible exports include Swiper and default

I was trying to implement pagination using swiper. I included the Pagination module with this import statement: import { Pagination } from "swiper"; Here's my configuration: The error that I encountered is : I have noticed that it w ...

Unable to get PrependTo to remove itself when clicked

Below is a custom jQuery script I put together: $(function(){ $("a img").click(function() { $("<div id=\"overlay\"></div>").hide().prependTo("body").fadeIn(100); $("body").css({ ...

Guide to testing a JavaScript function in Mocha that accepts a select element

I need to write unit tests for the following JS function: let converter = {}; converter.removeSelectedAttribute = function removeSelectedAttribute(element) { options = Array.from(element.options); options.forEach(function (item, index) { ...