Директива @Input всегда не определена в тесте - PullRequest
0 голосов
/ 25 октября 2019

У меня есть директива для копирования в буфер обмена. Когда я использую его как обычно, он работает, однако, когда я хочу выполнить некоторые тесты, свойство @Input всегда неопределено

Вот директива CopyClipboard.directive.ts

import { Directive, Input, Output, EventEmitter, HostListener } from '@angular/core';


/**
 * Directive use to copy the content of the event to the clipBoard
 * Source :
 * https://stackoverflow.com/a/52949299/5703228
 *
 * @export
 * @class CopyClipboardDirective
 */
@Directive({
  selector: '[appCopyClipboard]'
})
export class CopyClipboardDirective {

  constructor() {}

  @Input('appCopyClipboard')
  public payload: string;

  @Output() public copied: EventEmitter<string> = new EventEmitter<string>();

  @HostListener('click', ['$event'])
  public onClick(event: MouseEvent): void {

    event.preventDefault();
    if (this.payload == null) {
      return;
    }

    const listener = (e: ClipboardEvent) => {
      const clipboard = e.clipboardData || window['clipboardData'];
      clipboard.setData('text', this.payload.toString());
      e.preventDefault();

      this.copied.emit(this.payload);
    };

    document.addEventListener('copy', listener, false);
    document.execCommand('copy');
    document.removeEventListener('copy', listener, false);
  }
}

А вот соответствующий тест Copyclipboard.directive.spec.ts

/* tslint:disable:no-unused-variable */

import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { CopyClipboardDirective } from './CopyClipboard.directive';
import { Component, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { TestUtils } from 'src/app/tests/test-utils';

@Component({
  template: '<button [appCopyClipboard]="getTextToCopy()" (copied)="onCopied($event)">Test button</button>'
})
class TestCopyClipboardDirectiveComponent {

  public textToCopy = 'Test Text';
  public textCopied = '';

  public onCopied(textCopied: string): void {
    this.textCopied = textCopied;
  }

  public getTextToCopy(): string {
    return this.textToCopy;
  }
}

describe('Directive: CopyClipboardDirective', () => {

  let component: TestCopyClipboardDirectiveComponent;
  let fixture: ComponentFixture<TestCopyClipboardDirectiveComponent>;
  let buttonDe: DebugElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        CopyClipboardDirective,
        TestCopyClipboardDirectiveComponent
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TestCopyClipboardDirectiveComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();

    buttonDe = fixture.debugElement.query(By.css('button'));
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
    expect(buttonDe).toBeTruthy();
  });

  it('should call callback after copy on click', () => {

    spyOn(component, 'onCopied').and.callThrough();
    const buttonNe: HTMLElement = buttonDe.nativeElement;
    buttonNe.click();
    fixture.detectChanges();

    fixture.whenStable().then(() => {
      expect(component.onCopied).toHaveBeenCalledTimes(1);
      expect(component.onCopied).toHaveBeenCalledWith(component.textToCopy);
      expect(component.textCopied).toBe(component.textToCopy);
    });
  });

  // We cannot access content of ClipBoard as it is a security violation
  // So we cannot test if the data is actually in the clipBoard
  // But we can try to listen for event copy
  it('should emit copy event on document', (done: DoneFn) => {

    spyOn(component, 'onCopied').and.callThrough();
    let success = false;
    const listener = (e: ClipboardEvent) => {

      success = true;
      done();
      document.removeEventListener('copy', listener, false);
    };

    document.addEventListener('copy', listener, false);
    const buttonNe: HTMLElement = buttonDe.nativeElement;
    buttonNe.click();
    fixture.detectChanges();
    /*fixture.whenStable().then(() => {
      expect(success).toBe(true);
    });*/
  });
});

оба последних теста не пройдены. Когда я отлаживаю выполнение, я вижу, что payload в директиве не определено. Почему директива @Input не определена в тесте?

Я использую ее в компоненте, и она отлично работает:

<button mat-raised-button color="secondary"
  [appCopyClipboard]="getStringFormattedQuotation()"
  (copied)="notifyCopied($event)">
    <mat-icon>file_copy</mat-icon> Copier
</button>

** Редактировать ** Я пыталсякак предложено изменить шаблон тестовых компонентов следующим образом:

...[appCopyClipboard]=" \'someText string here\' "...

После этого я получаю @Input, установленный на это значение, однако в коде директивы набор слушателей в документе по-прежнему никогда не вызывается. Я попытался удалить строку

document.removeEventListener('copy', listener, false);

для проверки, но она тоже не сработала. Еще одна вещь, которую я заметил, в консоли браузера, я получаю 2 строки этого:

zone.js:3401 Access to XMLHttpRequest at 'ng:///DynamicTestModule/TestCopyClipboardDirectiveComponent.ngfactory.js' from origin 'http://localhost:9876' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.

Я не получаю эту ошибку ни для одного из моих других тестов.

...