[Angular] テキストエリアとボタンを使ったコンポーネントのテストを書く

# 環境
- Angular CLI: 7.3.1
 
たぶん他のバージョンでも動く

# 準備
### 事前準備
テキストエリアやボタンを使うのにFormsModule必要です。

```app.modules.ts
import { FormsModule } from '@angular/forms';
// ...
  imports: [
    FormsModule,
// ...
```
```app.component.spec.ts
  beforeEach(async(() => {
    TestBed.configureTestingModule({
// ...
      imports: [
        FormsModule
// ...
```

### HTMLとTypeScriptの準備
```app.component.html
<div id="message">{{message}}</div>
<textarea [(ngModel)]="input"></textarea>
<button (click)="postMessage(input)">書く</button>
```
```app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  message = '';
  input = '';

  postMessage(message: string) {
    this.message = message;
    this.input = '';
  }

}
```

### あると便利なもの
```app.component.spec.ts
  beforeEach(async(() => {
    TestBed.configureTestingModule({
// ...
      providers: [
        { provide: ComponentFixtureAutoDetect, useValue: true }
// ...
```
CompoentFixtureAutoDetectはある程度コンポーネント変更検知を自動検知してくれる。
何度もdetectChanges()書かなくて済む
まぁ、DOMイベントは自動検知してくれないけど(以下のサンプルでは必須)。

# 本題
### inputイベントが発行されたことをテストする
```app.compoent.spec.ts
it('should input to textarea', () => {
  const fixture = TestBed.createComponent(AppComponent);
  const app = fixture.componentInstance;
  const htmlElement: HTMLElement = fixture.debugElement.nativeElement;
  const textarea = htmlElement.querySelector('textarea');

  textarea.value = 'なんか書いた';               // 注目
  textarea.dispatchEvent(new Event('input')); // 注目
  fixture.detectChanges();                    // 注目

  expect(app.message).toBe('なんか書いた');
});
```
ちなみにquerySelectorに渡す文字列はCSSのセレクターの指定の仕方と同じ

### ボタンをクリックをテストする
```app.component.spec.ts
it('should post message by clicking button', () => {
  const fixture = TestBed.createComponent(AppComponent);
  const htmlElement: HTMLElement = fixture.debugElement.nativeElement;
  const textarea = htmlElement.querySelector('textarea');
  const button = htmlElement.querySelector('button');
  const message = htmlElement.querySelector('div#message');

  textarea.value = 'なんか書いた';
  textarea.dispatchEvent(new Event('input'));
  button.dispatchEvent(new Event('click')); // 注目
  fixture.detectChanges();

  expect(message.htmlContent).toBe('なんか書いた');
});
```

### エンターキーで投稿できるようにする
keydown.enterのコールバックを指定する

```app.component.html
<div id="message">{{message}}</div>
<textarea [(ngModel)]="input" (keydown.enter)="postMessage(input)"></textarea>
<button (click)="postMessage(input)">書く</button>
```
KeyboardEventを発行してテストする

```app.component.spec.ts
it('should post message by pressing enter key', () => {
  const fixture = TestBed.createComponent(AppComponent);
  const htmlElement: HTMLElement = fixture.debugElement.nativeElement;
  const textarea = htmlElement.querySelector('textarea');
  const message = htmlElement.querySelector('div#message');

  textarea.value = 'なんか書いた';
  textarea.dispatchEvent(new Event('input'));
  textarea.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'})); // 注目
  fixture.detectChanges();

  expect(message.htmlContent).toBe('なんか書いた');
});
```

# 参考記事
- [Angular 日本語ドキュメンテーション - テスト](https://angular.jp/guide/testing)
- [HTMLElement - MDN - Mozilla](https://developer.mozilla.org/ja/docs/Web/API/HTMLElement)
- [HTMLTextAreaElement - MDN - Mozilla](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement)
- [HTMLButtonElement - MDN - Mozilla
](https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement)
- [KeyboardEvent - MDN - Mozilla
](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code)
- [JavaScriptでキーボードの入力値を別の値しようとしたらめちゃめちゃ苦しんだ話](http://kwnktks0515.hatenablog.com/entry/2016/07/04/225558)

間違いなどありましたら気兼ねなくご指摘ください