Is there a way to seamlessly integrate expect() with regular promises in protractor promise sequences?

As I dive into the world of promises in protractor, I find myself puzzled by the execution order, especially when mixing non-protractor promises with protractor promises. My goal is to troubleshoot and resolve some inconsistent protractor tests at my workplace, even though I'm not an expert JavaScript programmer. So please keep your explanations simple.

After struggling for days with a problematic test, I decided to simplify things and create basic examples to grasp how promise chains work. According to what I've researched online, I believed that chaining like this:

a().then(() => {
    x();
    b().then(() => {
        y();
        c().then(() => z());
    });
});

is equivalent to:

a().then(() => {
    x();
    b();
}).then(() => {
    y();
    c();
}).then(() => z());

which should also be similar to this (assuming I was using ES6 at work, which is not the case):

await a();
await x();
await b();
await y();
await c();
await z();

You can check out my full code and the results I obtained here: https://github.com/cpjust/TypeScriptTest/tree/dev/specs

I discovered different outcomes for each scenario, particularly when comparing native promises to protractor promises. In the second_spec.ts file, I even attempted adding expect() statements within promises, expecting them to fail and prevent the execution of subsequent promises. However, what surprised me was that all promises continued executing post-expect(), leading to a failed test due to the expect() error. Quite perplexing...

Answer №1

Your inquiry involves multiple subquestions, all of which are excellent. To provide comprehensive assistance, it is advisable to address each question individually. I will do my best to offer explanations that can aid in resolving your issue.

first_spec.ts

 it('promise chain 1', function () {
        /* Displays the following:
        [TRACE] default - Start
        [DEBUG] default - Sleeping for 100 ms...
        [TRACE] default - End
        [INFO] default - --1
        [DEBUG] default - Sleeping for 200 ms...
        [INFO] default - --2
        [DEBUG] default - Sleeping for 300 ms...
        [INFO] default - --3
        [INFO] default - --done
        */
        logger.trace("Start");

        printLater("--1", 100).then(() => {
            printLater("--2", 200).then(() => {
                printLater("--3", 300).thenFinally(() => {
                    logger.info("--done");
                });
            });
        });

        logger.trace("End");
    });
  • This section is fairly straightforward. The printLater function returns a promise involving browser.sleep, requiring completion before advancing to the next step. There should be no confusion regarding this specific 'it' block.

    it('promise chain 1.1', function () { /* Displays the following: [TRACE] default - Start [DEBUG] default - Sleeping for 100 ms... [TRACE] default - End [INFO] default - --1 */ logger.trace("Start");

        nativePromise("--1", 100).then(() => {
            nativePromise("--2", 200).then(() => {
                nativePromise("--3", 300).finally(() => {
                    logger.info("--done");
                });
            });
        });
    
        logger.trace("End");
    });
    

In the scenario presented above, the nativePromise function generates a Promise object by calling printLater within it. Here's how the process unfolds: Upon invoking the nativePromise function, it immediately produces a promise object in the pending state. Subsequently using 'then' on nativePromise anticipates a resolve/reject state, but since it remains pending indefinitely until test timeout, the testing halts at some point without any progress. Notably, we still receive output from the first nativePromise due to its asynchronous behavior through the printLater function.

To mimic the behavior of the initial 'it' block, a slight adjustment must be made to the nativePromise function. It becomes imperative to utilize 'resolve' to ensure proper resolution of the promise.

function nativePromise(msg, time) {
        return new Promise((resolve) => {
            resolve(printLater(msg, time));
        });
    }

Note: It is always considered a best practice to properly resolve/reject promises.

it('await promise 3.1', async function () {
        /* Displays the following:
        [TRACE] default - Start
        [DEBUG] default - Sleeping for 100 ms...
        [INFO] default - --1
        */
        await logger.trace("Start");

        await nativePromise("--1", 100);
        await nativePromise("--2", 200);   // Halts after "--1"
        await nativePromise("--3", 300);
        await logger.info("--done");

        await logger.trace("End");
    });

The rationale behind the aforementioned code snippet aligns with the earlier explanation. 'await' also waits for the resolution/rejection of promises. Should it fail to return anything, 'await' too gets stuck at a certain juncture.

We integrate 'await' to enhance code clarity, ensuring that it waits for promise resolution/rejection without returning the promise object itself. Instead, 'await' extracts the value directly from the promise object and returns it.

Note1: Jasmine's 'expect' function holds off action until the control flow is clear refer to this link from Protractor

Note2: Protractor Promise (recognized as webdriver.Promise) has been deprecated in Protractor and webdriverJs. Hence, avoiding their usage is advisable whenever possible.

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

Error message: AJAX encountered an unexpected token that caused a SyntaxError to be thrown

I have recently developed a user-friendly form that is supposed to fetch translation keys via the API. However, I encountered an issue that reads "Uncaught SyntaxError: Unexpected token :" when executing the code. Allow me to present you with a snippet of ...

