playwright-msw

repository·main·Indexed 41 days ago

https://github.com/valendres/playwright-msw

A library that enables intercepting and mocking network requests in Playwright end-to-end tests using Mock Service Worker (MSW). It provides utilities like createWorkerFixture and createWorker to manage API mocks, support REST and GraphQL communication patterns, and override handlers on a per-test basis.

Tokens
6.9K
Snippets
18
Records
27
Agent score
49%

What's inside playwright-msw

  1. Overview of Playwright MSW

    main
    The playwright-msw package provides integration between Playwright and Mock Service Worker (MSW), allowing you to intercept and mock network requests during Playwright end-to-end tests. This enables testing of application behavior against mocked API responses without a live backend.
  2. Quickstart: Set up playwright-msw with a custom fixture

    main

    To use playwright-msw, follow these three steps:

    1. Create mock handlers: Define your API mocks using MSW's http and HttpResponse utilities.
    2. Create the worker fixture: Use createWorkerFixture to extend Playwright's test object. This automatically starts the worker for every test.
    3. Use the fixture in tests: Import your extended test object and use the worker fixture to override mocks on a per-test basis using worker.use().

    Note: If your application already runs MSW in the browser (e.g., a local dev server), you should disable it during Playwright tests. It is recommended to test against your production bundle.

    // 1. handlers.ts
    import { http, delay, HttpResponse } from 'msw';
    export default [
      http.get('/api/users', async () => {
        await delay(500);
        return HttpResponse.json([{ id: '1', firstName: 'Rhydian', lastName: 'Greig' }], { status: 200 });
      }),
    ];
    
    // 2. test.ts (Fixture Setup)
    import { test as base, expect } from '@playwright/test';
    import { http } from 'msw';
    import type { MockServiceWorker } from 'playwright-msw';
    import { createWorkerFixture } from 'playwright-msw';
    import handlers from './handlers';
    
    const test = base.extend<{ 
      worker: MockServiceWorker; 
      http: typeof http; 
    }>({
      worker: createWorkerFixture(handlers),
      http
    });
    export { expect, test };
    
    // 3. http.spec.ts (Usage)
    import { expect, test } from '../test';
    
    test('should allow mocks to be overridden', async ({ page, worker, http }) => {
      await worker.use(
        http.get('/api/users', async () => {
          return new HttpResponse(null, { status: 403 });
        })
      );
      await page.goto('/');
      // ... assertions
    });
  3. Run the Playwright MSW Vue Example

    main

    To run the Vue SPA example locally, follow these steps to clone the repository, install dependencies, build the project, and start the development server.

    Note: To run the Playwright tests, the playwright-msw package must have been previously built using yarn build from the root of the repository.

    git clone https://github.com/valendres/playwright-msw
    cd playwright-msw
    yarn
    yarn build
    cd packages/example-vue
    yarn start
  4. Run the Playwright MSW Example SPA

    main

    To run the local development server for the React SPA example, follow these steps from the repository root:

    1. Clone the repository.
    2. Install dependencies using yarn.
    3. Build the project using yarn build.
    4. Navigate to the example package and start the server.

    The SPA uses MSW for mocking and supports both REST (via ReactQuery) and GraphQL (via Apollo) communication patterns.

    git clone https://github.com/valendres/playwright-msw
    cd playwright-msw
    yarn
    yarn build
    cd packages/example
    yarn start
  5. Explore Playwright MSW usage examples

    main

    The monorepo includes two example applications to demonstrate how to integrate playwright-msw with different frontend frameworks:

    • React SPA: Located in the example package.
    • Vue3 SPA: Located in the example-vue package.
  6. Create a manual worker with createWorker

    main

    If createWorkerFixture is too simplified for your needs, use createWorker(page, handlers, config) to manually create a server that intercepts API calls for an individual Playwright page. This returns a MockServiceWorker instance.

    When implementing this as a fixture, ensure the scope is set to 'test' so you can access the page fixture, and set auto: true if you want the worker to initialize even if the test doesn't explicitly call the fixture.

    import { test as base, expect } from '@playwright/test';
    import { createWorker, MockServiceWorker } from 'playwright-msw';
    import handlers from './handlers';
    
    const test = base.extend<{ worker: MockServiceWorker }>({
      worker: [
        async ({ page }, use) => {
          const server = await createWorker(page, handlers);
          await use(server);
        },
        {
          scope: 'test',
          auto: true,
        },
      ],
    });
    
    export { test, expect };
  7. Configure createWorkerFixture

    main

    The createWorkerFixture(handlers, config) function creates a fixture that mocks API calls on a per-test basis. It is automatically started for all tests using the provided handlers.

    Configuration Options

    keyrequireddefaultdescription
    graphqlUrlfalse"/graphql"The URL of the GraphQL endpoint to send requests to.
    waitForPageLoadfalsetrueIf true, waits for the page to load before mocking API calls. This emulates MSW's browser behavior where initial static resource requests are not mocked because MSW initializes after page load.
  8. Customize mocks with MockServiceWorker

    main

    The MockServiceWorker instance (returned by createWorker or createWorkerFixture) provides methods to manipulate mocks during a test execution:

    • use(...customHandlers: RequestHandler[]): Prepends the provided handlers to the existing list. Use this to override default behavior for specific test cases (e.g., simulating a 500 error).
    • resetHandlers(...customHandlers: RequestHandler[]): Resets the handlers to the initial list provided during creation, or to a new specific list if provided.
    • resetCookieStore(): Removes all cookies from MSW's internal cookie store. Note: This is automatically called at the end of each test.
  9. Configure Playwright for playwright-msw examples

    main

    The playwright.config.ts file in the example package defines the test environment for running Playwright tests alongside a local web server. Key configurations include:

    • Test Directories: Tests are located in ./tests/playwright/specs and results are stored in ./tests/playwright/results.
    • Timeouts: Global test timeout is set to 30 seconds, and expect assertions have a 10-second timeout.
    • CI Behavior: On CI, forbidOnly is enabled, retries is set to 2, and workers is set to 1 to prevent parallel execution issues.
    • Reporting: Uses the html reporter, saving reports to ./tests/playwright/report.
    • Base URL: The baseURL is configured to http://localhost:4173/ to match the web server port.
    • Web Server: Automatically starts the local development server using yarn preview --port 4173 before running tests. It reuses the existing server if not running in a CI environment.
    import type { PlaywrightTestConfig } from '@playwright/test';
    import { devices } from '@playwright/test';
    import { resolve } from 'path';
    
    const playwrightDir = (partialPath: string) =>
      resolve(__dirname, './tests/playwright', partialPath);
    
    const WEB_SERVER_PORT = 4173;
    
    const config: PlaywrightTestConfig = {
      testDir: playwrightDir('./specs'),
      outputDir: playwrightDir('./results'),
      timeout: 30 * 1000,
      expect: {
        timeout: 10000,
      },
      forbidOnly: !!process.env.CI,
      retries: process.env.CI ? 2 : 1,
      workers: process.env.CI ? 1 : undefined,
      reporter: [
        [
          'html',
          {
            outputFolder: playwrightDir('./report'),
            open: process.env.CI ? 'never' : 'on-failure',
          },
        ],
      ],
      use: {
        actionTimeout: 0,
        baseURL: `http://localhost:${WEB_SERVER_PORT}/`,
        trace: 'retain-on-failure',
      },
      projects: [
        {
          name: 'chromium',
          use: { ...devices['Desktop Chrome'] },
        },
        {
          name: 'firefox',
          use: { ...devices['Desktop Firefox'] },
        },
        {
          name: 'webkit',
          use: { ...devices['Desktop Safari'] },
        },
      ],
      webServer: {
        command: `yarn preview --port ${WEB_SERVER_PORT}`,
        port: WEB_SERVER_PORT,
        reuseExistingServer: !process.env.CI,
      },
    };
    
    export default config;
  10. Configure Playwright for Vue with MSW

    main

    When using playwright-msw in a Vue project, you must configure the playwright.config.ts file to ensure the test runner can communicate with your local development server. Key configurations include setting the baseURL to match your webServer port and defining the webServer command to build and preview your application before tests run.

    In the provided example, the webServer is configured to run yarn run build && yarn run preview on port 4173. The use.baseURL is set to http://localhost:4173 to ensure Playwright directs requests to the correct local instance.

    import { PlaywrightTestConfig } from '@playwright/test';
    import { resolve } from 'path';
    
    const WEB_SERVER_PORT = 4173;
    
    const testDir = (partialPath?: string) =>
      resolve(__dirname, './test', partialPath);
    
    const config: PlaywrightTestConfig = {
      testDir: testDir('./specs'),
      outputDir: testDir('./results'),
      timeout: 30 * 1000,
      expect: {
        timeout: 10000,
      },
      forbidOnly: !!process.env.CI,
      retries: process.env.CI ? 2 : 0,
      workers: process.env.CI ? 1 : undefined,
      reporter: [
        [
          'html',
          {
            outputFolder: testDir('./report'),
            open: process.env.CI ? 'never' : 'on-failure',
          },
        ],
      ],
      use: {
        baseURL: `http://localhost:${WEB_SERVER_PORT}`,
        trace: 'retain-on-failure',
      },
      webServer: {
        command: 'yarn run build && yarn run preview',
        url: `http://localhost:${WEB_SERVER_PORT}`,
        timeout: 10000,
      },
    };
    
    export default config;