Cypress テストケース書いてわかったこと

# 概要

Cypressを案件に導入しいくつかテストケースを書いてみて溜まったtips
以前、ログインさせるだけでちょっとハマった話は[こちら](https://qiita.com/papettoTV/items/0e90646c0667cdfcc79f)

# Tips

## 難しそうだができたこと

### スマホのswipe

[cy-mobile-commands plugin](https://gitlab.com/nTopus/cy-mobile-commands)で解決

 * swipeの実現方法は、touchmoveイベントをJSで拾ってstyleに描画する、という泥臭い実装をしていた部分
 * [scrollTo](https://docs.cypress.io/api/commands/scrollto#Syntax) や [mouse event](https://docs.cypress.io/api/commands/trigger#Mouse-Events) を試したが動かなかった

#### サンプルコード

```javascript
    cy.get('main > svg')
      .swipe({delay: 100}, [180,200],[40,10])
      .wait(1000) // wait('@some_intercept'); したいがswipeの場合はできなかった
```
##### 注意: interceptはできない

コメントアウトに書いてる通り、`swipe`後にAPI通信が走るので、それをcy.intercept()で監視したかったが、waitしてくれなかった。なので、ここは渋々 .wait(Integer)

### 画像アップロード

* ~~公式に[サンプルコード](https://docs.cypress.io/api/utilities/blob#Image-Fixture)があるので、そのままでok~~
    * だったが CI環境 (GithubActions)上で、動作しないことが判明
* plugin: [cypress-file-upload](https://www.npmjs.com/package/cypress-file-upload) で解決した

### 非同期通信を待つ

* クリック等のイベント後のajaxな処理は cy.interceptでレスポンス完了を待てる
* 複数のリクエストとんで、それらの完了を全て待つ、ってのは単純には行かなそう→できた
    * 複数wait書けばそれぞれが各interceptを待つ

```javascript
cy.get('target_dom')
  .wait('@intercept1')
  .wait('@intercept2')
```

各interceptの時間を変えて終了する順番を変えてみるなどの詳細な動作検証までは未実施

### ダウンロードファイル

CSVファイル（Zipファイル）ダウンロードして内容の検証できた

 * `cypress/downloads/` に保存するので、そこからファイル読み取る

#### サンプルコード

```javascript
const node_zip = require('node-zip')
// 中略
    const filename = 'cypress/downloads/some_data.zip';
    cy.readFile(filename, 'binary').then(buffer => {
      // zip解凍
      const zip = new node_zip(buffer);
      let csv = "";
      for (const fileinzip in zip.files) {
        // 解凍ファイルの中身を取得（Uint8Array形式）
        const uint8arr = zip.files[fileinzip]["_data"].getContent();
        // Uint8ArrayをUTF8に変換
        csv = (new TextDecoder).decode(Uint8Array.from(uint8arr))
      }
      expect(csv).includes("CSVの中の文字列");
    });
```

##### 注意: 文字化け対策が必要

`node-zip` を使った解凍方法を調べて出てるサンプルコードを参考にしたが、それだと、csvファイル（UTF8）の文字化けが発生した。色々試行錯誤した結果

* new node_zip の optionは何も渡さない
* zip.files[fileinzip]["_data"].getContent() で Uint8Array形式のデータを取得
* それをUTF8にエンコード

で、解決。（あと、もっとリファクタできるなw）

## できなかった

### npm module `browser-image-compression` が使えない

本案件では、ブラウザ側で画像圧縮をしており、そのライブラリに `browser-image-compression` を利用していた。画像アップロード自体は上記の通り実施できたが、この圧縮処理はエラーになった。

* cypressの"自動"実行時にだけ `利用できない画像` エラー。手動実行は問題なかった。
* `imageObject instanceof Blob === false`になるためだがその原因は不明
* 回避策として、自動実行時は圧縮しない

```javascript
        if (imageObject instanceof Blob) {
            // browser-image-compressionをコール
        }
```

プロダクト側のコードをテスト用に変える苦渋の決断が必要。

## コツ

### DOMの指定方法

#### `Open Selector Playgroud` は使いにくい

![スクリーンショット 2021-04-22 9.18.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1533/8f3b92ee-6a3d-ca89-c30b-2bc970c05520.png)

 * jsで表示非表示が切り替わるUIでは、要素がうまく取得できないことあり
 * 親要素にフォーカス当たって、子要素を指定できないことあり
 * 通常のブラウザでclassを確認するのが安定

#### 子要素指定は、 `:nth-child(n)` が安定かも
 
* 例）` .parent-dom > :nth-child(2)'`
* `.parent-dom > li` とかしてもDOM取得できないことがある
* この場合は、 `Open Selector Playgroud` が楽。

## 余談

検証したい要素をclassやidを指定するのが良いか？
https://blog.autify.com/ja/why-id-should-not-be-used
基本方針はhtmlの内部構造に影響を受けないようロケータを使う感じ。