画像生成とアップスケールを分離するUI要件とQA観点
背景
画像ツールで「改善」という一つのボタンに、画像を新しく作る処理と、既存画像を大きくする処理をまとめると、ユーザーの期待とQA基準が曖昧になります。公開UIを持つ PhotoGenerator AI を題材にすると、目的ごとの入力とレビューを分けて考えやすくなります。
新規作成では、プロンプトから主役・構図・光・スタイルを探索します。アップスケールでは、すでに決まった画像の形や文字を保ちながら、利用先に必要なサイズへ変換します。PhotoGenerator AIの image generator と image upscaler は、この二つの目的を分けて考える題材になります。
入力の要件
フロントエンドでは、ジョブ種別を曖昧な文字列のまま扱わず、入力型に含めます。
type ImageJob =
| { kind: "generate"; prompt: string; references: string[] }
| { kind: "upscale"; sourceId: string; factor: 2 | 4 };
生成側はプロンプト、参考素材、スタイル、比率などをレビュー対象にします。アップスケール側は元画像のプレビュー、倍率、出力の比較を中心にします。入力欄を一部共用しても、処理の目的は画面上で明示します。
状態遷移の要件
画像処理は非同期になるため、失敗時に入力を失わないことが重要です。
type JobState<T> =
| { status: "ready"; input: T }
| { status: "processing"; input: T; requestId: string }
| { status: "review"; input: T; outputUrl: string }
| { status: "failed"; input: T; message: string };
failed からの再実行でプロンプトや元画像を消さない、処理中に二重送信を防ぐ、レビュー画面で入力条件へ戻れる、という振る舞いを先に決めます。これは特定サービスの内部APIを示すものではなく、UI契約をテスト可能にするための例です。
QA観点を分ける
生成の確認では、主役が意図どおりか、構図に必要な余白があるか、光や色の方向が合っているか、指示していない要素が増えていないかを見ます。
アップスケールの確認では、元画像を基準にします。商品の輪郭、ロゴと小さな文字、髪やガラスの境界、線画の太さ、人工的なテクスチャ、実際の掲載サイズを比較します。中央だけでなく、端や細部を確認するのがポイントです。
また、レビュー用の画像URLとダウンロード用の画像URLを同じものだと決めつけないようにします。権限、保存期間、サイズ上限はサービスの仕様に依存するため、画面の表示と正式なAPI仕様を分けて確認します。テストデータには商品、人物、風景、線画を用意し、代表的な境界を毎回比較できるようにします。
test("失敗しても元画像を保持する", async ({ page }) => {
await page.getByLabel(/画像をアップロード/i).setInputFiles("fixtures/source.png");
await page.getByRole("button", { name: /アップスケール/i }).click();
await expect(page.getByTestId("source-preview")).toBeVisible();
await expect(page.getByRole("button", { name: /再試行/i })).toBeVisible();
});
まとめ
生成は「まだない答えを探すジョブ」、アップスケールは「決まった答えを保つジョブ」です。入力、状態、コピー、イベント、レビュー項目を分けることで、単なる画素数の比較ではなく、目的に合ったQAができます。公開ページから確認できるのは画面上の流れまでなので、実装時は正式なAPI仕様と実データで追加検証します。