Patience is key when awaiting a response from a fire-and-forget event in jest

While working on building an integration test in jest, I encountered a situation where the product being tested would call into a callback "at some point". The code under test is initiated by calling an event over an rpc "connection" (which is mocked out to use in-memory messages), but the connection notification API returns immediately instead of waiting for all asynchronous processes to finish.

The setup looks something like this:

test.tsx
//...preamble...
it('fire and forget with callback', async () => {
  const receivedRequests = [];
  clientConnection.onRequest(requestData => {
    receivedRequests.push(requestData);
    if (receivedRequests.length == 5) {
      signal.release();
    }
  });

  clientConnection.sendNotification(usefulTestData);
  // wait for a signal on 
  await signal.waitAsync();
  expect(receivedRequests).toEqual(expectedTestDataArrFromFile);
});

server.tsx
// ...preamble...

Server() {
// ...
    //onNotification handler callback's return type is void, not Promise
    this.connection.onNotification((notificationData) => this.handleNotification(notificationData));

    private async function handleNotification(data) {
      // do something interesting
      await this.connection.sendRequest(requestData);
      // do more...
    }

In this scenario, the connection object on both ends are different objects communicating through a mocked-out RPC. The sendNotification method on the client connection eventually triggers the onNotification method on the server side, and vice versa.

Without altering the contract, is there a way to create a signal in the callback that will only allow the test to continue executing conditionally?

Answer №1

Source of inspiration taken from https://github.com/microsoft/vscode-languageserver-node/issues/747

The code below demonstrates the ability to asynchronously wait for an event:

      const emitter = new events.EventEmitter();

      client.onNotification("textDocument/didOpen/processed", () =>
      {
        emitter.emit("notificationProcessed");
      });

      client.sendNotification(
        "textDocument/didOpen",
        openParams,
      );

      await new Promise((resolve) => {
        emitter.on("notificationProcessed", (_args) => {
          resolve();
        })
      });

In this scenario, the event emitter triggers after the fire and forget event finishes.

The test context generates a fresh Promise, which will only be resolved (thus allowing the test context to proceed) once the event is triggered.

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

Angular 2: Issue with Component not reinitializing when query parameters change

I am currently working with Angular 2 and the latest router component to create a search functionality. Upon clicking the search button for the first time, the router navigates to the search component and retrieves data from the service. However, I have no ...

Angular 2 - Emulating Encapsulated Properties

Although I understand that it's best to test code by consuming it the same way it will be in production and therefore not directly dealing with private properties and methods, TypeScript is causing me some confusion. I have a user service. // user.s ...

Ensuring consistency in property formatting strategies

I'm curious if there exists a way to specify the format that an interface property should adhere to. For instance: interface User { age?: number, name: string, birthdate: string // must be in 'YYYY-MM-DD' format } I came across de ...

Ensuring React Lifecycle Methods are Type Safe

For my React project, I have chosen to use TypeScript. After examining the type definitions, it appears that in order for the componentWillReceiveProps's first argument to match the generic type assigned to the Component class, I need to ensure that t ...

Stop the ability to "submit" inline edits in AG-Grid

Currently, I am attempting to implement an inline-editable table using Ag-Grid (v 17.0). However, I have encountered an issue where once I finish editing a row and press enter, the changes are immediately saved. Ideally, I would like the user to remain in ...

The comparison of Booleans in Typescript sometimes produces inaccurate results

There is a strange issue I encountered in one of my classes involving a readonly boolean property. Whenever I try to check this property, the results are not as expected. Here is an example of the code: // vorgang is a reference to the class, isEK is the ...

Simple method for wrestling with objects in TypeScript HTTP POST responses

Can anyone help me understand how to extract information from an object received through an HTTP POST request? Below is the code I am using: this.http.post(url, user, httpOptions).subscribe(result => { console.log(result.status); }); The issue aris ...

Issue with encoding the string in TypeScript is not being resolved appropriately

My application is developed using Angular 7 with Typescript. The code snippet below is from a typescript file: this.confirmationDialogService.confirm(null, 'er du sikker på, at du vil gøre denne ændring', "Acceptere", "Afvise") However, aft ...

Is there a way to apply a filter to an object without using an additional map function?

When an error occurs, my pipe returns of{}. I need to filter it to redirect to other code flows. Currently, I am using the following scenario. Is there a way to remove the map and filter it within the existing filter? readonly k$ = combineLatest( [t ...

Arrange several columns according to the chosen criteria

There is a dropdown feature in my application that has three states - ascending, descending, and none. This dropdown is responsible for rearranging the items in a list. https://i.sstatic.net/xYIfM.png This is my code: list.component.html <div *ngFor= ...

Guide to slicing strings specifically with numerical characters at the end

I've encountered a challenge. I need to slice the last two characters in a string, but only for strings that contain numbers. I attempted using "nome": element.nome.slice(0,-2) and now I require some sort of validation. However, figuring out how to do ...

selective ancestor label Angular 8

I am looking for a way to place my content within a different tag based on a specific condition. For instance, I need my content to be enclosed in either a <table> or <div> depending on the condition. <table|div class="someClass" ...

Exploring the child's type name with React Typescript

In my Typescript application, I am working with React Functional components extensively. I have been attempting to dynamically update certain child components within another component. Here is the code snippet: const memoizedIterateAndAddProps = useCallba ...

The variable 'minSum' is being referenced before having a value set to it

const arrSort: number[] = arr.sort(); let minSum: number = 0; arrSort.forEach((a, b) => { if(b > 0){ minSum += minSum + a; console.log(b) } }) console.log(minSum); Even though 'minSum' is defined at the top, TypeScript still throws ...

How can data be typically encapsulated within Pinia stores when leveraging the composition API?

Currently, in my Vue 3 application with Pinia, I am interested in leveraging the composition API within my Pinia stores. Take a look at this example store: export const useMyStore = defineStore("my", () => { const currentState = ref(0); return ...

Can anyone explain why the Splice function is removing the element at index 1 instead of index 0 as I specified?

selectedItems= [5,47] if(this.selectedItems.length > 1) { this.selectedItems= this.selectedItems.splice(0,1); } I am attempting to remove the element at index 0 which is 5 but unexpectedly it deletes the element at index ...

What is the best way to incorporate this in a callback function?

Utilizing a third-party component requires creating an object for configuration, such as itemMovementOptions in the given code sample. export class AppComponent implements OnInit { readonly itemMovementOptions = { threshold: { horizontal: ...

Incorporating HTML and JavaScript into TypeScript: How to Embed a Shopify Buy Button in a .tsx document

I am currently looking to integrate Shopify with my personal website. My frontend is built using React (NextJS with TypeScript). The embed code for the Shopify buy button consists of an HTML div tag wrapping JavaScript. I am wondering how I can effectivel ...

Service import of the Dynamic Module

Having a global dynamic module with a KafkaService, I encountered an issue where NestJS was unable to inject the KafkaService into my AuthenticationService. The KafkaModule is defined as a Dynamic and Global module. import { DynamicModule, Global, Module ...

Issue with unhighlighted and unmarked text in Visual Studio Code while coding in Python

Experiencing difficulties with unhighlighted and non-red text in Visual Studio Code. Check out the images below to see: General view: https://i.sstatic.net/Vhiz6.png Issue in code without red highlighting: https://i.sstatic.net/S0BIz.png Corrected co ...