Retrieve the callback arguments using sinon.spy within a JavaScript promise

During my test with mocha and sinon, I encountered an issue where I couldn't retrieve a callback value from inside a promise scope of an HTTP-request due to the asynchronous nature of promises. It seems that by the time sinon.spy checks on the callback, it has already vanished or become empty/undefined. Below is the testing code snippet:

 it('should issue GET /messages ', function() {
  server.respondWith('GET', `${apiUrl}/messages?counter=0`, JSON.stringify([]));
  let callback = sinon.spy();
  Babble.getMessages(0, callback);
  server.respond();
  sinon.assert.calledWith(callback, []);
});

Here's the promise in question:

function requestPoll(props) {
    return new Promise(function(resolve, reject) {
            var xhr = new XMLHttpRequest();
            xhr.open(props.method, props.action);
            xhr.timeout = 500; // time in milliseconds
            if (props.method === 'post' ) {
                    xhr.setRequestHeader('Content-Type', 'application/json');
            }
            xhr.addEventListener('load', function(e) {
                    resolve(e.target.responseText);
            });

            xhr.send(JSON.stringify(props.data));


    });
}

and the call which I'm trying to get a callback from using sinon.spy

getMessages: function(counter, callback){

            requestPoll({

                            method: "GET",
                            action: "http://localhost:9090/messages?counter="+counter

            }).then(function(result){

                    callback(result);
            });


        }

The issue lies in sinon.spy not receiving any arguments (due to the async functionality). I attempted to find a way to extract the result outside the scope and assign it to the callback, but it proved impossible. I also tried using resolve and promise return methods but found no success.

How can I ensure this unit test passes?

Edit:
this is my attempt:

getMessages: function(counter, callback){

            var res;
            res = httpRequestAsync("GET",'',"http://localhost:9097/messages?counter=",counter);

            console.log(res);
            if(res!="")
                    callback( JSON.parse(res) );                      
        }

I moved the request to a separate function:

function httpRequestAsync(method,data,theUrl,counter)
    {

            return requestPoll({

                    method: method,
                    action: theUrl+counter,
                    data: data

            }).then(JSON.parse);

    }

It returned res as the promise and within its prototype contains the promised value required. https://i.sstatic.net/ZKc36.png

Is there a way to access that promised value successfully?

Answer №1

It is advisable not to mix promises and callbacks. It is best to stick with a promise-based function if you are already using one.

To ensure getMessages does not disrupt the promise chain, make sure it returns a Promise:

getMessages: function(counter) {
  return requestPoll({
    method: "GET",
    action: "http://localhost:9090/messages?counter=" + counter
  }).then(JSON.parse)
}

Then utilize this promise in your test case:

it('should issue GET /messages ', function() {
  server.respondWith('GET', `${apiUrl}/messages?counter=0`, JSON.stringify([{testdata}]));
  const gettingMessages = Babble.getMessages(0);
  server.respond();

  // Ensure to return a promise so that the testing framework recognizes the test as asynchronous
  return gettingMessages.then(function(messages) {
      // Perform assertion to verify that messages actually match the test data
  })
})

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

How can I add data to a relational table that includes a foreign key reference?

There are two tables that are related with a one to many relationship: envelopes: CREATE TABLE envelopes ( id integer DEFAULT nextval('envelope_id_seq'::regclass) PRIMARY KEY, title text NOT NULL, budget integer NOT NULL ); transact ...

Set a variable to represent a color for the background styling in CSS

My goal is to create an application that allows users to change the background color of a button and then copy the CSS code with the new background color from the <style> tags. To achieve this, I am utilizing a color picker tool from . I believe I ...

Retrieve all elements from an array that have the highest frequency of occurrence

Consider an array like [1,4,3,1,6,5,1,4,4]. The element with the highest frequency in this array is 3. The goal is to select all elements from the array that have a frequency of 3, so in this case we would select [1,4]. To achieve this, one possible meth ...

What is the method for extracting JavaScript code as data from a script tag?

