メール OTP と TOTP は分けて考えます
メール OTP は、メールで届く使い捨ての認証コードです。Playwright のメール認証自動化では、コードの取得だけでなく、入力後に認証が成立したことまで確認します。
| 方法 | コード・状態の取得元 | 検証する範囲 | 本記事での扱い |
|---|---|---|---|
| メール OTP | テスト用メールボックス | メール送信から受信、入力、ログインまで | 対象です |
| TOTP | 認証アプリなど | 時間ベースのコードによる認証 | 生成手順は扱いません |
| 認証済み状態の再利用 | 保存したログイン状態 | ログイン後の機能 | メール認証経路の代用にはしません |
Mailera の仕組みは、既存のメールボックスを API につなぐものです。接続構成の全体像は既存メールプロバイダーを接続するガイド、取得の基本は自分の受信箱から OTP を読む手順を参照してください。

テスト前の準備
- 自分が管理するアプリと、テスト専用メールボックスを用意します。通常業務のメールが届く個人用受信箱は使いません。
- プロバイダー別の手順に沿ってテスト用メールボックスを接続します。Outlook・Microsoft 365 の接続条件は、Microsoft の接続手順とMicrosoft 365 の Graph 設定チェックリストで確認してください。
- 「設定」の「API key」タブから「キーを作成」を開き、「メールボックスのスコープ」をテスト用グループに限定します。読み取り権限(
read)を選び、「有効期限」も設定します。キーは一度しか表示されないため、その場で CI のシークレットに保存します。
Gmail の接続は Gmail の接続ガイドを使ってください。Mailera ではアプリパスワード(app password)を使います。Google のアプリパスワードの説明では、利用条件として 2 段階認証の有効化が示されています。

