Skip to content

Instantly share code, notes, and snippets.

@Quramy
Created February 2, 2024 01:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Quramy/75943784df2e325db20772ee0c50431d to your computer and use it in GitHub Desktop.
Save Quramy/75943784df2e325db20772ee0c50431d to your computer and use it in GitHub Desktop.
Next.js の Experimental Test Mode は何をしているのか

先週に同僚と会話していたときに気になっていた Experimental Test Proxy について。

https://github.com/vercel/next.js/blob/canary/packages/next/src/experimental/testmode/playwright/README.md

mugi さんの素振りメモこの機能が実装された際の PR のコードを読んで、ようやく何が行われているかを理解した。

まず、README にある以下のコードがどこをスタブしているかであるが、

import { test, expect } from "next/experimental/testmode/playwright";

test("/product/shoe", async ({ page, next }) => {
  next.onFetch((request) => {
    if (request.url === "http://my-db/product/shoe") {
      return new Response(
        JSON.stringify({
          title: "A shoe",
        }),
        {
          headers: {
            "Content-Type": "application/json",
          },
        }
      );
    }
    return "abort";
  });

  await page.goto("/product/shoe");

  await expect(page.locator("body")).toHaveText(/Shoe/);
});

スタブしているのは下記の図でいうところの req B の方であって、 req A の Browser - Next server ではない。Request URL の例に my-db と入ってるのはこれを強調する意図なんだと思う。

flowchart LR

ua[Browser]
bff[Next server]
backend[Backend service]

ua -- "req A"--> bff -- "req B" --> backend

Next Server からの Backend に対する Request を Playwright spec からスタブできるのは普通に考えるとかなり奇妙だが、以下のようにしてこの仕組みを実現している。

  1. Playwright Worker でテストのライフサイクルに合わせて Node.js の HTTP Server を上げ下げする処理を挟み込む. 起点はここ
  2. Playwright Page Fixture で Browser - Next server に Next-Test-Proxy-Port Header に 1.で上げた Server の Port 番号を付与するようにする. この辺
  3. Next Server 側は --experimental-test-proxy が付与されたときに以下の処理が追加される
    • Next Server 自身の Request ハンドラで Experimental Test 用の ALS を起動しておく
    • fetch をインターセプトする. ここの interceptTestApis という関数が実体.
      • インターセプトされた fetch は本来の Backend Service ではなく、リクエストの Next-Test-Proxy-Port Header Port にプロキシする

このようにして Next server からの fetch が Playwright Worker 上の HTTP Server に流れ込むようになる。

flowchart LR

ua[Browser]
proxy[Proxy Server\ne.g. MSW handlers]
bff[Next server]

subgraph Playwright
    direction BT
    subgraph Worker
        direction LR
        proxy
    end
    Worker -- "Control via CDP" --> ua
end

ua -- "req A" --> bff

subgraph Next
    direction TB
    bff --> fetch
end

fetch -- "req intercepted via test mode" --> proxy

Experimental Text Mode では Request ハンドラとして MSW も選択できることになっているが(ただし v1 系)、Node.js + MSW で想像する msw/node などは一切でてこない。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment