0

This question is a continuation of sorts from the previous question (linked here). I'm still working with the same files with the intent of writing simple tutorials for Angular testing. The new issue relates to the setTimeout function.

On both ngOnInit and ngAfterViewInit I'm implementing setTimeout (other tests I'll be writing in the future do this, so this is just to create a basic example). The function inside sets the value of a variable that gets passed to a child component through the HTML file. This child component's variable is then checked for a specific value.

I've already tried working with tick(value) and fixture.detectChanges() to try to trigger the contents of the setTimeout function, but the value is still undefined (see code snippets).

random.test.component.ts

public dataThatGetsDelayed: any;

constructor() {

}

ngOnInit() {
    setTimeout(() => {
        this.dataThatGetsDelayed = "OnInit";
    }, 100);
}

ngAfterViewInit() {
    setTimeout(() => {
        this.dataThatGetsDelayed = "AfterView";
    }, 100);
}

random.test.component.html

[variableThatStoresDelayedData]="dataThatGetsDelayed"

random.test.component.child.ts

@Input() variableThatStoresDelayedData: any;

random.test.file.spec.ts

it('Testing if receiving input works properly with a time delay in ngOnInit', fakeAsync(() => {
    const componentInThePage: TestComponentChild = fixture.debugElement.query(By.directive(TestComponentChild)).componentInstance;
    expect(componentInThePage).toBeTruthy();

    tick(100);
    fixture.detectChanges();

    fixture.whenStable().then(() => {
        expect(componentInThePage.variableThatStoresDelayedData).toEqual("OnInit");
    });
}));

it('Testing if receiving input works properly with a time delay in ngAfterViewInit', fakeAsync(() => {
    const componentInThePage: TestComponentChild = fixture.debugElement.query(By.directive(TestComponentChild)).componentInstance;
    expect(componentInThePage).toBeTruthy();

    //tick(100);
    //component.ngAfterViewInit();

    fixture.whenStable().then(() => {
        expect(componentInThePage.variableThatStoresDelayedData).toEqual("AfterView");
    });
}));

I read that I was supposed to use whenStable at the end, but I now know this gets passed over entirely. Without that, I get the error "Expected undefined to equal 'OnInit'/'AfterView'". I omitted the second tick because it causes the error "1 timer(s) still in the queue".

How do I tell the program to wait until the value is set in the setTimeout function?

Justin
  • 159
  • 1
  • 2
  • 13

1 Answers1

0

Alright, so through some research and experimentation, I've found the solution. The code comments explain the finer details:

random.test.component.ts

public dataThatGetsDelayed: any = "Initial value"; //This is just to show how the value changes.

constructor(private detectorReference: ChangeDetectorRef) {
    //The detectorReference is used in ngAfterViewInit
}

ngOnInit() {
    setTimeout(() => {
        this.dataThatGetsDelayed = "OnInit";
    }, 100);
}

ngAfterViewInit() {
    setTimeout(() => {
        this.dataThatGetsDelayed = "AfterView";
        this.detectorReference.detectChanges(); //Needed to stop the error about changing the value.
    }, 200);
}

(Thanks to this source for the help!)

random.test.file.spec.ts

//This test is for the ngOnInit function
it('Testing if receiving input works properly with a time delay in ngOnInit', fakeAsync(() => {
    const componentInThePage: TestComponentChild = fixture.debugElement.query(By.directive(TestComponentChild)).componentInstance;
    expect(componentInThePage).toBeTruthy();

    expect(componentInThePage.variableThatStoresDelayedData).toEqual("Initial value");

    component.ngOnInit();
    //tick(99); - This doesn't /quite/ run out the setTimeout function from the ngOnInit function.
    tick(100); //This does, though.
    fixture.detectChanges();

    expect(componentInThePage.variableThatStoresDelayedData).toEqual("OnInit");
}));

//This test is for the ngAfterViewInit function. Note that the value isn't set with ngOnInit first;
//only the explicit method call will perform the desired action.
it('Testing if receiving input works properly with a time delay in ngAfterViewInit', fakeAsync(() => {
    const componentInThePage: TestComponentChild = fixture.debugElement.query(By.directive(TestComponentChild)).componentInstance;
    expect(componentInThePage).toBeTruthy();

    expect(componentInThePage.variableThatStoresDelayedData).toEqual("Initial value");

    component.ngAfterViewInit();
    //tick(199); - Again, this doesn't /quite/ run out the setTimeout.
    tick(200); //But, once again, this does.
    fixture.detectChanges();

    expect(componentInThePage.variableThatStoresDelayedData).toEqual("AfterView");
}));

Hope this helps someone in the future!

Justin
  • 159
  • 1
  • 2
  • 13