1

I have an angular component that polls for new data, and while we are waiting for a response it updates a message in the UI every X seconds. It cycles through a simple string array of messages to show.

Once the correct data is received, we navigate to the appropriate page.

this.pollSubscription = timer(0, pollTimeInSeconds)
    .pipe(
        //convert each observable iteration into an interval in seconds
        map((count: number) => count * pollTimeInSeconds),
        //keep going if we are below the max poll time
        takeWhile((time: number) => time < this.maxPollTime),
        //update the UI message
        tap((time: number) => this.updateCurrentMessage(time)),
        //
        switchMap(() => this.getDataOrError$())
    )
    .subscribe(
        //navigate to the correct page
        (d: IData) => this.navigateBasedOnStatus(d),
        //If an error occurs, OR the observable completes (which should not happen), navigate to the error page
        () => this.navigateToError(),
        () => this.navigateToError()
    );

I want to test this behavior, but I'm not entirely sure how to mock the timer for this situation. I saw this SO answer but I wasn't able to make that work for my situation.

Ideally I'd like to structure a test somewhat like this to ensure that each message shows and that if the timer goes longer than expected it loops back around to the first message:

it('should show every status as the timer changes', fakeAsync(() => {
    tick(0);
    fixture.detectChanges();
    expect(debugEl.nativeElement.innerText.trim()).toEqual('Message one');

    tick(1000);
    fixture.detectChanges();
    expect(debugEl.nativeElement.innerText.trim()).toEqual('Message two');

    tick(2000);
    fixture.detectChanges();
    expect(debugEl.nativeElement.innerText.trim()).toEqual('Message three');

    //etc...
}));

EDIT I'm including a new attempt at testing this based on some comments below, but this is stil not working

let timerTick: (milliseconds: number) => void;

beforeEach(() => {
    let fakeNow = 0;
    timerTick = (milliseconds: number): void => {
        fakeNow += milliseconds;
        tick(milliseconds);
    };
    asyncScheduler.now = (): number => fakeNow;
});

afterEach(() => {
    delete asyncScheduler.now;
});

it('should show every status as the timer changes', fakeAsync(() => {
    fixture.detectChanges(); // triggers ngOnInit()

    const pollTimeInSeconds = loginPollTime * 1000;
    let received: number | undefined;
    timer(0, pollTimeInSeconds, asyncScheduler).subscribe((value: number) => received = value);
    console.log(received);

    timerTick(0);
    fixture.detectChanges();
    expect(debugEl.nativeElement.innerText.trim()).toEqual('Message one');
    console.log(received)

    timerTick(pollTimeInSeconds);
    fixture.detectChanges();
    expect(debugEl.nativeElement.innerText.trim()).toEqual('Message two');
    console.log(received)

    timerTick(pollTimeInSeconds * 2);
    fixture.detectChanges();
    expect(debugEl.nativeElement.innerText.trim()).toEqual('Message three');
    console.log(received)
}));

The jasmine output is:

Expected 'Message one' to equal 'Message two'.
Expected 'Message one' to equal 'Message three'.

The console output is:

> undefined
> 0
> 1
> 3
Chris Barr
  • 29,851
  • 23
  • 95
  • 135
  • "... wasn't able to make that work for my situation." Why? – cartant Apr 10 '19 at 17:32
  • I'm not terribly familiar with RXJS so that code just didn't seem to work when I tried it. I was not able to get it to increment the timer no matter what I did. If you think it would work with some changes I'd love to see what they are! – Chris Barr Apr 10 '19 at 18:10
  • 1
    Have a look at this https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/testing/marble-testing.md but in general you need to pass an instance of `TestScheduler` to `timer(x, y, testSchedulerInstance)` – martin Apr 11 '19 at 07:07
  • @martin check my updated post, I've tried out what I think should be working here... but I'm having no luck – Chris Barr Apr 11 '19 at 14:23

0 Answers0