WebView2
はじめに
以下では、Microsoft Edge WebView2でPlaywrightを使用する方法について説明します。WebView2はWinFormsコントロールであり、内部でMicrosoft Edgeを使用してWebコンテンツをレンダリングします。これはMicrosoft Edgeブラウザの一部であり、Windows 10およびWindows 11で利用可能です。PlaywrightはWebView2アプリケーションを自動化するために使用でき、WebView2でWebコンテンツをテストするために使用できます。PlaywrightはWebView2に接続するために、Chrome DevTools Protocol (CDP) を介して接続するBrowserType.connectOverCDP()を使用します。
概要
WebView2コントロールは、環境変数WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTSを--remote-debugging-port=9222に設定するか、--remote-debugging-port=9222引数を使用してEnsureCoreWebView2Asyncを呼び出すことで、着信CDP接続をリッスンするように指示できます。これにより、Chrome DevTools Protocolが有効になったWebView2プロセスが開始され、Playwrightによる自動化が可能になります。この場合、9222はポートの一例ですが、他の未使用のポートも使用できます。
await this.webView.EnsureCoreWebView2Async(await CoreWebView2Environment.CreateAsync(null, null, new CoreWebView2EnvironmentOptions()
{
  AdditionalBrowserArguments = "--remote-debugging-port=9222",
})).ConfigureAwait(false);
WebView2コントロールを備えたアプリケーションが実行されたら、Playwrightを介して接続できます
Browser browser = playwright.chromium().connectOverCDP("https://:9222");
BrowserContext context = browser.contexts().get(0);
Page page = context.pages().get(0);
WebView2コントロールが準備完了であることを確認するために、CoreWebView2InitializationCompletedイベントを待つことができます
this.webView.CoreWebView2InitializationCompleted += (_, e) =>
{
    if (e.IsSuccess)
    {
        Console.WriteLine("WebView2 initialized");
    }
};
テストの作成と実行
デフォルトでは、WebView2コントロールはすべてのインスタンスで同じユーザーデータディレクトリを使用します。これは、複数のテストを並行して実行すると、互いに干渉することを意味します。これを避けるには、WEBVIEW2_USER_DATA_FOLDER環境変数を設定する(またはWebView2.EnsureCoreWebView2Async メソッドを使用する)ことで、テストごとに異なるフォルダーに設定する必要があります。これにより、各テストが独自のユーザーデータディレクトリで実行されるようになります。
以下を使用すると、PlaywrightはWebView2アプリケーションをサブプロセスとして実行し、一意のユーザーデータディレクトリを割り当て、テストにPageインスタンスを提供します。
package com.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class WebView2Process {
  public int cdpPort;
  private Path _dataDir;
  private Process _process;
  private Path _executablePath = Path.of("../webview2-app/bin/Debug/net8.0-windows/webview2.exe");
  public WebView2Process() throws IOException {
    cdpPort = nextFreePort();
    _dataDir = Files.createTempDirectory("pw-java-webview2-tests-");
    if (!Files.exists(_executablePath)) {
      throw new RuntimeException("Executable not found: " + _executablePath);
    }
    ProcessBuilder pb = new ProcessBuilder().command(_executablePath.toAbsolutePath().toString());
    Map<String, String> envMap = pb.environment();
    envMap.put("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--remote-debugging-port=" + cdpPort);
    envMap.put("WEBVIEW2_USER_DATA_FOLDER", _dataDir.toString());
    _process = pb.start();
    // wait until "WebView2 initialized" got printed
    BufferedReader reader = new BufferedReader(new InputStreamReader(_process.getInputStream()));
    while (true) {
      String line = reader.readLine();
      if (line == null) {
        throw new RuntimeException("WebView2 process exited");
      }
      if (line.contains("WebView2 initialized")) {
        break;
      }
    }
  }
  private static final AtomicInteger nextUnusedPort = new AtomicInteger(9000);
  private static boolean available(int port) {
    try (ServerSocket ignored = new ServerSocket(port)) {
      return true;
    } catch (IOException ignored) {
      return false;
    }
  }
  static int nextFreePort() {
    for (int i = 0; i < 100; i++) {
      int port = nextUnusedPort.getAndIncrement();
      if (available(port)) {
        return port;
      }
    }
    throw new RuntimeException("Cannot find free port: " + nextUnusedPort.get());
  }
  public void dispose() {
    _process.destroy();
    try {
      _process.waitFor();
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }
}
package com.example;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import java.io.IOException;
public class TestExample {
  // Shared between all tests in this class.
  static WebView2Process webview2Process;
  static Playwright playwright;
  static Browser browser;
  static BrowserContext context;
  static Page page;
  @BeforeAll
  static void launchBrowser() throws IOException {
    playwright = Playwright.create();
    webview2Process = new WebView2Process();
    browser = playwright.chromium().connectOverCDP("http://127.0.0.1:" + webview2Process.cdpPort);
    context = browser.contexts().get(0);
    page = context.pages().get(0);
  }
  @AfterAll
  static void closeBrowser() {
    webview2Process.dispose();
  }
  @Test
  public void shouldClickButton() {
    page.navigate("https://playwright.dokyumento.jp");
    Locator gettingStarted = page.getByText("Get started");
    assertThat(gettingStarted).isVisible();
  }
}
デバッグ
webview2コントロール内で右クリックしてコンテキストメニューを開き、「検査」を選択してDevToolsを開くか、F12を押すだけです。WebView2.CoreWebView2.OpenDevToolsWindowメソッドを使用してDevToolsをプログラムで開くこともできます。
テストのデバッグについては、Playwrightのデバッグガイドを参照してください。