Карма с этой ошибкой TypeError: Невозможно прочитать свойство 'textContent' из неопределенного - PullRequest
1 голос
/ 28 марта 2019

Я пишу очень простой модульный тест.Я не знаю, что с этим делать.Я пробовал так много вещей, но не могу решить эту ошибку.Кроме первого теста другой тест не пройден.

Spec file

fdescribe("New TestOrder ICD10 Code Selection Modal Component", () => {
  let component: NewTestOrderICD10CodeSelectionModalComponent;
  let fixture: ComponentFixture<NewTestOrderICD10CodeSelectionModalComponent>;
  let de: DebugElement;
  let element: HTMLElement;

  beforeEach(async (() => {
    TestBed.configureTestingModule({
      declarations: [
        NewTestOrderICD10CodeSelectionModalComponent
      ],
      imports: [
        ReactiveFormsModule,
        NgbModule.forRoot(),
        FormsModule,
        RouterTestingModule,
        StoreModule.forRoot({}),
        HttpModule
      ],
      providers: [
        AuthService,
        UtilService,
        SessionService,
        TestOrderService,
        {provide: APP_BASE_HREF, useValue : '/'}
      ],
      schemas: [
        NO_ERRORS_SCHEMA
      ]
    }).compileComponents();
  }));

  beforeEach(async() => {
    fixture = TestBed.createComponent(NewTestOrderICD10CodeSelectionModalComponent);
    component = fixture.debugElement.componentInstance;
    de = fixture.debugElement.query(By.css('.new-test-order-icd10-code-selection-modal.component'));
    element  = de.nativeElement;
    fixture.detectChanges();
  });

  it("New Test Order ICD10 Coed Selection Modal Should be created", () => {
    expect(component).toBeTruthy();
  });

  it('Should have a title', () => {
    expect(element.textContent).toContain(component.title);
  });
});

Вот файл mew-test-order-icd10-code-selection-modal.component.ts

// Angular DOM
import {Component, ViewChild, TemplateRef, OnDestroy} from "@angular/core";
// Angular Hooks
import {OnInit, AfterViewInit} from "@angular/core";
// Redux Store
import {Store} from "@ngrx/store";
import {SetTestOrderCodeSelectionSession} from "../../../../shared";
// Models
import {
  TestOrder,
  TestOrderCodeSelectionSession
} from "../../../../shared/models";
// Third Party Services
import {NgbModal, NgbModalOptions} from "@ng-bootstrap/ng-bootstrap";
import {ActivatedRoute, Router} from "@angular/router";
import * as lodash from "lodash";
// Services
import {TestOrderService} from "../../../../shared";
import {SetTestOrderICDCodes} from "../../../../shared/actions/test-order.actions";
import {Subscription} from "rxjs/Subscription";

