If I have a non-standalone component as follows:
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'sb-index',
template: `
<a [routerLink]="['routeA']" [routerLinkActive]="'active'">A</a>
<a [routerLink]="['routeB']" [routerLinkActive]="'active'">B</a>
`
})
export class IndexComponent {
}
I'm able to test the routerLinkActive
with the following spec, as seen in SO question:
import { Component, NgZone } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Router, RouterLinkActive } from '@angular/router';
import { IndexComponent } from './index.component';
import { RouterTestingModule } from '@angular/router/testing';
@Component({
selector: 'sb-stub'
})
class StubComponent {}
describe('IndexComponent', () => {
let fixture: ComponentFixture<IndexComponent>;
let component: IndexComponent;
beforeEach(() => {
TestBed
.configureTestingModule({
declarations: [
IndexComponent,
StubComponent
],
imports: [
RouterTestingModule.withRoutes([
{ path: 'routeA', component: StubComponent },
{ path: 'routeB', component: StubComponent }
])
]
})
.compileComponents();
fixture = TestBed.createComponent(IndexComponent);
component = fixture.componentInstance;
});
it('should show "A" route as active', fakeAsync(() => {
fixture.detectChanges();
TestBed.inject(NgZone).run(() => {
TestBed.inject(Router).navigate(['routeA']);
tick();
fixture.detectChanges();
fixture.debugElement
.queryAll(By.directive(RouterLinkActive))
.map(debugElement => debugElement.nativeElement as HTMLAnchorElement)
.forEach(nativeElement => {
if (nativeElement.textContent === 'A') {
expect(nativeElement.classList.contains('active')).toBeTrue();
} else {
expect(nativeElement.classList.contains('active')).toBeFalse();
}
});
});
}));
});
I can't figure out how to setup the same tests, if I change the IndexComponent
to be a standalone component as follows:
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { RouterLink, RouterLinkActive, RouterModule, RouterOutlet } from '@angular/router';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'sb-index',
standalone: true,
template: `
<a [routerLink]="['routeA']" [routerLinkActive]="'active'">A</a>
<a [routerLink]="['routeB']" [routerLinkActive]="'active'">B</a>
`,
imports: [ RouterLink, RouterLinkActive, RouterOutlet ]
})
export class IndexComponent {
}
I've tried a number of different things, mostly focusing on the injection and scoping aspects as I assume that's what's changed as follows:
import { Component, EnvironmentInjector, NgZone } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { provideRouter, Router, RouterLinkActive, RouterOutlet } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { RouterLinkDirectiveStub } from '@sb/testing';
import { IndexComponent } from './index.component';
@Component({
standalone: true,
selector: 'sb-stub'
})
class StubComponent {}
describe('IndexComponent', () => {
let fixture: ComponentFixture<IndexComponent>;
let component: IndexComponent;
beforeEach(() => {
TestBed
.configureTestingModule({
imports: [
IndexComponent,
StubComponent
],
providers: [
provideRouter([
{ path: 'routeA', component: StubComponent },
{ path: 'routeB', component: StubComponent }
])
]
})
.overrideComponent(
IndexComponent,
{
set: {
imports: [ RouterOutlet, RouterLinkActive, RouterLinkDirectiveStub ]
}
}
)
.compileComponents();
fixture = TestBed.createComponent(IndexComponent);
component = fixture.componentInstance;
});
it('should show "A" route as active', fakeAsync(() => {
fixture.detectChanges();
fixture.ngZone?.run(() => {
fixture.componentRef.injector.get(Router).navigate(['routeA']);
// TestBed.inject(NgZone).run(() => {
// TestBed.inject(EnvironmentInjector).get(NgZone).run(() => {
// TestBed.inject(Router).navigate(['routeA']);
// TestBed.inject(EnvironmentInjector).get(Router).navigate(['routeA']);
// TestBed.platform.injector.get(Router).navigate(['routeA']);
tick();
fixture.detectChanges();
fixture.debugElement
.queryAll(By.directive(RouterLinkActive))
.map(debugElement => debugElement.nativeElement as HTMLAnchorElement)
.forEach(nativeElement => {
if (nativeElement.textContent === 'A') {
expect(nativeElement.classList.contains('active')).toBeTrue();
} else {
expect(nativeElement.classList.contains('active')).toBeFalse();
}
});
});
}));
});
Almost all of these give me the message: IndexComponent should show "A" route as active FAILED Expected false to be true.
Through debugging, I can see that the tests are finding the elements with the RouterLinkActive
directive, but the routing that is being done at the beginning of the test method doesn't seem to effect the routerLinkActive, so none of the elements are getting the 'active' class.
Is there a way to test the routerLinkActive
elements inside of a standalone component?