E2EテストのCypressでFirebase認証を突破する

# 何をしたいか
E2EテストにCypressを利用するシーンで、ログイン認証にFibrebaseを採用しているシステムをテスト。
具体的にはログイン画面を経由はするものの、ログイン済みの物としてCypressをシステムにアクセスさせる。

イメージ的にはこんな感じ
![プレゼンテーション1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193036/1a60690c-d48e-9900-1d66-c4647ce207ef.png)


# 何を使うか
- Node
- Cypress
- cypress-firebase
- firebase API
- firebase-tools-extra

# 下準備
この記事で掲載するやり方はCypressを実行するコンピュータにもサーバと同じFirebaseAPIを召喚し、テスト用のTOKENを生成。それを使ってFirebase認証をすり抜けるやり方です。
つまり、FirebaseのProjectIDや、Firebaseコンソールから発行するTEST_UIDなどが必要になります。

FIrebaseコンソールで以下の物を取得してください

- TEST_UID
- serviceAccount.json

# このやり方はセキュリティ的に大丈夫なのか
TEST_UIDなどの漏洩に気をつけていれば特に心配はないです。
と言うのも、システムにバックドアを仕込むでもなく、Firebase公式で、かつ、コンソールからしか取得できない情報が必要なのでそうそう簡単には他者に突破を許しません。
逆に言うと`下準備` で生成する２つの情報はソースコードに直接埋め込まない様に気を付ける必要があります。

※CIだったら環境変数に埋め込むなどの対策が必要です。

# 実装
以下、基本的な手順です。
なお、Cypressの導入手順は割愛します

### .gitignore編集
`Gitまたはソース管理使ってる人だけ！`
忘れがちだけどセキュリティ上やばいので最初に機密情報をGitの対象から外します

以下２行を、 `.gitignore`の一番最後に追加してください

```　.gitignore
serviceAccount.json
cypress.env.json
```

### npmパッケージのインストール
Firebase自体とそのツール、及びcypress-firebaseと、他環境へアクセスする場合はcross-envが必要です。

コピペ用

```
npm install --save cypress-firebase cross-env firebase-tools-extra firebase
```

### Firebaseプログジェクト設定
package.jsonと同じ場所に以下２点を配備してください

- serviceAccount.json
- cypress.env.json

ちなみに、cypress.env.jsonには下準備で取得したTEST_UIDを記入します

```cypress.env.json
{
  "TEST_UID": "************************",
  "FIREBASE_PROJECT_ID": "*********<serviceAccountのproject_id>",
  "FIREBASE_AUTH_JWT": "<上記２点でもし動かなかったらこの項目を追加してみてください>"
}
```

### .fireabsercファイルの作成
またしてもFirebaseの設定ファイルを作成します
名前は `.firebaserc` 固定です

```　.firebaserc
{
  "projects": {
    "develop": "****<serviceAccountのproject_id>"
  }
}
```

### CORSの設定（ホストと違うPCから実行する時だけ）
Cypressはiframeを使用しているため、CORSのチェックで失敗する事があります。
例えば、自分のlocalhostでCypressを実行して、ステージングや本番環境を実行したい時などです。
その場合、 `cypress.json` に以下の設定を追記する事で回避します

```cypress.json
"chromeWebSecurity": false
```

### Cypressのログインテストスクリプトの準備
シンプルなログインテストを書きます。
注意としては、最初にlogoutを記載しておく事です。クライアントがセッションを持っていて１回目はできたのに２回目は動かないなどというトラブルを防いでいます。

```login-specs.js
describe('login form test', function() {
	it('init', function() {
		cy.logout()
	})
	it('login', function() {
		cy.login(Cypress.env('TEST_UID')) // こう書いておけばCIに持っていっても万事OK
		cy.visit('https://hogehoge.jp') //ログイン先
	})
})
```

### npm scriptを追加する
この方法を使うとCypress runでは実行できないので以下の手順を実行するスクリプトを作ってあげます

- FirebaseTestConfigの作成
- testConfigを使ってcypress run

package.jsonにこれをコピペしましょう

```package.json
  "scripts": {
    "test": "npm run build:testConfig && cross-env CYPRESS_baseUrl=https://hogehoge.jp $(npm bin)/cypress run --browser chrome",
    "build:testConfig": "cypress-firebase createTestEnvFile",
    "test:ui": "npm run build:testConfig && cross-env CYPRESS_baseUrl=https://hogehoge.jp $(npm bin)/cypress open"
  },
```

:star: ポイント１：　CYPRESS_baseUrlと先述の `cy.visit`のURLは揃えてあげてください
:star: ポイント２：　`--browser chrome` はChromeヘッドレスモードを強制するための指定です。していないとElectronで実行されて起こらないはずのバグを誘発したりします（ブラウザが違うので）

### 実行
適材適所で先ほどのスクリプトを実行してもらえれば良いのですが、各スクリプトには以下の様な意味があります

- npm run test
    - testConfigを生成して、ヘッドレスChromeでFirebase認証＝＞Cypressを実行する。
    
- npm run test:ui
    - testConfigを生成して、Cypressの画面を表示してからFirebase認証＝＞Cypress実行を行います。グラフィカルにエビデンスをみたい時とかにどうぞ

- npm run build:testConfig
    - Firebaseのホストからテスト用のTOKENを取得します。一度うまくいってしまえば基本的にこれ単体で実行することに意味はありません

# 以上です
設定手順は以上となります。
もしうまく行かない場合は、エラーに表示されている言葉をよく読んでみてください
もし、baseURLが〜〜といっている場合はFirebaseの設定周りがよろしくない事が原因の様です。
Originがなんとかといっている場合は、chromeWebSecurityの設定を変えてみてください。

# 最後に
上記はlocalhostから自分のPC、またはWEB上のサービスをテストするための手順です。
私は実際にはCircleCIから実行したかったのでさらにシックハックしながら完遂させました。その時のお話はまた別の機会に記載しようかと思います :smile: