fakeAsync failing to synchronize with async task completion

Scenario

In my testing process, I am evaluating a component that utilizes an observable-based service to retrieve and display data for internationalization purposes.

The i18n service is custom-made to cater to specific requirements.

While the component functions correctly in development mode (used in various templates), it fails during testing.

Details

Component Details

@Component({
    selector     : "i18n",
    template     : '<span [innerHTML]="text"></span><span #wrapper hidden="true"><ng-content></ng-content><span>',
    encapsulation: ViewEncapsulation.None
})
export class I18nComponent implements OnChanges {

    constructor(private i18n:I18n) {
    }

    @ViewChild('wrapper')
    content:ElementRef;

    @Input('key')
    key:string;

    @Input('domain')
    domain:string;

    @Input('variables')
    variables:Variables = [];

    @Input("plural")
    plural:number;

    text:string;

    ngOnChanges():any {
        this.i18n.get(this.key, this.content.nativeElement.innerHTML, this.variables, this.domain).subscribe((res) => {
            this.text = res;
        });
    }
}

I18n.get

public get(key:string,
               defaultValue?:string,
               variables:Variables = {},
               domain?:string):Observable<string>{
    const catalog = {
                         "StackOverflowDomain":
                         {
                             "my-key":"my-value"
                         }
                    };

    return Observable.of(catalog[domain][key]).delay(300);
}

using Variables:

export interface Variables {
    [key:string]:any;
}

Testing Process

describe("I18n component", () => {

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers   : [
                I18n,
                {
                    provide : I18N_CONFIG,
                    useValue: {
                        defaultLocale : "fr_FR",
                        variable_start: '~',
                        variable_end  : '~'
                    }
                },
                {
                    provide : I18N_LOADERS,
                    useClass: MockLocaleLoader,
                    multi   : true
                }
            ],
            declarations: [
                I18nComponent
            ]
        });
        fixture = TestBed.createComponent<I18nComponent>(I18nComponent);
        comp = fixture.componentInstance;
    });

    fit("can call I18n.get.", fakeAsync(() => {
        comp.content.nativeElement.innerHTML = "nope";
        comp.key = "test";
        comp.domain = "test domain";
        comp.ngOnChanges();
        tick();
        fixture.detectChanges();
        expect(comp.text).toBe("test value");
    }));

});

Issue Encountered

The test is failing with the following message:

Expected undefined to be 'test value'.

Error: 1 periodic timer(s) still in the queue.

This failure occurs because the i18n.get function has not completed its task before the assertion is verified, resulting in comp.text remaining as undefined.

Solutions Attempted

  • Attempting to adjust the value in the tick method by setting it significantly high (tried with 5000) did not yield any changes.
  • Creating ngOnChanges to return a Promise<void> that resolves immediately after this.text = res; and switching from fakeAsync zone to a simple test utilizing a done method called in the then of comp.ngOnChanges. This solution worked; however, ngOnChanges should not return a Promise, therefore seeking a cleaner alternative.

Answer №1

Keep in mind that utilizing jasmine.done is more comprehensive and robust compared to async and fakeasync

Referencing the following information (current as of this writing): https://angular.io/guide/testing#component-fixture

The mentioned source states:

Utilizing done for test functions, although a bit more complex than async and fakeAsync, is sometimes necessary and effective. For instance, when testing code involving intervalTimer, like async Observables, using async or fakeAsync may not be suitable

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

Guide to creating a personalized pipe that switches out periods for commas

