How can I get angular custom route data within a test?

33 Views Asked by At

In my route config each route has some custom data regarding page/theme colors

export const appRoutes: Array<Route> = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [AuthGuard],
    data: { areaColorName: 'default' },
  },
  {
    path: 'question-library',
    component: QuestionLibraryComponent,
    canActivate: [AuthGuard],
    data: { areaColorName: 'questions' },
  }
  //etc...
];

In my app.component.ts I read that data like this where I then set a CSS class on the body with the received data

private readonly destroyRef = inject(DestroyRef);
private readonly router = inject(Router);

ngOnInit(): void {
  this.router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
    if (event instanceof NavigationEnd) {
      const routeData = this.router.routerState.root.firstChild?.snapshot.data;
      console.log(this.router.routerState.root.firstChild)
      let areaName = 'default';
      if (routeData && Boolean(routeData['areaColorName'])) {
        areaName = routeData['areaColorName'] as string;
      }

      //remove any CSS class that begin with the BG status prefix
      this.document.body.className = this.document.body.className
        .split(' ')
        .filter((c) => !c.startsWith(this.bgClassPrefix))
        .join(' ')
        .trim();

      //Add a new class to indicate the status
      this.document.body.classList.add(this.bgClassPrefix + areaName);
    }
  });
}

How can I go about testing this behavior? Here's what I have so far:

describe('AppComponent', () => {
  let app: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let injectedDocument: Document;
  let injectedRouter: Router;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([
          {
            path: 'dashboard',
            component: DashboardComponent,
            data: { areaColorName: 'foo' },
          },
        ]),
        HttpClientTestingModule,
        AppComponent,
      ],
    });
    fixture = TestBed.createComponent(AppComponent);
    app = fixture.componentInstance;
    injectedDocument = TestBed.inject(DOCUMENT);
    injectedRouter = TestBed.inject(Router);
  });

  it('should change the body class when navigating to different routes', () => {
    fixture.detectChanges(); // triggers ngOnInit()

    const event = new NavigationEnd(1, '/dashboard', '/dashboard');
    const ev = injectedRouter.events as Subject<Event>;
    ev.next(event);
    fixture.detectChanges();

    expect(injectedDocument.body.classList).not.toContain('app-area-default');
    expect(injectedDocument.body.classList).toContain('app-area-foo');
  });
}

This does trigger a routing event, but when console.log(this.router.routerState.root.firstChild) (inside ngOnInit) is called from within the component it just returns null. What do I need to do to be able to extract the custom route data from within a test?

1

There are 1 best solutions below

0
wnvko On

You are not performing any navigation in your test. You should call initialNavigation method over the router like this:

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([
          {
            path: 'dashboard',
            component: DashboardComponent,
            data: { areaColorName: 'foo' },
          },
        ]),
        HttpClientTestingModule,
        AppComponent,
      ],
    });
    fixture = TestBed.createComponent(AppComponent);
    app = fixture.componentInstance;
    injectedDocument = TestBed.inject(DOCUMENT);
    injectedRouter = TestBed.inject(Router);
    injectedRouter.initialNavigation(); // This sets up the location change listener and performs the initial navigation
  });