@Component({
  selector: "app-new-test-order-icd10-code-selection-modal",
  templateUrl: "./new-test-order-icd10-code-selection-modal.component.html",
  styleUrls: ["./new-test-order-icd10-code-selection-modal.component.scss"]
})
export class NewTestOrderICD10CodeSelectionModalComponent
  implements OnInit, AfterViewInit, OnDestroy {
  @ViewChild("icd10codes") template: TemplateRef<any>;
  public codeSelectionSession: TestOrderCodeSelectionSession;
  public icdCodes: Array<ICDCode>;
  public newIcdCodesArray = [];
  public modalRef: any;
  title: string = "Please Select ICD-10 Codes";
  public serviceSubscription: Subscription;
  private selectedCodes: Array<ICDCode> = [];
  private options: NgbModalOptions = {
    backdrop: "static",
    windowClass: "icd10-codes",
    container: "app-new-test-order-icd10-code-selection-modal",
    keyboard: false,
    size: "lg"
  };

  constructor(
    private modalService: NgbModal,
    private testOrderService: TestOrderService,
    public router: Router,
    private route: ActivatedRoute,
    private store: Store<any>
  ) {
  }

  ngOnInit() {
    this.serviceSubscription = this.testOrderService.getICDCodes().subscribe((codes: Array<ICDCode>) => {
      this.icdCodes = codes;
    });
    this.store
      .select("codeSelectionSession")
      .subscribe((session: TestOrderCodeSelectionSession) => {
        // checks for null and undefined
        if (session == null) {
          this.store.select("testOrder").subscribe((testOrder: TestOrder) => {
            this.codeSelectionSession = new TestOrderCodeSelectionSession();
            this.store.dispatch(
              new SetTestOrderCodeSelectionSession(this.codeSelectionSession)
            );
          });
        } else {
          this.codeSelectionSession = session;
        }
      });
  }

  ngAfterViewInit() {
    setTimeout(() => {
      this.modalRef = this.modalService.open(this.template, this.options);
    });
    this.serviceSubscription = this.route.queryParams.subscribe(params => {
      //for selected icd
      if(params.selectedIcdCodes) {
        this.selectedCodes = JSON.parse(params.selectedIcdCodes);
      }
      this.codeSelectionSession.SelectedCodes = lodash.concat(this.codeSelectionSession.SelectedCodes, this.selectedCodes);
      //for icd id
      this.codeSelectionSession.ICDCodeIds = lodash.concat(this.codeSelectionSession.ICDCodeIds, params.icd10Codes);
      this.newIcdCodesArray = lodash.concat(this.newIcdCodesArray, params.icd10Codes);

      this.codeSelectionSession.ICDCodeIds = lodash.uniq(this.codeSelectionSession.ICDCodeIds);

      let difference = lodash.difference(this.codeSelectionSession.ICDCodeIds, this.newIcdCodesArray);
      this.codeSelectionSession.ICDCodeIds = lodash.differenceBy(this.codeSelectionSession.ICDCodeIds, difference);  //remove the difference
    });
  }

  onCheckboxUpdate(selected: boolean, icdCode: ICDCode) {
    this.codeSelectionSession.ICDCodeIds = this.codeSelectionSession.ICDCodeIds.filter(
      codeId => codeId !== icdCode.Id
    );
    this.codeSelectionSession.SelectedCodes = this.codeSelectionSession.SelectedCodes.filter(
      code => code.Id !== icdCode.Id
    );
    if (selected) {
      this.codeSelectionSession.ICDCodeIds.push(icdCode.Id);
      this.codeSelectionSession.SelectedCodes.push(icdCode);
    }
  }

  ngOnDestroy() {
    setTimeout(() => {
      this.modalRef.close();
    });
    this.serviceSubscription.unsubscribe();
  }
}

Второй тест долженпройти также.Я не знаю, что я делаю здесь неправильно.

Другой метод, который я попробовал.Это мой specfile, он показывает, что мне не удается прочитать свойство 'nativeElement' со значением null

beforeEach(async() => {
    fixture = TestBed.createComponent(NewTestOrderICD10CodeSelectionModalComponent);
    component = fixture.debugElement.componentInstance;
//    de = fixture.debugElement.query(By.css('.h5')).nativeElement.innerText;
//    element  = de.nativeElement;
    fixture.detectChanges();
  });

  it("New Test Order ICD10 Coed Selection Modal Should be created", () => {
    expect(component).toBeTruthy();
  });

  it('Should have a title', () => {
    //expect(element.textContent).toContain(component.title);
    fixture.detectChanges();
    const de = fixture.debugElement.query(By.css('.modal-title')).nativeElement.innerText;
    expect(de).toContain(component.title);
  });

Это мой HTML-файл

<ng-template #icd10codes let-c="close" let-d="dismiss">
  <form role="form" #icdCodeSelectionForm="ngForm" novalidate>
    <div class="modal-header">
      <span class="material-icons clickable text-dimmed" (click)="onBackButtonClick()" [routerLink]="['/test-order/create/details']">arrow_back</span>
      <h5 class="modal-title">{{title}}</h5>
      <button type="button" class="btn square-btn" (click)="update()">ADD CODES</button>
      <div class="icd-search">
        <app-search-list (onSearchInputUpdate)="onSearchUpdate($event)"></app-search-list>
      </div>
    </div>
    <div class="modal-body">
      <div class="row">
        <div class="col-3 text-dimmed pad-left-45">ICD-10 Codes</div>
        <div class="col-8 text-dimmed">Description</div>
        <div class="col-1"></div>
      </div>
      <div class="list-border"></div>
      <div class="code-list">
        <div class="row" *ngFor="let icdCode of icdCodes; let odd = odd" [ngClass]="{'isOdd': odd}">
          <div class="col-3 pad-left-45">{{ icdCode.Code }}</div>
          <div class="col-8 text-dimmed">{{ icdCode.ShortDescription }}</div>
          <div class="col-1">
            <label class="custom-control custom-checkbox">
                <input
                  type="checkbox"
                  class="custom-control-input"
                  [ngModel]="codeSelectionSession.ICDCodeIds.indexOf(icdCode.Id) !== -1"
                  (ngModelChange)="onCheckboxUpdate($event, icdCode)"
                  [name]="icdCode.Id">
                <span class="custom-control-indicator"></span>
            </label>
          </div>
        </div>
      </div>
    </div>
  </form>
