TestCafeの導入からお問い合わせフォームのSampleまで

## TestCafeとは

ブラウザテストを自動化するjavascriptのフレームワークになります。
async/awaitやimport機能を使いながらテストコードを書くことができます。

## Seleniumとの違い

「Selenium」はjavascriptで書くブラウザテストで有名なフレームワークですが「TestCafe」との大きな違いがあります。

- TestCafeでは各ブラウザのWebdriverを設定する必要がない
- Node.jsをインストールしてない環境でもTestCafeでテスト結果を見ることができる。

## 作業環境構築

### TestCafeをインストール

``` command:command
$ npm init
$ npm i testcafe -D                       // ローカルにtestcafeをインストール
$ export PATH=$PATH:./node_modules/.bin   // パスを通す
$ testcafe -v                             // versionが表示されればインストール成功
```

### ローカル環境でテスト実行

``` command:command
$ npm i http-server -D　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　// ローカルにhttp-serverをインストール
```

``` javascript:package.json
{
  "name": "testcafe",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "http-server -o"　　　　　　　　　　　　　　　　　　　　　　　// npm startでブラウザ起動
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "http-server": "^0.10.0",
    "testcafe": "^0.18.2"
  }
}
```

``` command:command
$ npm start                　　　　　　　　　　　　　　　　　　　　　　　　　// http://127.0.0.1:8080が起動
```

## TestCafeのテンプレート

### javascript

``` javascript:test.js
// import 文
// 変数・定数等の宣言

fixture('テスト内容の分類やページのタイトル等')
  .page('テストするページのURL');

test('テスト項目名その1', async t => {
  // テスト記入
});

test('テスト項目名その2', async t => {
  // テスト記入
});

test('テスト項目名その3', async t => {
  // テスト記入
});

```

### テスト実行

``` command:command
$ testcafe chrome,safari ./test.js
```

## お問い合わせフォームのSample

- 入力項目「お名前」 ※必須項目
- 送信ボタンを押下したとき「お名前」が入力されているかチェック
- 入力された場合、送信する旨をダイアログで表示してthanks.htmlに遷移
- 入力されない場合、エラーメッセージを表示
- 戻るボタンを押下したとき「TOPページ」に戻る

### HTML

``` html:contact.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width">
  <title>TestCafe お問い合わせフォーム</title>
  <style>
    .errorMessage {
      display: none;
    }
    .button {
      display: flex;
    }
  </style>
</head>
<body>
  <form>
    <div class="errorMessage"></div>
    <table>
      <tr>
        <th>お名前 <span>※</span></th>
        <td><input type="text" name="name" class="name" required /></td>
      </tr>
    </table>
    <div class="button">
      <div><input type="button" value="戻る" class="back" onclick="location.href='/demo/'" /></div>
      <div><input type="button" name="submit" value="送信" class="submit" /></div>
    </div>
  </form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
  $(function() {
    formAction.init();
  });
  /*
  名前入力が空白だったらエラーメッセージを表示
  名前入力の値をSessionStorageのnameにsetして、thanks.htmlでgetする
   */
  var formAction = {
    init: function() {
      var self = this;
      self.nameSelector = $('.name');
      self.errorMessageSelector = $('.errorMessage');
      self.submitSelector = $('.submit');
      self.errorMessageNone = self.errorMessageSelector.hide();
      self.submitAction();
    },
    submitAction: function() {
      var self = this;
      self.submitSelector.on('click', function() {
        if (self.nameSelector.val() === '') {
          self.errorMessageAction(true);
          return false;
        }
        self.errorMessageAction(false);
        self.confirmAction();
      });
    },
    errorMessageAction: function(errorMessage) {
      var self = this;
      if (errorMessage) {
        self.errorMessageSelector.show().text('必須項目にご記入ください');
      } else {
        self.errorMessageNone;
      }
    },
    confirmAction: function() {
      var self = this;
      if (confirm('この内容で送信します。')) {
        self.submitSelector.prop('disabled', true);
        sessionStorage.setItem('name', self.nameSelector.val());
        setTimeout(function() {
          document.location.href = 'thanks.html';
        }, 1000);
      } else {
        return false;
      }
    }
  }
</script>
</body>
</html>
```

``` html:thanks.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width">
  <title>TestCafe 完了画面</title>
</head>
<body>
  <p class="thanksMessage">
    <span class="name"></span>様、お問い合わせありがとうございます。
  </p>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
  $(function() {
    thanksAction.init();
  });
  /*
  名前の値をSessionStorageから取得する
   */
  var thanksAction = {
    init: function() {
      var self = this;
      self.nameSelector = $('.name');
      self.nameGet = sessionStorage.getItem('name');
      self
      self.thanksDisplay();
    },
    thanksDisplay: function() {
      var self = this;
      self.nameSelector.text(self.nameGet);
    }
  }
</script>
</body>
</html>
```

``` html:index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width">
  <title>TestCafe</title>
</head>
<body>
  <p>TOPページ</p>
</body>
</html>
```

### テストコード

``` javascript:contactTest.js
import Page from  './pageModel';
import { ClientFunction } from 'testcafe'; // 現在のページのURLを確認

const page = new Page();
const getWindowLocation = ClientFunction(() => window.location);

fixture('お問い合わせフォーム')
  .page('http://127.0.0.1:8080/demo/contact.html'); // 実行するページ

test('必須項目を入力して確認後、thanksページに遷移', async t => {
  await t
    // テスト実行速度 1が最高速 0.01が最低速
    .setTestSpeed(page.setTestSpeed)
     //　ダイアログの操作
    .setNativeDialogHandler(() => true)
    // アクション
    .typeText(page.nameSelector, '佐藤一郎')
    .click(page.submitSelector);
  // アサーション(求める結果)
  await t.expect(page.thanksMessage.innerText).eql('佐藤一郎様、お問い合わせありがとうございます。');
});

test('必須項目に入力されていないとき、errorMessageを表示', async t => {
  await t
    .setTestSpeed(page.setTestSpeed)
    .click(page.submitSelector);
  await t.expect(page.errorMessage.innerText).eql('必須項目にご記入ください');
});

test('戻るを押下したときにtopページに戻る', async t => {
  await t
    .setTestSpeed(page.setTestSpeed)
    .click(page.backSelector);
    const location = await getWindowLocation(); // ページ遷移してから現在のURLを取得
  await t.expect(location.pathname).eql('/demo/');
});
```

``` javascript:pageModel.js
import { Selector } from 'testcafe';

/*
  Page Object
 */
export default class Page {
  constructor () {
    this.nameSelector = Selector('.name');
    this.submitSelector = Selector('.submit');
    this.thanksMessage = Selector('.thanksMessage');
    this.errorMessage = Selector('.errorMessage');
    this.backSelector = Selector('.back');
    this.setTestSpeed = 0.5;
  }
}
```

### テスト実行

``` command:command
$ testcafe chrome ./contactTest.js

Running tests in:
 - Chrome 61.0.3163 / Mac OS X 10.13.1

 お問い合わせフォーム
 ✓ 必須項目を入力して確認後、thanksページに遷移
 ✓ 必須項目に入力されていないとき、errorMessageを表示
 ✓ 戻るを押下したときにtopページに戻る


 3 passed (8s)
```

##　参考サイト

- [TestCafe入門 | CodeGrid](https://app.codegrid.net/entry/2017-testcafe-1)
- [TestCafe公式](https://devexpress.github.io/testcafe/)