接続後は GET /mailboxes を呼び、data 内の対象メールボックスの status と last_sync_at を確認します。Mailera の新着確認の頻度はメールボックスの状態によって変わります。リアルタイム監視に対応する場合は使用中に数秒以内で新着を検知し、対応しない場合は使用中に 2 分ごとに確認します。未使用のメールボックスはスリープする場合があります。送信直後に読めることを前提にせず、待機処理を組み込みます。
Playwright からコードを取得して入力する
GET /mailboxes/{id}/otp は最新の OTP を取得する read 権限の API です。from、subject、since_minutes で対象を絞れます。ただし、最新であることと、今回のログイン要求に対応することは別です。
以下の例では、コードだけでなく受信日時と件名を確認し、候補が複数あれば失敗させるため、GET /messages/search に extract=otp を指定します。検索 API には mailbox、from、since を渡し、返された各メッセージの subject を TypeScript 側で OTP_SUBJECT と完全一致で比較します。検索 API に subject パラメーターは渡しません。コードは data 内の各メッセージの otp.code から読みます。
最新コードの取得には /mailboxes/{id}/otp、メッセージ情報を使って候補を選別するこのテストには /messages/search を使う、という使い分けです。API の入口は 開発者向けページ、各操作は API ドキュメントにあります。
送信前に環境変数を設定する
@playwright/test が導入済みの既存の TypeScript テストプロジェクトで、次の値を環境変数に設定します。
| 変数 | 内容 |
|---|---|
MAILERA_API_KEY | テスト用グループに限定した read キー |
MAILBOX_ID | 接続したテスト用メールボックスの ID |
TEST_APP_URL | 自分が管理するアプリのログイン画面 URL |
TEST_EMAIL | そのアプリに登録したテスト用メールアドレス |
OTP_FROM | 認証メールの送信元アドレス |
OTP_SUBJECT | 認証メールの件名全文 |
期限付きの取得ヘルパーを用意する
次を既存のテストプロジェクト内の otp-helper.ts に保存します。待機期限の 60 秒と確認間隔の 3 秒は、この例の設定値であり、到着時間の保証ではありません。リアルタイム監視に対応しないメールボックスでは確認間隔が 2 分のため、アプリ側のコード有効期限も踏まえて待機期限を調整してください。候補が複数ある場合は、コードを推測して選ばず失敗させます。
import type { APIRequestContext } from '@playwright/test';
export async function waitOtp(api: APIRequestContext, started: number) {
const deadline = Date.now() + 60_000;
while (Date.now() < deadline) {
const res = await api.get('https://mailera.app/api/public/v1/messages/search', { timeout: Math.min(5000, deadline - Date.now()), params: {
mailbox: process.env.MAILBOX_ID!, from: process.env.OTP_FROM!, since: new Date(started).toISOString(),
extract: 'otp', limit: 20 } });
if (!res.ok()) throw new Error(`Mailera HTTP ${res.status()}`);
const { data } = await res.json();
const matches = data.filter((m: { subject: string; received_at: string; otp?: { code: string } }) => m.subject === process.env.OTP_SUBJECT && Date.parse(m.received_at) >= started);
if (matches.length > 1) throw new Error('認証メールの候補が複数あります');
if (matches[0]?.otp?.code) return matches[0].otp.code;
await new Promise(resolve => setTimeout(resolve, Math.min(3000, Math.max(0, deadline - Date.now()))));
} throw new Error('認証メールの待機期限に達しました');
}候補がなければ再確認します。HTTP エラーや通信エラーは未着として扱わず、その時点で終了します。コードやレスポンス本文はエラーメッセージに含めません。
既存のログインテストに組み込む
ヘルパーは単独で実行せず、既存の Playwright テストから呼び出します。画面要素は対象アプリに合わせ、次の順序で処理します。
- Mailera 専用の
APIRequestContextを作成します。baseURLはhttps://mailera.app/api/public/v1、認証ヘッダーはAuthorization: Bearer $MAILERA_API_KEYとし、環境変数の実際の値を渡します。上のヘルパーは取得先を完全な URL で指定しているため、baseURLの末尾の/に依存しません。認証ヘッダーを対象アプリへの通信に混在させません。 - ログイン画面を開いてテスト用メールアドレスを入力します。コード送信を要求する直前に
Date.now()を記録し、startedとして保持します。 - 送信要求後に
waitOtp(api, started)を呼び、返されたコードを認証コード欄へ入力します。テスト全体の制限時間は、ヘルパーの待機時間に画面操作と成功確認の時間を加えて設定します。 - ログイン操作を行い、認証後だけ表示される要素で成功を検証します。コードを取得できたことだけでテストを成功させません。
- 成否にかかわらず、
finallyで Mailera 専用コンテキストのdispose()を呼びます。
このテストは自動再試行なしで扱い、同じメールボックスを別の CI ジョブでも同時に使わないでください。
古いコードや別テストのコードを避ける
直列実行だけでは、前回のメールを除外できません。since_minutes も相対的な時間範囲であり、ログイン要求の識別子ではありません。
上の例は送信要求の直前の時刻、送信元、件名を使います。それでも、前回要求したメールが遅れて届けば候補に入る余地があります。厳密に対応付けるなら、テスト対象アプリ側で実行ごとの識別子を件名などに入れ、OTP_SUBJECT にその件名を渡す設計にします。
調査には GET /mailboxes/{id}/messages の received_at と id、必要に応じて GET /mailboxes/{id}/messages/{msgId} の本文を使います。件名は subject、送信元は from.address を確認し、今回の要求に対応するメールか照合してください。候補が多い場合は next_cursor による続きを確認する設計も必要です。上の短い例は、専用メールボックスで候補を少なく保つ前提です。
メールが届かない・認証に失敗する場合の確認
未着時は、まず GET /mailboxes/{id}/status、次に GET /mailboxes/{id}/placement、最後に受信トレイと迷惑メールの実際のメッセージを確認します。status は Microsoft ではサインインを直接確認し、ほかのプロバイダーでは最新の同期に基づく状態を返します。
| 症状 | 確認箇所 | 次の操作 |
|---|---|---|
| メールボックスに接続できない | status と接続情報 | 接続を復旧してから再実行します |
| アプリは送信したが見つからない | placement と迷惑メール | 実際の保存先を確認します |
| メールはあるが候補にならない | 送信元、件名、受信日時 | 実際のメールと条件を照合します |
| コードを入力しても失敗する | アプリ側の有効期限・再送仕様 | 期限切れや旧コードの無効化を調べます |
| 待機期限に達する | 接続状態と同期状態 | 診断情報を残して失敗させます |
Gmail でアカウントのパスワードを変更した場合は、Google が説明するアプリパスワードの失効も確認してください。
明示的な同期には POST /mailboxes/{id}/sync と sync 権限が必要です。通常の read キーに無条件で追加せず、接続準備の操作として分けます。未着のたびにコードを再送するループも作りません。
Mailera は即時到着や受信トレイへの振り分けを保証しません。「迷惑メールではない」で移動しても、次回の振り分けを決めるのはプロバイダーです。また、コードの有効性は対象アプリが判定します。API でコードを取得できただけでは、ログインテストは成功していません。
CI で API キーと認証コードを漏らさないための設定
キーは CI のシークレットからテストプロセスに渡し、ソースコードやブラウザー側に埋め込みません。GitHub Actions を使う場合は、公式のシークレット利用手順に沿って、secrets コンテキストから環境変数へ渡します。有効期限と失効担当を決めておきます。Mailera のキーを失効すると、そのキーを使う API 呼び出しは直ちに拒否されます。
メール OTP のテストでは、スクリーンショット、動画、トレースを無効にしてください。カスタムレポーターや HTTP デバッグログも別途確認し、認証ヘッダー、コード入力値、メール本文を出力しない設定にします。診断用の画像を残す場合は秘密情報を伏せ、保存先と閲覧権限を限定します。
メール本文は信頼できない入力として扱います。必要なコード以外を実行せず、本文に書かれた任意の指示やリンクにも従いません。運用上の確認には Mailera のセキュリティ情報も参照してください。