</ng-template>

Ответы [ 2 ]

0 голосов
/ 28 марта 2019

Я предлагаю удалить

  de = fixture.debugElement.query(By.css('.new-test-order-icd10-code-selection-modal.component'));

из блока beforeEach.Обратите внимание, что вы пытаетесь получить это значение до fixture.detectChanges();, поэтому я думаю, что это не очень хороший подход.Вы должны убедиться, что вы получите значения HTML после того, как все угловые изменения завершены.Попробуйте:

it('Should have a title', () => {
  fixture.detectChanges(); // No need to add this line if you have it in "beforeEach" block
  const de = fixture.debugElement.query(By.css('.new-test-order-icd10-code-selection-modal.component'));
  const element  = de.nativeElement;
  expect(element.textContent).toContain(component.title);
});

Обновление

Для тестирования ng-template вам придется сделать это по-другому:

app.component.html

<div id="title">
    {{title}}
</div>
<ng-template #content
             let-modal
             id="ng-modal">
  <div class="modal-header dark-modal">
    Header
  </div>
  <div class="justify-content-center flex-column flex-md-row list-inline">
    Body
  </div>
</ng-template>

app.component.ts

import { Component, ViewChild, TemplateRef } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'AngularProj';
  @ViewChild('content') modalRef: TemplateRef<any>;
}

Вам нужно записать spec файл немного другим способом:

app.component.spec.ts

import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { ViewChild, Component, OnInit, AfterContentInit, TemplateRef } from '@angular/core';
import { By } from '@angular/platform-browser';

@Component({
  template: `
    <ng-container *ngTemplateOutlet="modal"> </ng-container>
    <app-root></app-root>
  `,
})
class WrapperComponent implements AfterContentInit {
  @ViewChild(AppComponent) appComponentRef: AppComponent;
  modal: TemplateRef<any>;
  ngAfterContentInit() {
    this.modal = this.appComponentRef.modalRef;
  }
}

describe('AppComponent', () => {
  let app: AppComponent;
  let fixture: ComponentFixture<WrapperComponent>;
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [WrapperComponent, AppComponent],
    }).compileComponents();
  }));
  beforeEach(() => {
    fixture = TestBed.createComponent(WrapperComponent);
    const wrapperComponent = fixture.debugElement.componentInstance;
    app = wrapperComponent.appComponentRef;
    fixture.detectChanges();
  });
  it('should create the app', async(() => {
    expect(app).toBeDefined();
  }));
  it('should have title in HtmL ', async(() => {
    const titleText = (fixture.debugElement.nativeElement.querySelector('#title').innerText);
    expect(titleText).toBe('AngularProj');
  }));
  it('should have Header in HtmL ', async(() => {
    const headerText = (fixture.debugElement.queryAll(By.css('.modal-header.dark-modal'))[0].nativeElement.innerText);
    expect(headerText).toBe('Header');
  }));
});

  1. Как видите, я обернул app-root образцом тестового компонента (WrapperComponent).
  2. С тех пор app-root имеет ng-template, поэтому он не будет отображаться сам по себе.Это создает сложную ситуацию, так как нам нужно визуализировать эту часть app.component.
  3. Expose ng-template, создав @ViewChild('content') modalRef: TemplateRef<any>; и затем используя его для рендеринга внутри WrapperComponent.

Я знаю, что это похоже на взлом, но по всем статьям, которые я прочитал, вот как мы можем этого достичь.

0 голосов
/ 28 марта 2019

Как вы делаете element = de.nativeElement; в beforeEach(), так что element содержит значение.
И это можно сделать через

it('Should have a title', () => {
    expect(element).toContain(component.title);
});

Начиная с de = fixture.debugElement.query(By......, de будет HTMLNode, и использование nativeElement позволит вам получить содержимое. Или вы можете напрямую использовать de.textContent для получения содержимого элемента de.

Ссылка Alligator.io для тестирования модулей

...