When does the async pipe subscribe during an Angular unit test with Jasmine?

92 Views Asked by At

I have a navbar that displays a link if you're on the login page (which directs you to the next page) and a select drop-down that displays instead once you've navigated to the next page. A minimal working component example would be something like

@Component({
    selector: 'app-navbar',
    template: '''
        <ul>
            <ng-container *ngIf="isLogin$ | async else selector">
                <li><a routerLink="/next">Next page</a></li>
            </ng-container>
            <ng-template #selector><li>You\'re here on Next</li></ng-template>
        </ul>
    '''
})
export class NavComponent implements OnInit {
    isLogin$!: Observable<boolean>

    constructor(private router: Router) { }

    ngOnInit(): void {
        this.isLogin$ = router.events.pipe(
            filter((e): e is NavigationEnd => e instanceOf NavigationEnd),
            map((e) => e.urlAfterRedirect === '/login')
        );
    }
}

It seems as if the async pipe isn't subscribing to the isLogin$ observable at any point during my test, even after calling detectChanges on the component fixture.

The basic test looks something like

describe('AppComponent', () => {

    let app: AppComponent;
    let fixture: ComponentFixture<AppComponent>;
    let nav: NavComponent;
    let navFixture: ComponentFixture<NavComponent>;
    let router: Router;
    let location: Location;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            imports: [
                RouterTestingModule.withRoutes(routes),
                HttpClientTestingModule
            ],
            declarations: [
                AppComponent,
                LoginComponent,
                NextComponent,
                NavComponent
            ]
        }).compileComponents();

    });

    beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        app = fixture.debugElement.componentInstance;
        navFixture = TestBed.createComponent(DomHeaderComponent);
        nav = headerFixture.debugElement.componentInstance;
        location = TestBed.inject(Location);
        router = TestBed.inject(Router);
        router.initialNavigation();
    });

    it('should navigate to the next page on link click', waitForAsync(() => {
        fixture.detectChanges();
        navFixture.detectChanges();
        navFixture.whenStable().then(() => {
            expect(location.path()).toBe('/login');
            const de = navFixture.debugElement.query(By.css('a'));
            const ne = de.nativeElement as HTMLAnchorElement;
            ne.click(); // fails
            expect(location.path()).toBe('/next');
        });
    }));

However, the native element is null. The isLogin$ observable fires (I can subscribe to it directly in tests and check its value), the location is updated, but the async pipe never seems to subscribe and update the DOM with the link. Thus, it seems as if the link doesn't exist. I'm perplexed. Cheers.

0

There are 0 best solutions below