Unable to activate parameter function until receiving "yes" confirmation from a confirmation service utilizing a Subject observable

Currently, I am working on unit tests for an Angular application using Jasmine and Karma. One of the unit tests involves opening a modal and removing an item from a tree node.

Everything goes smoothly until the removeItem() function is called. This function uses a confirmationDialogService to confirm the removal of the item:

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ConfirmDialogService {
  subject = new Subject<any>();

  confirmThis(message: string, yesFn: () => void): any {
    this.setConfirmation(message, yesFn);
  }

  setConfirmation(message: string, yesFn: () => void): any {
    const that = this;
    this.subject.next({
      type: 'confirm',
      text: message,
      yesFn(): any {
        that.subject.next(); // This will close the modal
        yesFn();
      }
    });
  }

  getMessage(): Observable<any> {
    return this.subject.asObservable();
  }
}

Here are the relevant functions in my .ts file:

openModalInternal(itemRootNode: SiteTreeNode, detectChanges: Function) {
    // Code for opening modal and handling result
}

removeItem(itemRootNode: SiteTreeNode, detectChanges: Function) {
    // Code for confirming item removal
}

The main issue arises when the test reaches the part where

this.confirmDialogService.confirmThis(...)
is called with the second parameter being a function that should execute after confirmation. In this case, it's the yesFn() function inside the confirmationDialogService.

This leads to difficulties in spying on and expecting certain functions (such as deleteRequestTypeItem()) within the yesFn(). The challenge lies in triggering this confirmation and subsequent execution of the parameter function (yesFn()).

This is the relevant section of my .spec test:

it('should remove item from tree node and show success message', fakeAsync(() => {
    // Test setup and expectations
  }));

Note: It's worth noting that a Subject observable is used in the confirmation service, which presents opportunities for manipulation to achieve the desired outcome.

Answer №1

It appears that the issue may be related to your current approach to spying on deleteRequestTypeItem without returning a value, which is causing an inability to reach the desired outcome.

To address this, consider implementing the following:

import { of } from 'rxjs';
....
it('should successfully remove an item from the tree node and display a success message', fakeAsync(() => {
    const mockOpenModalResult = {
      result: new Promise((resolve, reject) => resolve('Remove'))
    };
    
    spyOn(ngbModal, 'open').and.returnValue(mockOpenModalResult);
    spyOn(component, 'removeItem').and.callThrough();
    spyOn(confirmDialogService, 'confirmThis').and.callThrough();
    spyOn(confirmDialogService, 'setConfirmation').and.callThrough();
    spyOn(confirmDialogService.subject, 'next').and.returnValue(confirmDialogService.subject.asObservable());
    // !! - Consider making changes here - !!
    spyOn(siteMaintenanceService, 'deleteRequestTypeItem').and.returnValue(of({}));
    confirmDialogService.subject.next();
    component.openModalInternal(mockItemRootNode, mockDetectChanges);
    flush();

    expect(component.removeItem).toHaveBeenCalledWith(mockItemRootNode, mockDetectChanges);
    expect(confirmDialogService.confirmThis).toHaveBeenCalled();
    expect(siteService.deleteRequestTypeItem).toHaveBeenCalled();

  }));

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

Transforming the date from JavaScript to the Swift JSON timeIntervalSinceReferenceDate structure

If I have a JavaScript date, what is the best way to convert it to match the format used in Swift JSON encoding? For example, how can I obtain a value of 620102769.132999 for a date like 2020-08-26 02:46:09? ...

Having trouble implementing catchError in a unit test for an HttpInterceptor in Angular 11

I am facing challenges in completing a unit test for my HttpInterceptor. The interceptor serves as a global error handler and is set to trigger on catchError(httpResponseError). While the interceptor functions perfectly fine on my website, I am struggling ...

Switching between Custom tooltips and default tooltips in Chart.js and Angular

