Модульный тестовый пример Angular 6 для проверки отключения кнопки завершается неудачей при использовании атрибута disable, но не отключенного querySelector

#angular #typescript #unit-testing #jasmine

#angular #typescript #модульное тестирование #jasmine

Вопрос:

Я новичок в Angular и пытаюсь написать 2 модульных теста с использованием Jasmine. Один тест, когда кнопка включена, и один тест, когда кнопка отключена.
Это требование….когда пользователь вводит допустимые данные в текстовое поле, кнопка активируется. Если пользователь вводит неверные данные в текстовое поле, кнопка отключается. Начальное значение отключено. Это текстовое поле и кнопка 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)
  

первый тест (допустимый тест) завершается с ошибкой.
ОШИБКА: ожидаемое значение true равно false.
При использовании атрибута disabled выполняется допустимый тест.

При использовании

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

второй тест (недопустимый тест) завершается с ошибкой.
** Ошибка: ожидается, что значение undefined соответствует действительности.**
При использовании атрибута disable выполняется недопустимый тест.

В чем разница между атрибутом disable и disabled? Код работает так, как ожидалось. Когда пользователь вводит недопустимое значение, кнопка отключается. При вводе допустимого значения кнопка включена. Как исправить эти тестовые примеры

Ответ №1:

Я думаю, что .disable отключает ввод с использованием JavaScript / HTML, и я не уверен, что .disabled делает. Я бы проверил атрибут с помощью селектора атрибутов.

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

1.) compiled по-прежнему ссылается на DOM / HTML до внесения изменений и обнаружения изменений, вам нужна новая ссылка каждый раз, когда происходят изменения.

2.) Используйте [disabled] атрибут CSS selector и посмотрите, когда он присутствует, а когда нет.

Попробуйте это, я добавил комментарии:

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

Комментарии:

1. Попробовал ваше предложение, и оно завершается ошибкой с другой ошибкой: Ошибка: Ожидаемый <button ngcontent-a-c5 color=»primary» id=»mat-toolbar _get-rate-button-id» mat-raised-button отключен> …</button> чтобы быть ложным.

2. Поиграл и обнаружил, что для действительного теста он пройдет, если я использую это в качестве селектора: #mat-toolbar__get-rate-button-id отключен Но недопустимый тест завершится неудачей, если я использую этот селектор. Если я использую этот селектор #mat-toolbar__get-rate-button-id[отключен] , недопустимый случай проходит

3. Это ложноположительный результат, он проходит, потому что его там нет. Я уверен в селекторе CSS, возможно, есть тип, но [] (жесткие скобки) необходимы для селекторов CSS. Проверьте консоль. журналы !component.triplegForm.valid, которые я добавил, и убедитесь, что вы видите точные значения. Если значения точны, возможно, вам нужен другой, fixture.detectChanges() после fixture.whenStable() внутри обратного вызова then.

4. Component.triplegForm.valid имеет значение false в обоих случаях. Допустимый флаг использует пользовательский валидатор. Этот валидатор отправляется в службу для получения действительных местоположений. Служба инициализируется: ** beforeEach(() => { fetchDataService. locationArray = [«UPPH/4097», «CACN/4030», «ZCRC/416», «NSGR /4143», «ZCRC/471», «REPH/4987»]; });**

5. Понял это … в массиве местоположений не было введенных мной местоположений. Таким образом, случай был корректно недействительным, поскольку местоположения не были в массиве. БОЛЬШОЕ СПАСИБО ЗА ВАШУ ПОМОЩЬ!