如何触发来自Angular规范的输入onchange

时间:2019-01-15 15:44:10

标签: angular angular6 angular-test angular-testing

我提交了input HTML File

<input type="file" class="custom-file-input" id="question-file-upload" formControlName="image" (change)="handleFileSelect($event)">

我想对handleFileSelect函数进行单元测试。但是我不知道如何触发输入的onChange方法。以下是我写的spec,但出现错误imageInputNE.onchange is not a function

fit('should keep track of image counter when an image is loaded', () => {
    let newPracticeQuestionComponent = component;
    expect(newPracticeQuestionComponent.currentImageAttachmentCount).toBe(0);
    let imageInputDE = fixture.debugElement.query(By.css("#question-file-upload"));
    expect(imageInputDE).toBeTruthy();
    spyOn(newPracticeQuestionComponent,'handleFileSelect');

    let imageInputNE:HTMLElement = imageInputDE.nativeElement as HTMLElement;
    imageInputNE.onchange(new Event("some event"));
    expect(newPracticeQuestionComponent.handleFileSelect).toHaveBeenCalled();


  });

2 个答案:

答案 0 :(得分:0)

请参阅Mozilla网站上的文档,事件通常由“外部”源(如Buttons)触发,也可以通过编程方式触发,例如通过调用元素的HTMLElement.click()方法。或通过定义事件,然后使用EventTarget.dispatchEvent()将其发送到指定的目标。

我认为click是一个简单的事件,可以通过编程方式触发,但是change的{​​{1}}事件更复杂,因为它需要选择一个文件,转换为博客,更新{ {1}}属性等。因此可能由于这个原因,我不能简单地调用<input type=file..>

所以我想到了使用files,它似乎可行

1)我明确创建了一个事件。我想imageElementNE.change()的构造函数有2个参数,第2个是可选的。

来自https://developer.mozilla.org/en-US/docs/Web/API/Event/Event

dispatchEvent

根据文档,Event是代表事件名称的DOMString。换句话说,第一个参数似乎是事件的event = new Event(typeArg, eventInit); 。当我要发送typeArg事件时,我需要将其称为type

对于change部分,我查看了https://developer.mozilla.org/en-US/docs/Web/Events/changechange事件的定义,并从那里选择了eventInitchange的值

bubbles

这将创建一个cancelable,其外观如下(我必须承认我不太了解它)

let fileSelectEvent = new Event("change",{bubbles:true,
    cancelable: false});

2)创建Event之后,我在 Event {isTrusted: false, type: "change", target: input#question-file-upload.custom-file-input.ng-untouched.ng-pristine.ng-valid, currentTarget: input#question-file-upload.custom-file-input.ng-untouched.ng-pristine.ng-valid, eventPhase: 2, …}bubbles: truecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: nulldefaultPrevented: falseeventPhase: 0isTrusted: falsepath: (10) [input#question-file-upload.custom-file-input.ng-untouched.ng-pristine.ng-valid, div#file-upload.custom-file, div.form-group, form#new-question-form.practice-question-form.ng-untouched.ng-pristine.ng-invalid, div#form-div.body__div--background, div#root0, body, html, document, Window]returnValue: truesrcElement: input#question-file-upload.custom-file-input.ng-untouched.ng-pristine.ng-validtarget: input#question-file-upload.custom-file-input.ng-untouched.ng-pristine.ng-validtimeStamp: 3759.4000000026426type: "change"__proto__: Event 上调用了Event方法。与dispatchEvent相比,我认为这意味着imageInputNE正在触发事件(在我的情况下EventTarget.dispatchEvent()正在触发EventTarget

imageInputNE

通过的整个规格是

fileSelectEvent

以上内容可能不是最好的解决方案,因为我无法在imageInputNE.dispatchEvent(fileSelectEvent); 字段中操作 fit('should keep track of image counter when an image is loaded', () => { let newPracticeQuestionComponent = component; expect(newPracticeQuestionComponent.currentImageAttachmentCount).toBe(0); let imageInputDE = fixture.debugElement.query(By.css("#question-file-upload")); expect(imageInputDE).toBeTruthy(); spyOn(newPracticeQuestionComponent,'handleFileSelect');/*.and.callThrough();/*.and.callFake((event)=>{ console.log("fake handleFileSelect called with event",event); });*/ /* nativeElemenet hasn'nt got any type. As this program will run in a browser, we can typecast it to HTMLElement so that we can access prperties and methods of the corresponding nativeElement The HTMLElement interface represents any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it. */ let imageInputNE = imageInputDE.nativeElement ; console.log("input element is ",imageInputNE); console.log("debug element is ",imageInputDE); //imageInputNE.click(); let fileSelectEvent = new Event("change",{bubbles:true, cancelable: false}); console.log("created event ",fileSelectEvent); imageInputNE.dispatchEvent(fileSelectEvent); expect(newPracticeQuestionComponent.handleFileSelect).toHaveBeenCalled(); }); ,但这是我能想到的最好的方法!

答案 1 :(得分:0)

other answer开始,我建议您将handleFileSelect更改为仅使用files

interface File {
  // the properties you need, for example:
  name: string;
}

// You don't need all this unless you must use `item()`
// interface FileList {
//     readonly length: number;
//     item(index: number): File | null;
//     [index: number]: File;
// }
// instead, just use ArrayLike<File>
export function handleFileSelect(files: ArrayLike<File>) {
    // Write code here, for example:
    if (files.length > 0) {
        console.log(files[0].name);
    }
}

然后您的测试应该是:

const files = [{
  name: 'File 1'
}];
handleFileSelect(files);

您的组件将只是:

handleFileSelect(event.target.files);

Here's an example展示了这两种用法如何不会在编译器中触发错误。