【问题标题】:Angular: How to test that clicking on a component nativeElement opens a dialogAngular:如何测试单击组件 nativeElement 会打开一个对话框
【发布时间】:2021-06-26 13:18:20
【问题描述】:

我正在为呈现mat-accordionmat-expansion-panel 的组件编写测试。 mat-expansion-panel 有一个 div(类 .comment),它具有使用 [innerHtml] 的动态 html。有时innerHtml 可能包含<img /> 标签。当用户点击这样的div 时,应该会弹出一个对话框,其中包含该图像(但尺寸更大)。

在我的测试中,我想断言点击img 应该会打开这个对话框,但是,问题是当我检查fixture.nativeElement 时,我确实没有看到任何对话框元素。

组件如下:

@Component({
    selector: 'app-node-history',
    templateUrl: './node-history.component.html',
    styleUrls: ['./node-history.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class NodeHistoryComponent {
    readonly data$: Observable<HistoryNode[]>;

    constructor(private readonly resultService: ResultService, private readonly route: ActivatedRoute, public dialog: MatDialog) {
        this.data$ = route.paramMap.pipe(
            map(params => Number.parseInt(params.get('id')!, 10)),
            filter(value => !!value),
            switchMap(id => resultService.getResultHistoryForNode(id))
        );
    }

    onCommentClick(event: MouseEvent) {
        if ((event.target as HTMLElement)?.tagName === 'IMG') {
            this.dialog.open(ImageDialogComponent, {
                data: (event.target as HTMLElement).outerHTML,
                maxHeight: '80vh'
            });
        }
    }
}

spec 文件是:

@Component({
  template: '<router-outlet></router-outlet>'
})
class TestRootComponent {}
fdescribe('NodeHistoryComponent', () => {
  function advance() {
      tick();
      rootFixture.detectChanges();
  }

  function navigateByNodeId(id: number) {
      rootFixture.ngZone?.run(() => router.navigate(['history', 'node', id]));
  }

  let component: TestRootComponent;
  let rootFixture: ComponentFixture<TestRootComponent>;
  let router: Router;
  let loader: HarnessLoader;
  let httpTestingController: HttpTestingController;

  afterEach(() => {
      httpTestingController.verify();
  });
  beforeEach(async () => {
      const fakeService = {
          getResultHistoryForNode(id: number) {
              console.log('Came here', id);
              return of(mockResult);
          }
      } as Partial<ResultService>;

      await TestBed.configureTestingModule({
          declarations: [TestRootComponent, NodeHistoryComponent, ImageDialogComponent, DatePipe, SafePipe],
          imports: [
              MatDialogModule,
              MatExpansionModule,
              NoopAnimationsModule,
              HttpClientTestingModule,
              RouterTestingModule.withRoutes([
                  {
                      path: 'history/node/:id',
                      component: NodeHistoryComponent
                  }
              ])
          ],
          providers: [
              {
                  provide: ResultService,
                  useValue: fakeService
              }
          ],
          schemas: [NO_ERRORS_SCHEMA]
      }).compileComponents();

      rootFixture = TestBed.createComponent(TestRootComponent);
      component = rootFixture.componentInstance;
      router = TestBed.inject(Router);
      rootFixture.detectChanges();
      loader = TestbedHarnessEnvironment.loader(rootFixture);
      httpTestingController = TestBed.inject(HttpTestingController);
  });

  it('should render img tags when comment has images', fakeAsync(async () => {
      navigateByNodeId(123);
      advance();
      const panel = await loader.getAllHarnesses(MatExpansionPanelHarness);
      expect(panel.length).toEqual(mockResult.length);
      const text = await (await panel[1].host()).getCssValue;

      const imageInComment = rootFixture.nativeElement.querySelector('.comment img') as HTMLImageElement;
    expect(imageInComment).toBeDefined();

    imageInComment.click();
    advance();
    /* The next assertion fails with the following error message:

         Error: Failed to find element matching one of the following queries:
        (MatDialogHarness with host element matching selector: ".mat-dialog-container")
    */
    const dialog = await loader.getHarness(MatDialogHarness);
    expect(dialog).toBeDefined();
  }));
});

从规范文件中可能很清楚,有一个额外的复杂性,因为该组件依赖于 ActivatedRoute(这就是我需要TestRootComponent 的原因)。

非常感谢任何帮助:)

【问题讨论】:

    标签: angular angular-material jasmine karma-jasmine angular9


    【解决方案1】:

    尝试在“then”功能中编写您的期望。像这样的:

    fixture.detectChanges();
    
    fixture.whenStable().then(() => {
      expect(dialog).toBeDefined();
    );
    

    【讨论】:

    • 谢谢!我尝试了这个建议,但没有奏效。它失败并出现错误Error: Failed to find element matching one of the following queries: (MatDialogHarness with host element matching selector: ".mat-dialog-container") 它可能与叠加层中呈现的对话框有关吗?
    • 是的。它可能是。
    【解决方案2】:

    OverlayContainer 中有一个用于对话框的 documentRootLoader 线束加载器。

    documentRootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);

    https://stackblitz.com/edit/angular-harness-dialogs-3f7zju?file=src%2Fapp%2Fapp.component.spec.ts

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-09
      • 2022-01-20
      • 2018-10-01
      • 1970-01-01
      • 2011-06-18
      相关资源
      最近更新 更多