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

The data type 'string' cannot be assigned to the data type 'undefined'

These are the different types available: export type ButtonProperties = { style?: 'normal' | 'flat' | 'primary'; negative?: boolean; size?: 'small' | 'big'; spinner?: boolean; } export type ...

Utilizing TypeScript namespaced classes as external modules in Node.js: A step-by-step guide

My current dilemma involves using namespaced TypeScript classes as external modules in Node.js. Many suggest that it simply can't be done and advise against using namespaces altogether. However, our extensive codebase is structured using namespaces, ...

Include a header in the API HTTP call for Angular 2 and Ionic 2

Working on my Ionic 2 app, I am using Angular 2 Http to retrieve JSON from an API. However, I am struggling to send the app-id, app-key, and Accept as headers in the main code snippet below: import {Component} from '@angular/core'; import {NavC ...

Beginner - managing tasks with React and TypeScript

Can someone assist me with a minor issue? I have experience programming in React using pure JavaScript, but I'm struggling with transitioning to TypeScript. I don't quite understand all the hype around it and am finding it difficult to work with. ...

Enabling social media crawlers to function properly (such as Facebook and Twitter) in an Angular 11 Single Page Application (SPA

I've developed a Single Page Application (SPA) using Angular 11 and it's being hosted on a shared hosting server. The problem I'm facing is that I can't share any pages, except for the homepage (/), on social media platforms like Faceb ...

Updating and Preserving Content in Angular

I've encountered an issue while working on a code that allows users to edit and save a paragraph on the screen. Currently, the editing functionality is working fine and the save() operation is successful. However, after saving, the edited paragraph do ...

Is there a way to dynamically create a property and assign a value to it on the fly?

When retrieving data from my API, I receive two arrays - one comprising column names and the other containing corresponding data. In order to utilize ag-grid effectively, it is necessary to map these columns to properties of a class. For instance, if ther ...

Expanding the width of an MUI Button smoothly using transitions

I am currently working on a custom ToggleButton that changes its text based on certain state changes. However, I am facing an issue where the width of the button abruptly grows when the text changes. How can I smoothly transition this change in width? Bel ...

having difficulties connecting the paginator with MatTable

I'm struggling to implement pagination for my mat-table in Angular 6. I've referenced some examples from the following link, but unfortunately, it's not functioning as expected: https://material.angular.io/components/table/examples While t ...

Issue with parsing string data from API call in Angular (Web Core 3)

Controller code: [Route("api/[controller]")] [ApiController] public class CustomController : ControllerBase { ApplicationDbContext dbContext = null; public CustomController(ApplicationDbContext ctx) { dbContext = ctx; } ...

Display the map using the fancybox feature

I have added fancybox to my view. When I try to open it, I want to display a map on it. Below is the div for fancybox: <div id="markers_map" style="display:none"> <div id="map_screen"> <div class="clear"></div> </div&g ...

Deactivate interactive functions on the mat-slider while preserving the CSS styling

I'm attempting to create a mat-slider with a thumb label that remains visible at all times. Below is my mat-slider component: <mat-slider class="example-margin" [disabled]="false" [thumbLabel]="true" [tickInterval]="tickInterval" ...

Update the TypeScript definitions in the index.d.ts file using the npm command, by overriding it with the reference types

After running npm install, I noticed that the index.d.ts file contains a reference to the wrong path: /// <reference types="[WrongPath]"/>. As someone new to npm, TypeScript, and web development in general, I'm wondering if it's possible t ...

What are the distinctions between generic and discriminated types?

Hi there, I've been thinking of an idea but I'm not sure how to implement it or if it's even possible. Is there a way to create a type SomeType where the first property can be any value from the set T, but the second property cannot be the ...

The TSX file is encountering difficulty rendering an imported React Component

Upon attempting to import the Day component into the Week component (both .tsx files), an error is thrown: Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. ...

Enhance your workflow with Visual Studio Code by incorporating multiple commands

Embarking on my journey to create my first VSC extension by following this tutorial. Within the "extension.ts" file resides a simple hello world command. My ambition is to introduce another command called git_open_modified_files, however, the tutorial la ...

Using Angular and Typescript to Submit a Post Request

I am working on a basic Angular and Typescript page that consists of just one button and one text field. My goal is to send a POST request to a specific link containing the input from the text field. Here is my button HTML: <a class="button-size"> ...

Is Angular 17 failing to detect changes in dependent signals?

Recently, I delved into experimenting with Angular Signals. Currently, I am working on a project involving a list of individuals with pagination that I display in my template. Strangely, when I modify the page, the pageState object updates accordingly as i ...

Implementing TypeScript/Angular client generation using Swagger/OpenAPI in the build pipeline

Generating Java/Spring Server-Stubs with swagger-codegen-maven-plugin In my Spring Boot Java project, I utilize the swagger-codegen-maven-plugin to automatically generate the server stubs for Spring MVC controller interfaces from my Swagger 2.0 api.yml fi ...

Removing validators in Angular forms after submitting the form and resetting it

I am currently working on an Angular app that includes a form. Whenever I click the submit button, the reset() function gets triggered on the form. However, after the reset() function is called, all inputs are marked as having errors. I have tried using fu ...