I have a GitHub actions workflow that runs Karma for an Angular project. When the test tries to read the 'innerText' of a native element. The error 'TypeError: Cannot read properties of null (reading 'innerText')' is thrown, which I assume is because the query selector is unable to find the elements. However, the tests pass locally. Furthermore, there are other query selector tests that are passing in the virtual environment.
HTML:
<mat-tab-group>
<mat-tab>
<ng-template mat-tab-label>
<mat-icon
[matTooltipDisabled]="(isWeb | async)!.matches"
[matTooltipShowDelay]="0"
matTooltip="Membership"
matTooltipPosition="after"
>groups
</mat-icon>
<mat-label *ngIf="(isWeb | async)?.matches" class="tab-mat-label">Membership</mat-label>
</ng-template>
Content 1
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<mat-icon
[matTooltipDisabled]="(isWeb | async)!.matches"
[matTooltipShowDelay]="0"
matTooltip="Rosters"
matTooltipPosition="after">calendar_month
</mat-icon>
<mat-label *ngIf="(isWeb | async)?.matches" class="tab-mat-label">Rosters</mat-label>
</ng-template>
Content 2
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<mat-icon
[matTooltipDisabled]="(isWeb | async)!.matches"
[matTooltipShowDelay]="0"
matTooltip="Notifications"
matTooltipPosition="after">edit_notifications
</mat-icon>
<mat-label *ngIf="(isWeb | async)?.matches" class="tab-mat-label">Notifications</mat-label>
</ng-template>
Content 3
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<mat-icon
[matTooltipDisabled]="(isWeb | async)!.matches"
[matTooltipShowDelay]="0"
matTooltip="Security"
matTooltipPosition="after">shields
</mat-icon>
<mat-label *ngIf="(isWeb | async)?.matches" class="tab-mat-label">Security</mat-label>
</ng-template>
Content 3
</mat-tab>
</mat-tab-group>
Component Code:
import { Component, OnInit } from '@angular/core';
import {BreakpointObserver, Breakpoints, BreakpointState} from "@angular/cdk/layout";
import {Observable} from "rxjs";
@Component({
selector: 'racs-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.scss']
})
export class AdminComponent implements OnInit {
constructor(private _breakpointObserver: BreakpointObserver) { }
public get isWeb(): Observable<BreakpointState> {
return this._breakpointObserver.observe(Breakpoints.Web);
}
ngOnInit(): void {
}
}
Test code:
import {AdminComponent} from './admin.component';
import {RacsAdminModule} from "./racs-admin.module";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {BreakpointObserver} from "@angular/cdk/layout";
describe('AdminComponent', () => {
let component: AdminComponent;
let fixture: ComponentFixture<AdminComponent>;
let compiled: any;
const tabLabelIconPairs: { [key: string]: string; } = {
Membership: 'groups',
Rosters: 'calendar_month',
Notifications: 'edit_notifications',
Security: 'shields',
}
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AdminComponent],
imports: [RacsAdminModule, BrowserAnimationsModule],
providers: [BreakpointObserver]
})
.compileComponents();
fixture = TestBed.createComponent(AdminComponent);
compiled = fixture.nativeElement;
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create and show tabs with labels in Web View', () => {
expect(component).toBeTruthy();
});
it('should show tab labels and icons', function () {
let tabGroup = compiled.querySelector('mat-tab-group');
let tabs: any[] = compiled.querySelectorAll('[role="tab"]');
expect(component).toBeTruthy();
expect(tabGroup).toBeTruthy();
expect(tabs.length).toBe(Object.keys(tabLabelIconPairs).length);
// Check labels and icons for each tab
tabs.forEach((tab: any) => {
const label: string = tab.querySelector('mat-label').innerText;
const icon: string = tab.querySelector('mat-icon').innerText;
expect(label in tabLabelIconPairs).toBeTruthy();
expect(tabLabelIconPairs[label]).toBe(icon)
});
});
});
I think it's with isWeb | async
. By the looks of it, the *ngIf
's are the only ones that could affect them being rendered. So maybe you should first wait for the async stuff to finish before querying them. Deploying --prod
and on server could increase query times, so on local it may query faster and tests pass.