繁体   English   中英

在 GitHub 中运行 Angular 测试 读取内部文本时操作失败(在本地通过)

[英]Running Angular tests in GitHub Actions fails when reading inner text (passes locally)

我有一个 GitHub 操作工作流,它为 Angular 项目运行 Karma。 当测试尝试读取本机元素的“innerText”时。 抛出错误“TypeError: Cannot read properties of null (reading 'innerText')”,我认为这是因为查询选择器无法找到元素。 但是,测试在本地通过。 此外,还有其他在虚拟环境中传递的查询选择器测试。

当地的: 在此处输入图像描述

行动: 在此处输入图像描述

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>

组件代码:

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 {
  }

}

测试代码:


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)
    });
  });
});

我认为是与isWeb | async isWeb | async 从外观上看, *ngIf是唯一可能影响它们被渲染的。 因此,也许您应该先等待异步内容完成,然后再查询它们。 在服务器上部署--prod可能会增加查询时间,因此在本地它可能会更快地查询并通过测试。

正如Joosep Parts所指出的,问题在于元素渲染方式的异步性。 这是通过创建一个BreakpointObserver螺柱并对其进行监视来解决的。 当调用 observe 方法时,调用了fixture.detectChange()并且测试通过了。 请注意,这被包装在一个fixture.wheStable()块中

观察者螺柱:

export class BreakpointObserverStub {
  observe(): BreakpointState {
    return {
      matches: true,
      breakpoints: {
        "web": true
      }
    } as BreakpointState
  }
}

测试:

it('Should show the correct tab labels and icons in Web View', waitForAsync(() => {
    fixture.whenStable().then(() => {
      spyOn(observer, 'observe').and.returnValue(of({
        matches: true,
        breakpoints: {
          "web": true
        }
      } as BreakpointState));

      // Wait for changes and confirm observe was called
      fixture.detectChanges();
      expect(observer.observe).toHaveBeenCalled();

      // Check correct labels and icons
      tabs.forEach((tab: any) => {
        const label: string = tab.querySelector('mat-label').innerText;
        const icon: string = tab.querySelector('mat-icon').innerText;

        // Check labels and icons for each tab
        expect(label in tabLabelIconPairs).toBeTruthy();
        expect(tabLabelIconPairs[label]).toBe(icon)
      });
    });
  }));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM