TestCafe（４）テストのAPI３（画面の操作とアサーション）

## 記事の概要
　TestCafeについての記述の4回目です。
　テストで何ができるかの概要の確認の為にテスト内容の記述について書いてます。ハンドブック的に使おうと思っているので詳しく知りたい場合はAPIのドキュメントを参照してください。
　APIの説明をを自分なりにかいつまんで書いているので、動作確認はしていません。間違ってたら教えてください。
　なんか、書いてたらAPIの日本語化＋補足してるみたいになってしまいました。
　やっと終わった。

## 画面操作

### 操作
　画面操作はテストコントローラのメソッドとして記述されています。
　これはクリックのサンプルです。

```
fixture `My fixture`
    .page `http://www.example.com/`;

test('MyTest', async t => {
    await t
        .typeText('#input', 'Hello world!')
        .click('#apply');
});
```

 利用できる処理を記述する前に、操作一覧の記述で用いる引数を示します。

| 引数 | 型 | 概要 |
| :-- | :-- | :-- |
| selector | 関数<br />文字列<br />セレクタ<br />スナップショット<br />プロミス | 捜査対象のエレメントのセレクタを指定します。 |
| dragOffsetX | 数値 | ドラッグ時の横の移動距離<br />右方向 |
| dragOffsetY | 数値 | ドラッグ時の縦の移動距離<br />下方向 |
| startPos | 数値 | テキスト選択時の開始文字位置<br />0始まり |
| endPos | 数値 | テキスト選択時の終了文字位置<br />0始まり |
| url | 文字列 | ページ表示時のURL |
| filePath | 文字列（配列） | アップロードするファイルパス<br />配列で複数指定可能 |
| options | オブジェクト | 「基本オプション」<br />「クリックオプション」<br />「マウス操作オプション」<br />「キー入力オプション」<br />「スクリーンショット」<br />のいずれか |

　オプションオブジェクトの属性は以下の通りです。（エレメントの部分スクリーンショットのオプションは複雑なので記述していません。使う場合はドキュメントのAPIを参照してください。主にスクリーンショットする部分を指定するものです）

| 属性 | 型 | 使用可能オプション | 概要 |
| :-- | :-- | :-- | :-- |
| modifiers | 真偽値 | クリック<br />マウス操作<br />キー入力 | 以下のメンバでキーの押下状態<br />　ctrl<br />　alt<br />　shift<br />　meta　|  
| offsetX | 数値 | クリック<br />マウス操作<br />キー入力 | 左上からの横方向の相対座標 |
| offsetY | 数値 | クリック<br />マウス操作<br />キー入力 | 左上からの縦方向の相対座標 |
| caretPos | 数値 | クリック<br />キー入力 | カレットの位置<br />（文字数で左端が0） |
| replace | 真偽値 | キー入力 | 既存のテキストと入れ替えるか<br />（デフォルトはfalse） |
| paste | 真偽値 | キー入力 | 貼付処理として実施するか<br />（デフォルトはfalse）
| path | 文字列 | スクリーンショット | スクリーンショットの保存先（相対パス） | 
| fullPage | 真偽値 | スクリーンショット | 表示されていない部分もスクリーンショットするかどうか |
| speed | 数値 | 全て | 処理速度。<br />1.0（最速）～0.01（最遅） |

 操作は以下の通りです。

| 操作 | メソッド | オプション |
| :-- | :-- | :-- |
| マウスクリック<br />（左ボタン） | click(selector[,options]) | クリックオプション |
| マウスクリック<br />（右ボタン） | rightClick(selector[,options]) | クリックオプション |
| マウスダブルクリック<br />（左ボタン） | doubleClick(selector[,options]) | クリックオプション |
| マウスドラッグ | drag(selector,dragOffsetX,dragOffsetY[,options]) | マウス操作オプション |
| マウスホバー | hover(selector[,options])| マウス操作オプション |
| テキスト選択 | selectText(selector[,startPos][,endPos][,options]) | 基本オプション |
| テキスト入力 | typeText(selector,text[,options]) | キー入力オプション |
| キープレス | pressKey(keys[,options]) | 基本オプション |
| ページ移動 | navigateTo(url)| 無し |
| スクリーンショット| takeScreenshot([options]) | スクリーンショット |
| エレメント部分の<br />スクリーンショット | takeElementScreenshot(selector[,path][,options]) | 複雑なのでここでは記述していません |
| アップロードファイルを設定 | setFilesToUpload(selector,filePath ) | 無し |
| ウィンドサイズの変更 | resizeWindow(width,height) | 無し |

#### テキスト入力のtype属性によるフィルター
 「typeText」では入力するinputエレメントの「type」属性によっては以下のような書式制御が行われます。

| type | 書式制御 | サンプル |
| :-- | :-- | :-- |
| Date | yyyy-mm-dd | '2017-12-23' |
| Week | yyyy-Www | '2017-W03' |
| Month | yyyy-mm | '2017-08' |
| DateTime | yyyy-mm-ddThh:mm | '2017-11-03T05:00' |
| Time | hh:mm | '15:30' |
| Color | #rrggbb | '#003000' |
| Range | n | '45' |

#### 「pressKey」で利用できるキー文字列
　キー入力は以下のようなキーが指定できます。

| キーの種類 | 例 |
| :-- | :-- |
| アルファベットや数字 | 'a', 'A', '1' |
| モディファイキー | 'shift', 'alt' (⌥ key on macOS), 'ctrl', 'meta' (meta key on Linux and ⌘ key on macOS) |
| 特殊キー | 'backspace', 'tab', 'enter' |
| コンビネーション | 'shift+a', 'ctrl+d' |

特殊キーには以下のものがあります。
'backspace'
'tab'
'enter'
'capslock'
'esc'
'space'
'pageup'
'pagedown'
'end'
'home'
'left'
'right'
'up'
'down'
'ins'
'delete'

## アサーション（チェック）
　アサーションはセレクタのプロパティーやクライアント関数の結果を期待値と比較し、想定通りかどうかのチェックを行います。チェックが正常なら次の処理に移りますが、失敗するとエラーメッセージを出力して「test」の実施関数を終了します。以下の例ではキー入力前後のテキストの変化をチェックしています。

```
import { Selector } from 'testcafe';


