定義
旧ブラウザ互換性の回帰を捕まえるには、画面が「描画されるか」だけを見るスモークでは足りない。実際の クリック・トグル・送信といったインタラクションを実行 しながら、広いコンソールエラーフィルタでランタイムエラーをテスト失敗に昇格させる必要がある。
なぜ必要か
ランタイムの TypeError は、コードが 実際に実行される時点 でのみ発生する。スモークがページを開いて「行が N 個見えるか、特定のテキストがあるか」のようにレンダリングだけを検査すると、肝心の壊れるイベントハンドラは 一度も実行されず、テストが緑で通ってしまう。そのため「テストは通るのに実際の旧ブラウザではログインできない」状況が生じる。
もう一つ: 5 年超の旧ブラウザは、最新の自動化ツールがバンドルする最新エンジンでは 再現そのものができない。そのバージョンのブラウザ・ドライバのバイナリを固定バージョンで直接ダウンロードして起動し、プロダクションビルドを実際にそのエンジンで操作してこそ、構文・ランタイムの互換性を本当に検証できる。
動作原理
- レンダー検査だけのスモーク: 存在/テキストのアサーション。ハンドラが全部死んでも通る — 互換性回帰に無力。
- インタラクションアサーション: 実際のクリック・トグル・送信・ルーティングを実行し、ハイドレーション + ハンドラの動作を証明する。
- コンソールエラー昇格:
syntaxerror・referenceerror・is not a function・is not definedを失敗とみなす。ただしネットワーク性のFailed to fetchは誤検知なので除外する。 - API 存在チェックと分離: ポリフィルがロードされたか(カバレッジ)とインタラクションが動作するか(動作)は役割が違うので、別々に検証する。
- 固定バイナリ起動: 旧ブラウザ + ドライバをバージョンピンでダウンロードし、バージョン強制検証したうえで起動する。
// コンソールエラーフィルタの例: 互換性シグナルだけ昇格、ネットワーク性は除外
function isCompatError(msg) {
const m = msg.toLowerCase();
if (m.includes("failed to fetch")) return false; // ネットワーク誤検知を除外
return (
m.includes("syntaxerror") ||
m.includes("referenceerror") ||
m.includes("is not a function") ||
m.includes("is not defined")
);
}
実務での適用
- ページごとの中核インタラクション(クリック・トグル・ダイアログ・ルーティング・画像など)を実際に実行するアサーションを追加する。
- コンソールフィルタを上のようにチューニングする。
- 旧ブラウザ・ドライバを固定バージョンで受け取り、プロダクションビルドをそのエンジンで起動する。
- 旧ドライバの既知のクラッシュ(例: 自動化アクションエンドポイントの欠陥)は迂回ヘルパーで包む。
トレードオフ
| 選択 | 利点 | コスト |
|---|---|---|
| レンダーのみスモーク | 速く安定 | 互換性回帰を捕まえられない |
| インタラクションスモーク | 実際の動作を証明 | 遅く、メンテが必要 |
| 固定旧エンジン起動 | 本当の互換性検証 | バイナリのダウンロード・バージョンピン維持 |
使うべきでない場合
- 最新ブラウザのみ対応するプロジェクトに重い旧エンジンスモークを常時回す場合 — 過剰投資。
- コンソールフィルタを広げすぎてネットワーク・サードパーティのノイズまで失敗にし、flake を誘発する場合。
よくある失敗
- レンダーだけ検査して「旧ブラウザ通過」と結論づける。
- コンソールフィルタが
syntaxerrorだけを見て、存在しない API(is not a function)を見逃す。 - ネットワークの
Failed to fetchを互換性失敗と誤検知する。 - 最新エンジンで旧ブラウザを真似て検証したと思い込む。
関連概念
- syntax-transpilation-vs-runtime-polyfill — スモークが捕まえるべき二種類の互換性エラー
- hydration-failure-dead-handlers — インタラクションテストが証明すること(ハンドラが生きているか)
- playwright-flaky-vs-failed-triage — 本当の失敗と flake を切り分けるスモークトリアージ