Order the variables in the dropdown menu based on the PHP variables

I currently have a select list that allows me to choose between two options: Popularity and Recently Ordered. My goal is to filter the objects on the page based on these attributes, connecting each option to its respective function in my dataloader.php fi ...

Develop a new IntermediateBaseModel class specifically designed for Sequelize

Currently, I am utilizing Sequelize as my data ORM. In my implementation, there exists a straightforward User Class. import { Model } from "sequelize"; class User extends BaseModel { } export default User; With this setup, I can easily interac ...

Troubleshooting the "h is undefined" issue with jQuery's ajaxForm

In my project, I have implemented a form that is displayed in an ajaxed modal. This form is used for updating user details. When the modal is loaded, a JavaScript function is called: teamCreate: function() { $j("#step1,form#createEditTeam").show(); $j("#s ...

A textarea with a height of zero is still visible

My goal is to create collapsible widgets within a side panel on my webpage. Overall, the functionality works smoothly. I am able to adjust the height of child divs to 0px with a transition, causing them to disappear. However, I have run into an issue wher ...

Tips for altering the visibility of a different class on hover using jss

Exploring the features of material ui react Below is my scss code snippet (when hovering over .content, the .replyBtn becomes visible): .content { &:hover { .replyBtn { visibility: visible } } } .replyBtn { visibility: hidden; } ...

Update the nest-cli.json configuration to ensure that non-TypeScript files are included in the dist directory

I've been searching for a solution for hours now: I'm developing an email service using nestJS and nest mailer. Everything was working fine until I tried to include a template in my emails. These templates are hbs files located in src/mail/templ ...

Import files from local directory to iframe in Electron application

I am currently working on an application using Electron that allows users to preview HTML5 banner ads stored locally on their computer. At this point, I have enabled the ability to choose a folder containing all the necessary files. Once a directory is s ...

Sending extra actions based on conditions from a redux-observable epic

To summarize, I am looking to create an epic that will make a request and dispatch an action based on whether the request is successful or not. Additionally, I want this epic to potentially dispatch more actions depending on the outcome and the current sta ...

Leveraging Angular js for performing operations in Putty

At the moment, we depend on Putty for connecting to the app server and checking logs. I am looking for a solution that would allow me to automate this process using angular js. Is it possible to send commands from my angular js or any other client-side a ...

Utilize Moment to round a date either up or down

I am using Moment to compare two datetime values. Specifically, I am utilizing Moment.isSameOrBefore function. However, my two date values are slightly different due to milliseconds. I want these two values to be considered the same: var date1 = ' ...

My Instagram stream using the Ionic framework's infinite-scroll feature is experiencing issues with repeated duplicates in the repeater

I am currently working on developing a mobile app for IOS and Android using the Ionic Framework. The app will feature an Instagram Feed with the Instagram API, displaying all photos shared under a specific hashtag (for example, #chocolat). I am also lookin ...

What is the best way to showcase the output of a Perl script on a webpage

I recently came across a resource discussing the process of executing a perl script from a webpage. What is the best way to run a perl script from a webpage? However, I am facing a situation where the script I have takes more than 30 seconds to run and d ...

The interaction between a JavaScript function call and C# is not functioning properly

Attempting to invoke the JavaScript function from CodeBehind ( C# ) : function scrollToBottom() { window.scrollTo(0, document.body.scrollHeight); } The function successfully executes when directly called from my asp.net application. However, ...

Issues with AngularJS testing using Jasmine: Receiving error message stating 'No pending request to flush' when working with $http

My UserManager service is set up to automatically send a $http POST request every hour in order to refresh the user access token. I am attempting to mock this call to ensure that the token is indeed being refreshed. However, when I try to flush the $httpb ...

What is the best approach to conceal elements from the main template in Meteor using a template event?

I have a main template along with two others that are displayed using Iron routing: <template name="main"> <div id="templateMain" name="templateMain"> <a href="nfnoscar">The Legend of NFN Oscar</a> <br/> <a h ...

What is the best way to invoke a function in Typescript while retrieving data?

Is it possible to run a query in my main.ts file with another ts file and then pull the result of the query into another file? If so, how can this be achieved? Here is an example from my main.ts file: async function getAllTips() { const tips: any = []; ...

Is the input URL modified by the Angular HttpClientModule's GET request?

I am currently using an Angular service to make calls to a Node.js server in order to fetch data. Here is a snippet of my code: constructor(private http: HttpClient){ } getPersonData(): Observable<person[]> { //return this.http.get<person ...

What is the best way to update my component when the selected value is changed?

Currently, I am facing an issue with my map component. It plots polyline data based on the option selected from the select menu component. The problem arises when I change the value in the select menu and click the play button for the animation. Instead of ...

Attaching an Event Listener to an array

I am struggling with a homework assignment and don't understand why I am receiving an error message saying 'undefined is not an object (evaluating 'buttons[i].style')). Any help would be appreciated. I have been attempting to loop throu ...