CypressでMailhog上のメールを取得したい

## やりたいこと
- E2EテストツールであるCypress上でメールフォーム等からメールを送信
- MailHogを使ってメールの受信を確認
- さらにメール本文内のURLを取得したい

Mailhog本体の導入は省略します。

| package | version |
| ------- | ------- |
| cypress | 10.0.7 |
| [cypress-mailhog](https://github.com/SMenigat/cypress-mailhog) | ^1.6.0 |
| [cypress-wait-until](https://github.com/NoriSte/cypress-wait-until) | 1.7.2 |

## 導入
### パッケージをインストール
```title:install
npm install cypress-mailhog
```

### Cypress内のcommand.jsで読み込む
```title:cypress/support/commands
import 'cypress-mailhog';
```

### mailHogUrlをCypressConfigで設定
```cypress.config.ts
export default defineConfig({
    env: { 
        mailHogUrl: "http://localhost:8025/"
    },
    e2e: {
        baseUrl: 'http://localhost:8080'
    }
})
```

## 実装
例：**「パスワード再設定画面で、メールアドレスを入力し送信ボタンを押すと手続きを進めるURLが記載されたメールが届く」** という流れ。

![reset-sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2718989/9d3b526b-0074-3e7c-781a-7143c461378a.png)
まずはコーディング。
```html:例: /reset-password.html
<form>
  <input type="text" name="email">
  <button type="submit" data-test="btn-reset-password">送信</button>
</form>
```

次はE2Eテスト。画面遷移とform入力、ボタンクリック。
```javascript:reset-password.cy.js
// reset-password.htmlに遷移
cy.visit('/reset-password');

// メールアドレスを入力
cy.get('input[name="email"]').type('test@aaa.com');

//再設定ボタンを押す
cy.get('[data-test="btn-reset-password"]').click();
```

![reset-mailhog-sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2718989/324bef22-9ab3-24d4-9f70-6dbc627161a3.png)
そしてcypress上でメールを取得。
cypress-mailhogにはコマンドが用意されており、今回は主に以下を使用する。
### コマンド一例

```javascript:cypress-mailhog - commands
// 全件取得(最大50)
cy.mhGetAllMails()

// 取得したうちの最新のメールを取得
cy.mhGetAllMails().mhFirst()

// MailHog上のメールを全件削除
cy.mhDeleteAll()

// 最新のメールの本文を取得
cy.mhGetAllMails().mhFirst().mhGetBody()
```

### 実際に使ってみる
```javascript: Cypressでmailhog上のメールを取得する
// 取得した一覧のうち、１件目（最新）のメール本文を取得
cy.mhGetAllMails().mhFirst().mhGetBody().then((body) => {})
```

`mailparser` が使えるのを後から知る。
参考：　https://github.com/SMenigat/cypress-mailhog/issues/25

### mailparser でメール本文を取得
``` js: インストール
npm install mailparser -D
```

```js: plugins/index.js
const parser = require('mailparser').simpleParser;
module.exports = (on, config) => {
    on('task', {
        'parse-mail': ({ mail }) => parser(mail),
    })
}
```

command.jsに登録
```js:command.js
Cypress.Commands.add('mhParseMail', { prevSubject: true }, (mail) => {
  return cy.task('parse-mail', { mail: mail.Raw.Data })
})
```
```javascript:test.cy.js
cy.mhGetAllMails().mhFirst().mhParseMail().then((res) => {
    //本文を取得しデコード
    const mailText = decodeURIComponent(escape(window.atob(_body)))

    //URLを抽出
    const urlArr = mailText.match(/((h?)(ttps?:\/\/[a-zA-Z0-9.\-_@:/~?%&;=+#',()*!]+))/g)})
})
```



## cypress-wait-until
また、mailhogはメールが届くまでタイムラグがある可能性があるので `cypress-wait-until` を合わせて使用することに。
30秒間、1秒ごとに `sender` 宛のメールを確認し、届いていたら `true` が返る。（タイムアウトしたらエラーメッセージが返される。）

```
npm install cypress-wait-until -D
```

これもcommand.jsに登録
```js:mhGetMailsBySender
Cypress.Commands.add('mhGetMailsBySender', (sender, wait=true) => {
  if(wait) {
    //Wait until there is at least one email from sender
    cy.waitUntil(() => cy.mhGetAllMails(10).then((mails) =>
      mails.filter((mail) => mail.Raw.From === sender).length > 0
    ), {
      errorMsg: `${sender} からのメールはありませんでした`,
      timeout: 30000,
      interval: 1000
    })
  }
  cy.mhGetAllMails().then((mails) => {
    return mails.filter((mail) => mail.Raw.From === sender);
  })
})
```
参考：https://github.com/SMenigat/cypress-mailhog/issues/11

---
mailparserとwait-utilを組み合わせると、こう。

```js: test.cy.js
const userEmail = "test@aaa.com"
cy.mhGetMailsBySender(userEmail).mhFirst().then((res) => {
    const fromEmail = res.Raw.From

    //メールアドレスが一致しているかチェック
    expect(fromEmail).to.eq(_userEmail)

    //本文からURLを抽出
    const _body = res.Content.Body
    if(!_body) return
    let mailText = decodeURIComponent(escape(window.atob(_body)))
    const urlArr = mailText.match(/((h?)(ttps?:\/\/[a-zA-Z0-9.\-_@:/~?%&;=+#',()*!]+))/g)

    //以下略
  })
})
```

## 終わり
Cypressに苦戦を強いられ続けていますが、もっと効率よく上手に調べて使う力をつけなくてはと反省する日々です。先人方に圧倒的感謝。