Timeout causes failure in AngularJS jasmine promise testing

I need help testing my login controller, here is the structure of it:

describe('LoginController', function() {
    beforeEach(module('task6'));

    var $controller, LoginService;

    beforeEach(inject(function(_$controller_, _LoginService_) {
        $controller = _$controller_;
        LoginService = _LoginService_;
    }));

    describe('LoginController.submitLogin', function() {
        it('verify if user exists', function(done) {
            var $scope = {};
            var controller = $controller('LoginController', {$scope: $scope});
            var resultValue;
            controller.loginField = 'John';
            controller.password = 'Smith';

            LoginService.signIn(controller.loginField,
                                controller.password)
            .then(function() {
                expect(true).toBe(true);
                done();
            }); 
        });
    });
});

The signIn function looks like this:

function signIn(loginField, password) {
        var defer = $q.defer();
   if (loginField && password) {
        defer.resolve("success");
   } else {
        defer.reject("failed");
   }
   return defer.promise;
}

However, I keep getting the error message "Async callback was not invoken within timeout specified..."

Answer №1

Make sure to always include $scope.$digest() at the end of your test in order to properly resolve promises. Promises cannot be resolved within the same digest cycle they were created in.

Elaboration

Although it could have been feasible for the framework to handle instant resolution within the same cycle, the decision was made against it. Consider the following scenario to understand why it might pose a problem:

function login(login, password) {
    LoginService.signIn(login, password)
        .then(function() {
            cancelLoadingAnimation();
        });

    startLoadingAnimation(); 
}

Typically, promises are resolved asynchronously, so issues do not arise. In this case, the loading animation starts in the login function and gets cancelled upon successful sign in. But imagine if the promise instantly resolved (as in a test scenario); the animation could potentially be cancelled before even starting.

To avoid this, one solution is to move the call to startLoadingAnimation() above the signIn() call. However, ensuring promises are resolved asynchronously simplifies code understanding.

Update: @gnerkus points out in their response that creating the $scope as a child of the $rootScope is essential. However, it is crucial to implement both solutions for resolution.

Answer №2

To ensure proper functionality, establish the scope object as an instance of $rootScope within the controller:

describe('LoginController', function() {
  beforeEach(module('task6'));

  var $controller, LoginService;

  // Utilize the $rootScope service to create new scope instances.
  beforeEach(inject(function($rootScope, _$controller_, _LoginService_) {
    $controller = _$controller_;
    LoginService = _LoginService_;
  }));

  describe('LoginController.submitLogin', function() {
    it('checks for existing user', function(done) {
      // Generate a new scope for the controller.
      var scope = $rootScope.$new();
      var controller = $controller('LoginController', {$scope: scope});
      var resultValue;
      controller.loginField = 'John';
      controller.password = 'Smith';

      LoginService.signIn(controller.loginField,
                            controller.password)
        .then(function() {
            expect(true).toBe(true);
            done();
        }); 
    });
  });
});

Answer №3

It turns out that both @gnerkus and @lex82 were correct - in order to handle promises properly, a $digest cycle needs to be run. However, the tricky part is ensuring that a real scope is referenced to achieve this. I have managed to come up with a final, functional version of my code:

describe('LoginController', function() {
    beforeEach(module('task6'));

    var $rootScope, $controller, LoginService;

    beforeEach(inject(function(_$rootScope_, _$controller_, _LoginService_) {
        $rootScope = _$rootScope_;
        $controller = _$controller_;
        LoginService = _LoginService_;
    }));

    describe('LoginController.submitLogin', function() {
        it('checks for the existence of a user', function(done) {
            var $scope = $rootScope.$new();
            var controller = $controller('LoginController', 
                                         {$scope: $scope});
            controller.loginField = 'John';
            controller.password = 'Smith';
            LoginService.signIn(controller.loginField,
                                controller.password)
            .then(function(logged) {
                expect(true).toBe(true);
                done();
            })
            $scope.$digest();
        });
    });
}); 

Big thanks to everyone who helped out!

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

Uncaught ReferenceError: jQuery is undefined" - Navigating the Angular and JavaScript Realm with

There is an Angular project that I am working on, and it utilizes the AvayaClientSDK consisting of three JavaScript files. While trying to import the AvayaClientSDK JS file into my component, an error message stating "jQuery is not defined" appeared. ...

Exploring Ancestors with Jquery to Find Input Fields

In my current project, I am working on a function that needs to extract the values from two input fields within a specific div element. These values will then be added to an array and posted as JSON data. However, I am facing difficulties in navigating thr ...

Is there a way to maintain the checked status of the checkboxes after a page refresh?