I need to show a tooltip based on certain conditions options: { tooltips: if (tooltipCondition === true) { { mode: 'index', position: 'nearest' } } else { { enabled: false, custom: function (tooltipMo ...

A universal TypeScript type for functions that return other functions, where the ReturnType is based on the returned function's ReturnType

Greetings to all TypeScript-3-Gurus out there! I am in need of assistance in defining a generic type GuruMagic<T> that functions as follows: T represents a function that returns another function, such as this example: fetchUser(id: Id) => (disp ...

Assessing the invalidity of user-defined type guards within class implementations

I just wrote this Typescript code and tested it in a sandbox. Check out the code snippet below: class Foo { public get test() : string|number{ return "foo" } public hasString() : this is { test:string }{ return type ...

Angular is unable to start the variable_INITIALIZation process

I've created a method called get that returns data of type ProductModel. getProduct(id:number): Observable<ProductModel[]> { const url = `${'api/product/getProductById/'}/${id}`; return this.http.get<ProductModel[]>(u ...

Using Angular to convert JSON data to PDF format and send it to the printer

Currently, I am retrieving JSON data from an API and now need to convert this data into a PDF format for printing. I am encountering an issue where the CSS styling for page breaks is not rendering properly within my Angular component. When I test the same ...

It appears that Stackblitz may have an outdated package.json file for older Angular projects, causing compatibility issues when running the project locally

Upon reviewing the package.json files for older Angular projects on Stackblitz, I have observed a pattern where Angular9 is listed under devDependencies while dependencies include older versions such as "@angular/core": "7.2.2" or "@angular/core": "6.1.10" ...

Tips on assigning a selected option value to the upload file

The user interface has a selection of documents for the user to choose from. I am trying to associate the fileType with the image that will be uploaded through the input tag. Although I can retrieve the value of the selected document from the drop down usi ...

Unable to bring in the directive from the Angular library

I've created this custom Directive within my library: @Directive({ selector: '[layoutHeaderTitle]', standalone: true }) export class HeaderDirective { constructor( readonly tpl: TemplateRef<any>, private re ...

Setting the "status" of a queue: A step-by-step guide

I created a function to add a job to the queue with the following code: async addJob(someParameters: SomeParameters): Promise<void> { await this.saveToDb(someParameters); try { await this.jobQueue.add('job', ...

Enhancing Vue functionality with vue-class-component and Mixins

In my Vue project, I am using vue-class-component along with TypeScript. Within the project, I have a component and a Mixin set up as follows: // MyComp.vue import Component, { mixins } from 'vue-class-component' import MyMixin from './mixi ...

Testing an angular function that requires multiple arguments in its constructor

My goal is to conduct unit tests on functions within a component. The constructor for this component requires four arguments. Initially, I attempted to simply set the arguments as (new AClass, new BClass, new CClass, new DClass); however, some of these cla ...

What are the consequences of relying too heavily on deep type inference in React and Typescript?

In the process of migrating my React + Javascript project to Typescript, I am faced with preserving a nice unidirectional flow in my existing code. The current flow is structured as follows: 1. Component: FoobarListComponent -> useQueryFetchFoobars() 2 ...

Troubleshooting the issue with Protractor/Jasmine test when browser.isElementPresent does not detect a class in the

As a newcomer to Jasmine testing, I've been facing some challenges while running my tests. Specifically, I have been struggling with my webdriver closing the browser before it can check the '.detailsColumn' element for expected results. Afte ...

What could be preventing the array from updating after subscribing to the service?

Attempting to fetch a list of movies from a Web API server on my localhost. The server does provide data when called with a Get request using the HttpClient module, but struggling to display it. MovieList Component import { Component, OnInit } from &apos ...

Exploring the capabilities of ExcelJS for reading xlsx files within an Angular environment

I'm trying to access a source file, make some changes to it, and then provide it for the user to download. However, I am facing an issue with reading the source file from my project directory. Below is my implementation using excelJS for file reading: ...

Tips for confirming a sub string is present in an array using JavaScript/TScript

I am currently testing for the presence of a SubString within an array. In my test, I am asserting using: expect(classList).toContain('Rail__focused') However, I encountered the following error: Error: expect(received).toContain(expected // inde ...

Issue with retrieving relative time using Moment.js - fromNow()

I want to utilize moment.js to display relative time fromNow(), but I am encountering an issue where the values are being rounded and showing higher durations instead of exact completion times. For example, moment().subtract('s',110).fromNow() / ...

Is it possible to verify if a bearer token has expired within an Angular application?

Is there a secure and efficient way to determine when a bearer token has expired in my Angular application? This is the issue I am facing: If I keep the app open in my browser, someone could potentially access sensitive information on my screen since I am ...