Skip to content

mihail-alexe-nutanix/playwright-request-mocker

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

playwright-request-mocker

Automatically generate and use network request mocks inside Playwright!

Features 🔎

Mocking your API requests takes too much precious development time, this library strives to make it effortless by:

  • Allowing you to declare just once the hook use, it finds the mock file;
  • If the mock file does not exist yet, it will open a Playwright's chromium tab, and record all your XHR requests and responses;
  • It'll automatically intercept all registered HTTP requests defined in the mock file for any future runs.

Install 👷

  1. Install the package:
  • npm install playwright-request-mocker -D
  • or yarn add playwright-request-mocker -D;
  1. Be sure to have @playwright/test also installed.

Using 🔌

  1. Add to a .spec file, inside a beforeEach or test method, the hook call useNetworkRecordMocks passing the test context page, identifier of the mock (only necessary if each test scenario has a different mock), and a route to be used by the recording tab if there is no mock file yet;

    • e.g.
import { useNetworkRecordMocks } from 'playwright-request-mocker';          // if using .mjs / .ts
// const { useNetworkRecordMocks } = require('playwright-request-mocker');  //if using .js

test.describe('Your test', () => {
  // If your network requests/responses are the same for every test scenario, define it here.
  test.beforeEach(async ({ page }) => {
    await useNetworkRecordMocks(page, {
      recordRoute: `${process.env.APP_URL}/page-you-are-testing`,
    });

    await page.goto(`${process.env.APP_URL}/page-you-are-testing`);
  });

  // else use it here if each test scenario expects different results.
  test('scenario1', async ({ page }) => {
    // It'll generate a new file if it does not exist (".spec.scenario1.mocks.json")
    // then it'll read it and mock all defined network requests.
    await useNetworkRecordMocks(page, {
      identifier: 'scenario1',
      recordRoute: `${process.env.APP_URL}/page-you-are-testing`
    });

    await page.goto(`${process.env.APP_URL}/page-you-are-testing`);

    //... your steps and asserts.
  });
});
  1. Run in debug mode;
  • PWDEBUG=console forces Playwright to run headed, disables the timeouts and shows the console helper.
// Linux/macOS
PWDEBUG=console npm run playwright test

// Windows on cmd.exe
set PWDEBUG=console
npm run playwright test

// Windows on PowerShell
$env:PWDEBUG="console"
npm run playwright test
  • If the mock file does not exist yet, it'll open a new tab and will be recording all the XHR requests as you navigate. When you think you recorded everything you needed, press the resume button in the playwright/test UI.
  • After it, or if the mock file ever exists, it will use the results to run your test scenario.

How it works ❓

  • useNetworkRecordMocks checks if there is a file [invoker-file-name].mocks.json (or [invoker-file-name].[identifier].mocks.json), if it doesn't exists, it'll open a chromiun tab using chrome's recordHAR option. When the Playwright debug mode is unpaused, it'll process such file removing every request other than XHR ones;
  • useNetworkRecordMocks will parse the existing file, and mock every request declared on it.

Different use cases 👣

  • For a given test scenario, I need to change the mocked value: there are 3 ways to achieve this, 1 being creating a new specific mock file at each scenario (by calling useNetworkRecordMocks inside each test with a identifier), and others being overriding the route response, like the following:

Overriding approach: for when the useNetworkRecordMocks call is inside the test scenario:

  import { useNetworkRecordMocks, mockRouteResponse } from 'playwright-request-mocker';

  // [...]

  test('my different scenario', async ({
    page,
  }) => {
    const errorMessage = 'error message I expect';

    await useNetworkRecordMocks(page, {
      recordRoute: `${process.env.APP_URL}/page-you-are-testing`,
      overrideResponses: {
        "/ENDPOINT_TO_CHANGE": { errors: [{ message: errorMessage }] }
      }
    });

    await page.goto(`${process.env.APP_URL}/page-you-are-testing`);

    await Promise.all([
      page.waitForRequest('**/ENDPOINT_TO_CHANGE'),
      fillForm(page),
    ]);

    await page.waitForSelector(`text=${errorMessage}`);
    const visible = await page.isVisible(`text=${errorMessage}`);
    expect(visible).toBeTruthy();
  });

Unrouting approach: for when the useNetworkRecordMocks call is inside a beforeEach:

test.describe('Test', () => {
  test.beforeEach(async ({ page }) => {
    await useNetworkRecordMocks(page, {
      recordRoute: `${process.env.APP_URL}/page-you-are-testing`,
    });

    await page.goto(`${process.env.APP_URL}/page-you-are-testing`);
  });

  test('my different scenario', async ({
    page
  }) => {
    const errorMessage = 'error message I expect';

    page.unroute('**/ENDPOINT_TO_CHANGE');
    mockRouteResponse(
      page,
      '**/ENDPOINT_TO_CHANGE',
      { errors: [{ message: errorMessage }] },
      400,
    );

    await Promise.all([
      page.waitForRequest('**/ENDPOINT_TO_CHANGE'),
      fillForm(page),
    ]);

    await page.waitForSelector(`text=${errorMessage}`);
    const visible = await page.isVisible(`text=${errorMessage}`);
    expect(visible).toBeTruthy();
  });
}
  • I want to assert the payload sent against what was expected: useNetworkRecordMocks returns every mocked request payload, you can use it to assert the values:
  test('my different scenario', async ({
    page,
  }) => {

    const requests = await useNetworkRecordMocks(
      page,
      {
        recordRoute: `${process.env.APP_URL}/test-route`,
      }
    );

    const [req] = await Promise.all([
      page.waitForRequest('**/ENDPOINT_TO_TEST'),
      fillForm(page),
    ]);

    const expectedPayload = requests.find(r => r.url.includes('ENDPOINT_TO_TEST'))?.requestData;

    expect(expectedPayload).toStrictEqual(req.postDataJSON());

  });
  • I already have a HAR recorded file that I want to use: as long as it follows the same name structure as the file that is using it, the lib will prioritize it over recording a new one to generate the new mocks.json file:
  /tests
  |- my-test.spec.js
  |- my-test.spec.har
  |- my-test.spec.identifier.har


Feel free to contribute, report bugs, or contact me.

About

Automatically generate and use network request mocks inside Playwright.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 94.6%
  • JavaScript 5.4%