Is there a way to keep the checkboxes checked even after the page is refreshed in this code snippet? Any code sample or explanation on how to achieve this would be highly appreciated. The complete project code can be found here: https://github.com/Orelso/P ...

Adding new elements to a list with Jquery, seamlessly integrating them without the need to

I am facing a bit of a roadblock in figuring out how to achieve this, mainly due to my limited understanding of JavaScript. The code that I have been looking at is as follows: http://jsfiddle.net/spadez/VrGau/ What I am attempting to accomplish is allowi ...

Add the $scope ng-click event to a previously hidden element once it becomes visible

If you need a clearer explanation, feel free to ask. I have incorporated a global search function into the header of my website. I am looking to show a separate input box for mobile search that utilizes the same ng-click event, but the input field remains ...

Authentication failure in Asp.Net MVC authorization during Web API request

My ASP.Net MVC application is set up with the default Visual Studio template, using individual user accounts through asp.net identity. When a user logs in and makes calls to MVC Controllers, they are authenticated and I can check for claims by casting to S ...

Leverage Html5 to open and retrieve data from an Excel document

Is there a method to access excel file data using HTML5 while uploading the file on the client side? I have come across the use of reader in JavaScript, but unsure how it can be helpful in this scenario. Is there a way to read excel file data seamlessly ...

Issue with negative z-index in modal window has not been resolved

I'm currently trying to customize the radio button using my own CSS styles, but I've encountered an issue. For some reason, setting the z-index to -1 is not working when the radio button is within a modal. Below is the code snippet that I am wor ...

Forms for uploading and submitting multiple files

On my single page, I have several file upload forms that are generated in a loop. The issue is that the first file upload control works fine, but the others do not. <div> <form action="/docs/1046/UploadDocument?Id=1046&amp;propertyTypeId ...

What is the best way to verify that a JSON key contains only distinct values within a JSON document using JavaScript?

I'm working with a JSON file structure that looks something like this: "fields": { "asset": { "values": [{ "asset": { "id": "Info_text", "type": "text", "value": "ABCD" } ...

Using a self-invoking function in JavaScript with addEventListener

I'm struggling to get an Event Listener to self invoke a function and work correctly. Although the following code runs the function, the Event Listener is not functioning as expected: window.addEventListener("resize", (function () { document.getElem ...

Content Security Policy Error triggered by Iframe Source Running Script in Web Extension

My web extension for Firefox utilizes a content script to add HTML to a webpage when a button is clicked. The injected HTML includes an iFrame nested in multiple div elements. Below is the relevant part of the content script: var iFrame = document.create ...

In Angular, the advanced search box automatically loses focus once a suggested key-value pair is selected using the

I have successfully integrated the angular-advanced-searchbox (using AngularJS 1.5.8, ui-bootstrap, JQuery) following the demo page: html <nit-advanced-searchbox ng-model="searchParams" parameters="availableSearchParams" placeholder="Search...">< ...

Using React hooks to reset state in a reducer: step-by-step guide

event.js import React, { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Link, useHistory } from 'react-router-dom'; import { addEvent } from '../actions/event'; ...

Unable to set values to an array of objects in JavaScript

Currently, I am facing an issue in my node.js project where I am unable to assign values to an array of objects. This problem has occurred before, but I just can't seem to figure out the root cause. My suspicion is that it might be related to variable ...

Step-by-Step Guide to Editing a Firebase Document

Attempting to update a Firebase document results in an error message displayed in the console. Unhandled promise rejection FirebaseError: "Function DocumentReference.update() called with invalid data. Unsupported field value: undefined (found in fie ...

Looking for a way to upload only part of a large file using HTML and PHP

Is it possible to create a PHP script that can upload only the first 1 MB of a very large file? Can this be achieved with a standard form upload in PHP by closing off the connection after 1 MB is uploaded? I have researched various uploaders (HTML5/Java ...

Is there a way to alter a class using ng-class only when the form is deemed valid?

I am trying to implement a feature where an input field shows as required, but once the user enters text, it indicates that the input is valid by changing the border color from red to green. I am facing an issue with this line of code always returning fal ...

Directive experiencing issues with Angular attribute expressions not being evaluated properly

Why is there a difference in how the expression inside the directive is evaluated using the link attribute compared to the template? Note that 'link' is only used here for illustrative purposes. I aim to pass data into a directive through its at ...

Obtain the visual representation of an object created in three.js during the rendering process

I have been pondering the way in which three.js handles rendering. It seems to me that it converts each element into an image before drawing it on a context. I'm curious if there are resources available where I can learn more about this process. Addit ...