Search code examples
javascriptplaywrightplaywright-test

page.route for just one method in playwright


To mock routes in playwright, we can do a page.route, and that page.route will apply for every method of that route. That means that within page.route, you need to specify

test("Run a test", async ({ page }) => {
  page.route('endpoint', route => {
    switch (route.request().method()) {
      case 'GET':
        await route.fullfill(<get_response>)
      case 'POST':
        await route.fullfill(<post>)
      defaut:
        await route.continue()
    }
  })
})

If I need to then use some other response for another case, I can call page.route again for this route. But let's say I only need to change the POST response. If I were to do this:

test("Run a test", async ({ page }) => {
  page.route('endpoint', route => {
    switch (route.request().method()) {
      case 'GET':
        await route.fullfill(<get_response>)
      case 'POST':
        await route.fullfill(<post_response>)
      defaut:
        await route.continue()
    }
  })

  // perform some actions

  page.route('endpoint', () => {
    switch (route.request().method()) {
      case 'POST':
        await route.fullfill(<different_post_response>)
      defaut:
        await route.continue()
    }
  })
})

In this case, all methods of the routeed endpoint are overwritten, and a GET call to that endpoint would fall through the route.continue().

Is there a more precise way to mock just a single route method in playwright, without overwriting mocks for other methods of the same endpoint?

(An example would be like in cypress, where a user can cy.intercept({ url: 'url', method: METHOD }), which can be called multiple times with various arguments, but because the method is part of the call option params, there is no interference between methods.)


Solution

  • You can use route.fallback() to fall back to the previous handler. This lets you set a default handler for all methods, then override it for one method. In your case,

    // perform some actions
    
    page.route('endpoint', () => {
      switch (route.request().method()) {
        case 'POST':
          await route.fullfill(<different_post_response>)
        defaut:
          await route.fallback() // <-- handle any non-POST methods
      }
    })
    

    You can also use an object with request method handler functions, then set the functions to whatever you want them to be on demand:

    const endpointHandlers = {
      GET: route => route.fullfill({body: "foo"}),
      POST: route => route.fullfill({body: "bar"}),
      // ...
    };
    
    page.route("endpoint", route => {
      const handler = endpointHandlers[route.request.method()];
      return handler ? handler(route) : route.continue();
    });
    
    // perform some actions
    
    // change one handler on demand:
    endpointHandlers.GET = route => route.fullfill({body: "baz"});
    

    For more advanced cases where you need to handle a series of requests, handlers can be arrays of functions acting as queues that are shifted on every request.