Angular 6 个用于验证按钮禁用的单元测试用例在使用禁用但未禁用 querySelector 属性时失败

Angular 6 unit test case to validate button disable fails when using disable but not disabled querySelector attribute

我是 Angular 的新手,正在尝试使用 Jasmine 编写 2 个单元测试用例。一项测试何时启用按钮,一项测试何时禁用按钮。
这是要求....当用户在文本框中输入有效数据时,该按钮被启用。如果用户在文本框中输入无效数据,该按钮将被禁用。初始值是禁用的。 这是文本框和按钮 html:

<input
      type="text"
      matInput
      formControlName = 'triplegOriginControl'
      [(ngModel)]="leg.originId"
      [matAutocomplete]="autoTripLegOrigin"
      class = 'trip-origin-input'
      (keydown.Tab)="onKey($event)"
  />

  <mat-autocomplete autoActiveFirstOption #autoTripLegOrigin="matAutocomplete">
    <mat-option
      *ngFor="let option of triplegOriginOptions | async"
      [value]="option"
    >
      {{ option }}
    </mat-option>
  </mat-autocomplete>
  <mat-error *ngIf="triplegForm.controls['triplegOriginControl'].hasError('invalid')">
    Please select a valid location
</mat-error>
</mat-form-field>
....
<mat-toolbar color="primary">
<button
  mat-raised-button
  color="primary"
  id="mat-toolbar__get-rate-button-id"
  [disabled] = !triplegForm.valid
>
  GET RATE
</button>
</mat-toolbar>

我们有一个自定义验证器来验证文本框输入是否在位置列表中。 这是使用验证函数的构造函数:

"triplegOriginControl": new FormControl(null, [ ValidateLocation(this.fetchDataService.locationArray)])

这是 2 个单元测试。

describe('Enable or Disable Get Rates Button', () => {
     beforeEach(() => {
       fetchDataService.locationArray = ["STOC/4958", "NSGR/4143", "ZCRC/416", "NSGR/4143"];
     });

    it('should enable the button when Origin and Destination are valid', (done) => {
      const compiled = fixture.debugElement.nativeElement;
      const originIdInput = compiled.querySelector(
        ".trip-origin-input"
      );
      originIdInput.value = "STOC/4958";
      originIdInput.dispatchEvent(new Event("input"));

      const destinationIdInput = compiled.querySelector(
        ".trip-destination-input"
      );
      destinationIdInput.value = "NSGR/4143";
      destinationIdInput.dispatchEvent(new Event("input"));
      fixture.detectChanges();

      fixture.whenStable().then(() => {
        expect(fixture.componentInstance.leg.originId).toBe('STOC/4958');
        expect(fixture.componentInstance.leg.destinationId).toBe('NSGR/4143');
        expect(compiled.querySelector('#mat-toolbar__get-rate-button-id').disable).toBeFalsy();
        done();
      });
    });

    it('should disable the button when Origin and Destination are invalid', (done) => {
      const compiled = fixture.debugElement.nativeElement;
      const originIdInput = compiled.querySelector(
        ".trip-origin-input"
      );
      originIdInput.value = "546";
      originIdInput.dispatchEvent(new Event("input"));

      const destinationIdInput = compiled.querySelector(
        ".trip-destination-input"
      );
      destinationIdInput.value = "NSGxx/43";
      destinationIdInput.dispatchEvent(new Event("input"));
      fixture.detectChanges();

      fixture.whenStable().then(() => {
        expect(fixture.componentInstance.leg.originId).toBe('546');
        expect(fixture.componentInstance.leg.destinationId).toBe('NSGxx/43');
        expect(compiled.querySelector('#mat-toolbar__get-rate-button-id').disable).toBeTruthy();
        done();
      });
    });
  });

使用时:

expect(compiled.querySelector('#mat-toolbar__get-rate-button-id').disable)

第一个测试(有效测试)失败。 错误:预期为假。 当使用属性disabled时,有效测试通过。

使用时

expect(compiled.querySelector('#mat-toolbar__get-rate-button-id').disabled)

第二个测试(无效测试)失败。 ** 错误:预期未定义是真实的。** 当使用属性disable时,无效测试通过

属性 disabledisabled 有什么区别? 该代码按预期工作。当用户输入无效值时,该按钮将被禁用。当输入有效值时,该按钮被启用。 如何纠正这些测试用例

我认为 .disable 使用 JavaScript/HTML 禁用输入,我不确定 .disabled 的作用。我会使用属性选择器检查属性。

https://www.w3schools.com/css/css_attribute_selectors.asp

1.) compiled 仍然是引用之前的 DOM/HTML 变化和变化检测,每次有变化都需要一个新的引用。

2.) 使用 [disabled] 属性 CSS 选择器并查看它何时存在或不存在。

试试这个,我已经添加了评论:

    it('should enable the button when Origin and Destination are valid', (done) => {
      const compiled = fixture.debugElement.nativeElement;
      const originIdInput = compiled.querySelector(
        ".trip-origin-input"
      );
      originIdInput.value = "STOC/4958";
      originIdInput.dispatchEvent(new Event("input"));

      const destinationIdInput = compiled.querySelector(
        ".trip-destination-input"
      );
      destinationIdInput.value = "NSGR/4143";
      destinationIdInput.dispatchEvent(new Event("input"));
      fixture.detectChanges();

      fixture.whenStable().then(() => {
        expect(fixture.componentInstance.leg.originId).toBe('STOC/4958');
        expect(fixture.componentInstance.leg.destinationId).toBe('NSGR/4143');
        // since you did fixture.detectChanges, you need a new reference to the dom
        // compiled is still pointing to the previous instance.
        const newCompiled = fixture.debugElement.nativeElement;
        // attach the attribute selector of [disabled] and expect it to not be there
        console.log(!component.triplegForm.valid); // ensure you see false here
        expect(newCompiled.querySelector('#mat-toolbar__get-rate-button-id[disabled]')).toBeFalsy();
        done();
      });
    });

    it('should disable the button when Origin and Destination are invalid', (done) => {
      const compiled = fixture.debugElement.nativeElement;
      const originIdInput = compiled.querySelector(
        ".trip-origin-input"
      );
      originIdInput.value = "546";
      originIdInput.dispatchEvent(new Event("input"));

      const destinationIdInput = compiled.querySelector(
        ".trip-destination-input"
      );
      destinationIdInput.value = "NSGxx/43";
      destinationIdInput.dispatchEvent(new Event("input"));
      fixture.detectChanges();

      fixture.whenStable().then(() => {
        expect(fixture.componentInstance.leg.originId).toBe('546');
        expect(fixture.componentInstance.leg.destinationId).toBe('NSGxx/43');
        // same logic here
        console.log(!component.triplegForm.valid); // ensure you see true here
        const newCompiled = fixture.debugElement.nativeElement;
        expect(newCompiled.querySelector('#mat-toolbar__get-rate-button-id[disabled]')).toBeTruthy();
        done();
      });
    });