I currently have a number with decimal points like --> 1.33 My goal is to convert this value so that instead of a dot, a comma is displayed. Initially, I attempted this using a custom pipe but unfortunately, it did not yield the desired result. {{get ...

Discover the method for displaying a user's "last seen at" timestamp by utilizing the seconds provided by the server

I'm looking to implement a feature that displays when a user was last seen online, similar to how WhatsApp does it. I am using XMPP and Angular for this project. After making an XMPP request, I received the user's last seen time in seconds. Now, ...

Resolve the issue of text overflow in PrimeNG Turbo Table cells

When utilizing Primeng table and enabling the scrollable feature, the table is expected to display a scrollbar while ensuring that the text within the cells does not overflow. Unfortunately, this expected behavior is not occurring. To see an example of th ...

The collaboration between an object literal declaration and an object instantiated through a constructor function

If I declare let bar: Bar; and set it to initialFooConfig;, is bar still considered as type Bar and an object, or does it become an object in literal notation? If the assignment can be done both ways (assuming initialFooConfig is not a constant), what set ...

"Encountering issues with iterating over a Mongo cursor after upgrading to Angular2 rc5

I'm currently working on a meteor/angular2 application and recently upgraded to rc5. Unfortunately, I encountered an error while trying to display a list of messages based on a mongo cursor in my component. I've tried to simplify the code for bet ...

Angular 2 chart showing horizontal stacking of databars

I am in search of a chart that visually represents different percentages. The purple bar should represent around 40%, the darkest blue approximately 10%, and the green about 8% of the total. After some research, I have come across this as a similar option, ...

Error message: Unable to locate Bootstrap call in standalone Angular project after executing 'ng add @angular/pwa' command

Having an issue while trying to integrate @angular/pwa, it keeps showing me an error saying "Bootstrap call not found". It's worth mentioning that I have removed app.module.ts and am using standalone components in various places without any module. Cu ...

Auto increment package.json version in a monorepo configuration

I am working on an Angular 6 app configured as a monorepo, comprising of a project that needs to be published to NPM along with a demo app. To update the versions of both the application and the project, I would like to utilize the npm version command. Th ...

Unexpected request causes a dilemma during the karma test for an Angular directive

Let's discuss a straightforward directive: 'use strict'; angular .module('app') .directive('ngEmailMask', ngEmailMask); function ngEmailMask() { var directive = { replace: true, restrict: 'EA', ...

Can APP_INITIALIZER function within lazy loaded modules in Angular?

I recently implemented a lazy loaded module in my application and tried to add APP_INITIALIZER but unfortunately, it doesn't seem to be triggering. Surprisingly, the syntax I used is identical to that of my main app where it works perfectly fine. Can ...

Utilizing RxJS finalize in Angular to control the frequency of user clicks

Can someone provide guidance on using rxjs finalized in angular to prevent users from clicking the save button multiple times and sending multiple requests? When a button click triggers a call in our form, some users still tend to double-click, leading to ...

Creating a Thrilling Triple Image Transformation on Hover using Material-UI

Here is a Codepen showcasing a triple hover effect on an image: Codepen I attempted to recreate this effect as a styled component in React Typescript using MUI and MUI Image, but encountered an error with my styling that is preventing it from working in m ...

Out of the blue, I encountered an issue while trying to install Express in node.js using Types

Encountered a failure while attempting to install Express with node.js in Typescript. Received the following warning: https://i.sstatic.net/XcrGX.png Performed npm initialization, started index.js, created tsconfig.json, and installed ts-node. The comman ...

Steps for creating sha512 in Ionic 2 with Angular 2

I am looking to integrate the PayUbizz payment gateway into the Ionic2 framework. As part of this integration, I need to create a hash code using product details. For example: sha512(C0Dr8m|12345|10|Shopping|Test|<a href="/cdn-cgi/l/email-protection" ...

Changing return values with Jest mocks in TypeScript

Here I am again with a very straightforward example. In summary, I require a different response from the mocked class. Below is my basic class that returns an object: class Producer { hello() { return { ...

Creating TypeScript domain objects from JSON data received from a server within an Angular app

I am facing a common challenge in Angular / Typescript / JavaScript. I have created a simple class with fields and methods: class Rectangle { width: number; height: number; area(): number { return this.width * this.height; } } Next, I have a ...

Exploring Substrings in TypeScript strings

Can you pass a partial string (substring) as a value to a function in TypeScript? Is something like this allowed? function transform( str: Substring<'Hello world'> ) { // ... } If I call the function, can I pass a substring of that st ...

Avoid flickering of images by properly handling the object URL when setting the image source in an asynchronous pipe

Implementing a JWT authorized endpoint for images has made it impossible to directly link to image urls in HTML. To work around this issue, we have created an async pipe that loads the image with proper authorization (handled by an HTTP interceptor, not s ...

Issue: The JSX element 'X' is missing any constructors or call signatures

While working on rendering data using a context provider, I encountered an error message stating "JSX Element type Context does not have any constructor or call signatures." This is the code in my App.tsx file import { Context } from './interfaces/c ...

Keep a record of the Observable for future subscription in Angular 7

In my scenario, I anticipate that an Angular 7 client may frequently go offline for extended periods of time. When the client is connected, I intend to process HTTP requests as usual using observables. However, in the event of a network disconnection, I p ...