🎭

Playwright の Chrome 拡張でハマったこと

2024/12/23に公開

環境

  • Playwright 1.45.1

参考資料

ハマったこと

localStorage

global.setup で localStorage に言語設定を行っていますが、テストに言語設定が反映されません。

tests/global.setup.ts
import { test as setup, expect } from "@playwright/test";
import dotenv from "dotenv";
import { STORAGE_STATE } from "../playwright.config";

// Read from default ".env" file.
dotenv.config();

setup.use({
  baseURL: process.env.BASE_URL,
});

setup.beforeEach(async ({ page }, testInfo) => {
  console.log(`Running ${testInfo.title}`);
});

setup("set localStorage", async ({ page }) => {
  await page.context().addInitScript(() => {
    window.localStorage.setItem("com.example.lang", "ja");
  });

  await page.goto("/");
  await expect(page).toHaveTitle(/Example/);

  await page.context().storageState({ path: STORAGE_STATE });
});
fixtures/chrome-extension.fixtures.ts
import {
  test as base,
  chromium,
  Page,
  type BrowserContext,
} from "@playwright/test";
import path from "path";

export const test = base.extend<{
  page: Page;
  context: BrowserContext;
  extensionId: string;
}>({
  context: async ({}, use) => {
    const pathToExtension = path.join(
      __dirname,
      "../chrome-extension"
    );
    const context = await chromium.launchPersistentContext("", {
      headless: false,
      args: [
        `--disable-extensions-except=${pathToExtension}`,
        `--load-extension=${pathToExtension}`,
      ],
    });

    await use(context);
    await context.close();
  },
  extensionId: async ({ context }, use) => {
    let [background] = context.serviceWorkers();
    if (!background) background = await context.waitForEvent("serviceworker");

    const extensionId = background.url().split("/")[2];
    await use(extensionId);
  },
});
export const expect = test.expect;

解決策

fixture では global.setup で設定した localStorage が反映されないため、fixture 内で同様の処理を行う必要があります。

fixtures/chrome-extension.fixtures.ts
import {
  test as base,
  chromium,
  Page,
  type BrowserContext,
} from "@playwright/test";
import path from "path";
import { STORAGE_STATE } from "../playwright.config";

export const test = base.extend<{
  page: Page;
  context: BrowserContext;
  extensionId: string;
}>({
  context: async ({}, use) => {
    const pathToExtension = path.join(
      __dirname,
      "../chrome-extension"
    );
    const context = await chromium.launchPersistentContext("", {
      headless: false,
      args: [
        `--disable-extensions-except=${pathToExtension}`,
        `--load-extension=${pathToExtension}`,
      ],
    });

    // global.setup で行っている localStorage の処理を追記する
    // https://github.com/microsoft/playwright/issues/27161
    context.addInitScript(() => {
      window.localStorage.setItem("com.example.lang", "ja");
    });
    context.storageState({ path: STORAGE_STATE });

    await use(context);
    await context.close();
  },
  extensionId: async ({ context }, use) => {
    let [background] = context.serviceWorkers();
    if (!background) background = await context.waitForEvent("serviceworker");

    const extensionId = background.url().split("/")[2];
    await use(extensionId);
  },
});
export const expect = test.expect;

Headless mode

公式ドキュメントの Headless mode を参考に実装すると次のエラーが発生しました。

fixtures/chrome-extension.fixtures.ts
import {
  test as base,
  chromium,
  Page,
  type BrowserContext,
} from "@playwright/test";
import path from "path";

export const test = base.extend<{
  page: Page;
  context: BrowserContext;
  extensionId: string;
}>({
  context: async ({}, use) => {
    const pathToExtension = path.join(
      __dirname,
      "../chrome-extension"
    );
    const context = await chromium.launchPersistentContext("", {
      // headless: false,
      channel: "chromium", // Headless mode
      args: [
        `--disable-extensions-except=${pathToExtension}`,
        `--load-extension=${pathToExtension}`,
      ],
    });

    await use(context);
    await context.close();
  },
  extensionId: async ({ context }, use) => {
    let [background] = context.serviceWorkers();
    if (!background) background = await context.waitForEvent("serviceworker");

    const extensionId = background.url().split("/")[2];
    await use(extensionId);
  },
});
export const expect = test.expect;
Test timeout of 30000ms exceeded while setting up "extensionId".
@chrome-extension.fixtures.ts:33
Error: browserContext.waitForEvent: Target page, context or browser has been closed

解決策

--headless=new を追記することでエラーが解決しました。

fixtures/chrome-extension.fixtures.ts
    const context = await chromium.launchPersistentContext("", {
      // headless: false,
      channel: "chromium", // Headless mode
      args: [
        `--headless=new`, // Headless mode
        `--disable-extensions-except=${pathToExtension}`,
        `--load-extension=${pathToExtension}`,
      ],
    });

Discussion