I have a file external (let's say bar.js) function qux() {} Then in my webpage, I include it using the script tag: <script type="text/javascript" src="bar.js"></script> I am looking for a way to retrieve the JavaScript code from within ...

Why is the radio button not chosen in the ns-popover popup? The radio button is only selected in the popup of the last column

In my Angular controller, I am trying to set the radio model but it is only appearing in the last column popup of the table. The ns-popover is displayed when clicking on a table column. Here is the Angular Code: var app = angular.module('app', ...

Is it possible to access the ID element of HTML using a variable in jQuery?

I have fetched some data from a JSON ARRAY. These values include Value1,Value2, and Value3. Additionally, I have an HTML checkbox with an ID matching the values in the array. My goal is to automatically select the checkbox that corresponds to the value re ...

What is the best way to swap out a div element with a text area when I press a button, all

I recently used a Fiddle found at http://jsfiddle.net/GeJkU/ function divClicked() { var divHtml = $(this).html(); var editableText = $("<textarea />"); editableText.val(divHtml); $(this).replaceWith(editableText) ...

Tips for properly removing Bootstrap 4 tooltips when deleting their corresponding DOM element using html()

In my Bootstrap 4 project, I've implemented a live search box that displays results with tooltips for longer descriptions. I've written jQuery scripts to hide the search results and their parent div when certain events occur, like clearing the se ...

Attempting to create a conditional state in Redux based on data fetched from an API

I'm currently exploring the most effective way to set up a conditional modal popup based on whether the response from an API call is null or not. While I typically work with functional components, the component I'm working with here is built as a ...

Is there a way to change the domain for all relative URLs on an HTML page to a different one that is not its current domain, including in HTML elements and JavaScript HTTP Requests?

I am dealing with a situation where my page contains "domain relative URLs" like ../file/foo or /file/foo within a href attributes, img src attributes, video, audio, web components, js ajax calls, etc. The issue arises when these URLs need to be relative ...

The MaskedInput text does not appear properly when it is passed through the props

Currently, I am facing an issue with a Material UI OutlinedInput along with the MaskedInput component from react-text-mask. Everything works fine when I input text initially and the element is not in focus. However, upon closing and reopening the Dialog wi ...

JavaScript callbacks using customized parameters

I am currently grappling with the challenge of incorporating an asynchronous callback in node without prior knowledge of the potential arguments. To provide more clarity, let me outline the synchronous version of my objective. function isAuthorized(userI ...

Prevent the execution of a Javascript function if it is already in progress

I've developed a function that retrieves records from a third party, and this function runs every 10 seconds. However, as I debug through Firefox, I notice a long queue of ajax requests. I'm contemplating including a statement that can signal to ...

Storing user data in node.js using the express-sessionTo save user data in

Using express and express-session with mysql on nodeJS has been successful for me. I managed to set up a cookie and session as well. Take a look at my code: app.use(cookieParser('3CCC4ACD-6ED1-4844-9217-82131BDCB239')); session({resave: true, s ...

How to make the slides in a Bootstrap 4 carousel slide in and out with animation

I'm currently utilizing the bootstrap 4 carousel and have made some customizations to fit my project needs. The main requirement is: When I click on the next slide, the current next slide should become active and a new next slide should load with ...

Guide for implementing async/await in conjunction with the eval() function within JavaScript

I'm currently using the eval function to evaluate strings and adding await to it to ensure all values are obtained, but unfortunately the await is not functioning correctly. Here is a snippet of my code: if (matchCard.card.status != "notstarted& ...

After reaching the conclusion, the code restarts

Any ideas on how to reset this code every 10-20 seconds? I'm struggling to find a solution! I'm new to coding, so any assistance would be greatly appreciated. Here's the code: var items = document.getElementsByClassName('btn-primary n ...

Having difficulty locating the login button on the webpage

I am attempting to log into a banking account using selenuim. After opening the webpage and locating the login element, I initially struggled to access it by its "name" or "id." Fortunately, I was able to successfully access it using driver.find_element_by ...

What are some creative ways to incorporate a variety of images into my website?

Currently working on a webpage and running into an issue. I have a div called "background" where I am loading several images using the <img>-tag. The problem is that the images are set to float:left;, causing them to wrap onto a new line as they can& ...

Apply the cursor property to the audio element and set it as a pointer

I have a question: how can I apply a cursor style to my <audio> controls? When I try to add them using CSS, the cursor only appears around the controls and not directly on the controls themselves. Below is the code snippet: <audio class="_audio" ...