Cypressを使って、１つの画面を開きながら、別の画面に指示を送る

Cypressは基本１つのブラウザを操作するテストツールですが、今回２つのブラウザを操作したいシチュエーションになり、何か方法はないかと色々試してみたので、それの記事をまとめたいと思います。

# 結論から言うと
**２つのブラウザを同時にテストすることはできないが、１つの画面を開きながら、別の画面のAPIを操作することはできる**


# どんなシチュエーション？
![websocketのイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3747635/2be15342-836d-387a-1500-7bd10723a497.png)
 画面Aの入力フィールドに文章入力後、「送信」ボタンを押すと、
 画面Bにwebsocket通知がされるようになっている。
 ![スクリーンショット 2024-04-13 0.34.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3747635/3af47274-f987-3652-47bc-d1cb20cb40f8.png)


 この時のAのメッセージ送信APIが叩かれたら、正しくwebsocket通知がされるかをCypressでテストしたい。
 
## つまずいたこと
Cypressは１つのブラウザ内で動作するので、テスト実行時に複数のタブをサポートできない。つまり、２つ以上の画面を開いてテストすることは不可能。

## 解決策
複数の画面を使って操作したい内容が、APIを叩く行為だった場合、cypress内で直接APIを叩けばいい。
公式にある通り、CypressにはHTTP リクエストを作成する機能があります。
```
cy.request('POST', 'http://localhost:8888/users/admin', { name: 'Jane' }).then(
  (response) => {
    // response.body is automatically serialized into JSON
    expect(response.body).to.have.property('name', 'Jane') // true
  }
)
```


引用元　Cypressのリクエスト作成について↓

https://docs.cypress.io/api/commands/request

これを使えば、Bの画面を開いた状態で、A画面で叩きたかったAPIを直接叩いて、Bの画面で通知を受け取ることが可能です。


実際にコードを見てみます
## A画面のvue.js
```
<template>
    <a>送りたいメッセージ<a>
    <input v-model="message" data-cy="message">
    <button @click="sendMessage" data-cy="send">送信</button>
</template>

<script>
export default {
  
  data() {
    return {
      message: "",
    };
  },
  methods: {
    sendMessage() {
        let result = await axios.post(
            process.env.VUE_APP_SEND_MESSAGE_API,
            {message: this.message}
        )
    },
  },
}
```
見た目はこんな感じ
![画面Aの例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3747635/98be42f6-124f-7f44-32eb-364197f01fe6.png)

## B画面のvue.js
```
<template>
    <p>画面B</p>
    <p>受け取ったメッセージ<p>
    <p data-cy="message">{{message}}<p>
</template>

<script>
export default {
    data() {
        return {
            message: ""
        };
    },
    async created() {
        this.connection = new WebSocket(process.env.VUE_APP_WEBSOCKET);
        this.connection.onmessage = (event) => {
            console.log('受け取ったmessage', event);
            let parsed = JSON.parse(event.data);
            this.message = parsed.message;
        };
        this.connection.onopen = () => this.connection.send("B画面");
    }
}
</script>
```
見た目はこんな感じ
<img width=60% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3747635/f1a08a34-fb8f-91ef-5549-856b124b04cf.png">


## Cypressでテストを書いていく！
```
// 画面Bを開く
cy.visit('/b')
// websocketの接続情報が登録されるのを待つ...🔴下に補足あり
cy.wait(3*1000)

// A画面にあるメッセージ送信APIを直接叩いている
cy.request({
    method: 'POST',
    url: 'メーセージを送信するボタンについてたエンドポイント　⭐️下に補足あり',
    body:{
        message: "テストメッセージ"
    }
}).then((response) => {
    // A画面で送信されたメッセージが、表示されているかの確認
    cy.get([data-cy="message"]).should('have.text', "テストメッセージ");
});
```
shoudは、想定される表示になるのを（4秒まで）待って判定してくれるメソッドなので、通知が遅れても拾ってくれます。遅れすぎる場合は、
```
cy.get([data-cy="message"], { timeout: 3*1000 }).should('have.text', "テストメッセージ");
```
こんな感じにして、表示を待つ時間を伸ばしても良さそうです。

補足
🔴...このwaitがないと、どのブラウザが開かれて、通知を送る場合どのブラウザに送るのかの住所になるconnection_idを貼る時間がないため、websocket通知が飛ばないことがあったので注意。waitを入れたら解消されました。

⭐️...url: 'メーセージを送信するボタンについてたエンドポイント'は、
叩きたいAPIを実際に叩くと、開発者ツールのネットワークでも確認できます。
![スクリーンショット 2024-04-13 0.08.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3747635/36743022-1782-1572-cae7-7101d703f55f.png)


## CypressでWebsocketのテストを行うことについて
Cypressのcy.request()コマンドは REST エンドポイントのみに限定されているため、websocketのテストをすることは既存のものでは難しいようです。

また「cypress websocket」で調べると、日本語の記事が全然出てきません。（2024年4月時点）
海外の方が作ってくれたwebsocketのテストツールがgitHubで公開されていますが、自分には正直難しかった…。そのため、できる限り、簡単に、直感的にwebsocketを叩けないかということで、こういった方法をとりました。しかし、これは**Websocket通知のトリガーがRESTエンドポイントであることが必須条件**であることに加え、本来のWebsocketのテストツールを使った方法ではないので、邪道だと思います。

CypressのWebsocket通知テストについては、まだまだ情報が少ないので、わかったら共有していきたいです。