fixture `Example page`
   .page `http://devexpress.github.io/testcafe/example/`;


test('Check property of element', async t => {
   const developerNameInput = Selector('#developer-name');

   await t
       .expect(developerNameInput.value).eql('', 'input is empty')
       .typeText(developerNameInput, 'Peter Parker')
       .expect(developerNameInput.value).contains('Peter', 'input contains text "Peter"');
});
```

　アサーション一覧で記述している共通の引数を以下の表に示します。

| 引数 | 型 | 概要 |
|:--|:--|:--|
| actual | 自由 | 比較元の値。主にセレクタの属性値やクライアント関数の結果 |
| message | 文字列 | エラー発生時のメッセージ | 
| option | オブジェクト | 以下の属性を持つオブジェクト<br />　timeout:チェックのタイムアウト(ms)<br />　allowUnawaitedPromise:比較元がPromisの場合に結果を返さない場合も正とする？（これについては自信が無いです） |
 
　以下は比較の一覧です。

| 比較方法 | メソッド | 個別の引数 |
| :-- | :-- | :-- |
| 完全一致 | expect(actual).eql(expected,message,options); | expected:一致すべき値 |
| 不一致 | expect(actual).notEql(unexpected,message,options); | unexpected:一致すべきでない値 |
| trueであること| expect(actual).ok(message,options); | 無し |
| falseであること| expect(actual).notOk(message,options); | 無し |
| 含む | expect(actual).contains(expected,message,options); | expected:含むべき値 |
| 含まない | expect(actual).notContains(unexpected,message,options); | unexpected:含むべきではない値 |
| 型が一致する | expect(actual).typeOf(typeName,message,options); | typeName:一致すべき型 |
| 型が一致しない | expect(actual).notTypeOf(typeName,message,options); | typeName:一致してはいけない型 |
| より大きい | expect(actual).gt(expected,message,options); | expected:この値より大きいこと |
| 以上 | expect(actual).gte(expected,message,options); | expected:この値以上であること |
| より小さい | expect(actual).lt(expected,message,options); | expected:この値より小さいこと |
| 以下 | expect(actual).lte(expected,message,options); | expected:この値以下であること |
| 範囲内 | expect(actual).within(start,finish,message,options ); | start:範囲の開始値<br />finish:範囲の終了値 |
| 範囲外 | expect(actual).notWithin(start,finish,message,options); | start:範囲の開始値<br />finish:範囲の終了値 |
| 正規表現一致 | expect(actual).match(re,message,options); | re:一致すべき正規表現 |
| 正規表現不一致 | expect(actual).notMatch(re,message,options); | re:一致してはいけない正規表現 |

## 気になるところ
　本当はこれが一番書いておきたかった事です。

1.テスト開始すると最初のページがGetされる
　前にも書いたのですが、テスト開始でページが設定されたページでGet処理されます。できればページをロードし直さず、そのまま実行できるオプションを付けてほしい。

2.テスト内を細かく分ける機能が無い
　上記の対応でテスト開始時にページを再取得せずに実行できればこれも解消されるのですが、テスト内で細かく処理を区分けして、どのチェックが通ったのかを見たい時に困る。アサーションごとにエラーメッセージは定義できますが、正常実行として見たいこともあります。

3.「input」タグで「type=number」の場合、数値以外が入力できない
　これはびっくりしたのですが、TestCafeの仕様かNode.jsに依存するのでしょうか。ブラウザによっては「type=number」でも数値以外が入力できるので、コンポーネントテストで正しくバリデーションできているかチェックしようとしてもできませんでした。
　rangeにはそのような記述（「キー入力のtype属性によるフィルター」に記述しています）はあるのですが。なんかバグっぽいなー。

4.マウスの長押しができない（回避策有り）
　ボタンの長押しの機能が無い。押し続けると数値が上がっていくカウンターのテストができない。回避手段としてはクリックではなくドラッグで実行速度を遅くして、ボタン内で微小な距離を移動させました。

5.キー入力の既存文字の扱い
　キー入力操作をすると、基本は入れ替えずに既存のテキストの後ろに追加される。オプションで「replace」を設定すると書き換えになる。（最初に参照した資料では「pressKey」を利用して全選択してデリートキーを押す処理を先に行っていましたが、キー入力のオプションを使う方が良いですよね。）