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

Can you identify the TypeScript type for an array containing various Angular components?

In my application, I have a diverse range of components that I would like to organize into an array. There are no restrictions on what types of components can be included in this array, as long as they are Angular components. What is the correct way to de ...

Dealing with a sequence of deletions in Angular 4

When working with a ReplaySubject of size 3 and a delete chain that must be in order, I encountered an issue. After the deletion process is completed, I need to redirect the user. However, when subscribing to this ReplaySubject method, it sends "here fin ...

Which types of mouse events are compatible with Angular2?

Exploring mouse events in Angular2 has sparked my curiosity. I have implemented the click event, but now I wonder what other mouse events are available, such as mouseover. Where can I find a comprehensive list of mouse events supported by Angular2? The o ...

Clicking on an Angular routerLink that points to the current route does not function unless the page is re

Currently, I am working on an E-commerce project. In the shop page, when a user clicks on the "View More" button for a product, I redirect them to the product details page with the specific product id. <a [routerLink]="['/product-details' ...

Transforming Typescript types into object literals

type SelectData = { name?: boolean; address?: boolean; } const selectData: SelectData = { name: true } const untypedSelectData = { name: true } I am looking to enforce TypeScript to throw an error if there is an attempt to assign a property that ...

Error code 2769 in Typescript occurs when attempting to transfer process information to the data state in order to utilize it within a modal

I'm encountering an issue while trying to pass a process to a setData state from a .map function in order to display it on a modal. The error message I'm receiving is: "No overload matches this call. Overload 1 of 2, '(props: { compone ...

Customizing Angular 2's Webpack environment setup dynamically

Currently, I have set up Webpack to compile my Angular project. Within my project, there is an important file called env.js: module.exports.ENV = { API_URL: "http://localhost:5001/", ... }; In the webpack.common.js file, I am referencing this file l ...

An effective way to pass an array as data to an express router from Angular

I've been attempting to retrieve more detailed information for multiple ID's from the database, but I've hit a roadblock. Below is the array of member ID's: var memberIds = ["2892056", "2894544", "2894545" ...

Make sure to always keep all stars contained within a div element

How can I keep five stars inside a div even when the screen size is small? I have created a div with an image and I want to place five stars within that div. However, as I reduce the size of the screen, the stars come out of the box. Is there a way to en ...

Evaluating a React Hooks component using Jest, Enzyme, and Axios for testing purposes

I'm having some trouble writing test cases for my React component that fetches data using Axios in HomePage.js and renders it using Post.js. Every time I attempt to write the tests, they fail. Can someone help me figure out what I'm doing wrong w ...

Developing interconnected dynamic components in Angular

Can you help me figure out how to create nested dynamic components while maintaining the parent-child relationship? For instance, if I have data structured like this: - A --A.1 --A.2 -B --B.1 -C I want to build components that reflect this structure, su ...

Guide to testing the next() function in Express middleware using JEST

Despite my efforts, I couldn't solve this problem on my own so I decided to seek help. In my Node.js and Express application, I am using a middleware that appears as follows: import mainConfig from '../mainConfig/index'; const axios = requi ...

Searching through all values can be done by following these steps

Need help with implementing a search feature that can search all values in Angular2. Here's the current code snippet: import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'filter' }) export class FilterPipe implem ...

Assign Monday as the selected day of the week in the MUI Datepicker 6

I am currently using version 6 of the material ui Datepicker and I am trying to configure it so that the week starts on Monday. import React from "react"; import { DatePicker as DatePickerDestop } from "@mui/x-date-pickers/DatePicker"; ...

What does Typescript compile when aiming for ES5 / ES3?

I'm currently grappling with the nuances of when the Typescript compiler decides to transpile code in order to align it with my designated target ECMAScript version (ES5 or ES3). As an example, TSC has no problem transpiling for(var int of intArray); ...

Tips for simulating eventHubclient (azure/event-hub) producer connectivity in unit testing with mocha

Here is the Node.js code snippet that I need assistance with. My goal is to simulate the eventHub connection to trigger the API call. const { EventHubProducerClient } = require("@azure/event-hubs"); const connectionString = "YOUR EVENT HUBS NAMESPACE CON ...

When loading a value from a promise in a service to populate a formgroup, the placeholder in the md-input does not

Currently in my Angular2 project, I am experimenting with a new approach to loading data into my input fields. By utilizing formgroup, I can maintain cleaner HTML and incorporate more validation logic in the component's TypeScript file. The code snipp ...

Transcompiling TypeScript for Node.js

I'm in the process of developing a Node project using Typescript, and I've configured the target option to es6 in my tsconfig.json file. Although Node version 8 does support the async/await syntax, Typescript automatically converts it to a gener ...

Route user based on login status using router

I want to set up automatic routing to a login page for users who are not logged in. app.module.ts import { RouterModule, Routes } from '@angular/router'; import { AppComponent } from './app.component'; import { LoginComponent } from &ap ...

Error: It is not possible to add the "providers" property as the object is not extendable within N

Ever since updating to the latest version of NGRX, I've been encountering an issue: users$= createEffect(() => this.actions$ .pipe( ofType(users.LOAD_USERS), tap((action: users.LoadUsersAction) => { action.item.name = ...