question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature] Allow request interception chaining (was 'based on request method')

See original GitHub issue

Currently page.route(url, handler, options)/browserContext.route(url, handler, options) intercept routes based on url alone. It would be great if we have the ability to intercept based on any request properties like method, header, etc.

Use case

A use case would be the common REST scenario where we want to intercept both GET /items and POST /items.

Very simplified example:

test('items', async ({ page }) => {
  // Intercept GET /api/items
  await page.route('/api/items', route => route.fulfill({ body: JSON.stringify([{ id: 1, name: 'Item 1' }]) }))
  
  // Intercept POST /api/items
  await page.route('/api/items', route => route.fulfill({ body: JSON.stringify({ id: 2, name: 'Item 2' }) }))
  
  await page.goto('/items')
  //  expect "Item 1" to be in the list
  
  await page.click('role=button[name="New item"]')
  // expect "Item 2" to be created in the list
})

The “matcher”(url<string|RegExp|function(URL):boolean>) doesn’t allow for any matching on the request itself - only the request url. The “response”(handler<function(Route, Request)>) doesn’t allow us to “skip” to the next interceptor - as the comment says in the source code // There is no chaining, first handler wins..

Existing workarounds

  1. Add single interceptor with conditional handler:
await page.route('/api/items', (route, request) => { 
  if (request.method() === 'GET') {
    return route.fulfill({ body: JSON.stringify([{ id: 1, name: 'Item 1' }]) }) 
  } 
  
  if (request.method() === 'POST') {
    return route.fulfill({ body: JSON.stringify({ id: 2, name: 'Item 2' })
  } 
  
  return route.continue()
})

While this is enough in some cases, it’s not very atomic and reusable. My idea is to make these a bit more generic and use them in fixtures - making sure that i can easily target just the request i need. Also, the idea of having the matching logic in the handler instead of the matcher just seems wrong.

  1. Use options.times and order the interceptors based on their runtime execution. This seems very fragile and really not ideal.

Proposal

Even though the contribution guide says “Expose as little information as needed. When in doubt, don’t expose new information.”, i feel like we should have an escape hatch in the “matcher” function. Two options:

  • Non-breaking change: adding the request as a second parameter to the url function: url<string|RegExp|function(URL, request):boolean> Not the best API but that’s the price of non-breaking i guess.
  • Breaking change: replacing the URL param with the request param in the url function: url<string|RegExp|function(request):boolean> This seems like a very robust and flexible way of route matching but it does break BC.

It’s a bit unfortunate that we can’t reuse the standard glob/regex url matching when we use the function. This is how i imagine it could look like:

await page.route(
  (url, request) => request.method() === 'GET' && minimatch('/api/items', url.toSting()), 
  route => route.fulfill({ body: JSON.stringify([{ id: 1, name: 'Item 1' }]) }) 
)

await page.route(
  (url, request) => request.method() === 'POST' && minimatch('/api/items', url.toSting()), 
  route => route.fulfill({ body: JSON.stringify({ id: 2, name: 'Item 2' })
)

I think that having a convenient way to match by method(in addition to url) would be helpful in a lot of situations. What do you guys think? Do you have other APIs in mind to achieve this or do you think the current workaround methods are good enough?

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:9 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
pavelfeldmancommented, Jun 9, 2022

~@mrmckeb would you mind filing a request for bubbling? That one I think we should implement, we were waiting for the user request.~

Ah, after reading last @purepear update it seems like original request is also about chaining! Let’s fix it under this issue then.

1reaction
mrmckebcommented, Jun 9, 2022

We’ve started hitting these limitations too, specifically “There is no chaining, first handler wins.”.

Engineers, after using tools like Cypress, expect that if they use route.continue(), the request will “bubble up” to the next handler. They also expect to be able to handle POST and GET separately.

We’ll add more custom tooling to our project to help mitigate the issue, but an implementation on the Playwright side would be amazing.

I quite like the interface that MSW offers (as an example): https://mswjs.io/

Read more comments on GitHub >

github_iconTop Results From Across the Web

Request Interception - Puppeteer
Once request interception is enabled, every request will stall unless it's continued, responded or aborted.
Read more >
Chapter 4. Intercepting network requests - Progressive Web ...
Some modern browsers have an opt-in to a feature that allows users to save data. If the feature is enabled, the browser adds...
Read more >
Angular Interceptors to Manage HTTP Requests
Learn how to use Angular interceptors to manage HTTP requests including JWT authorization , caching and logging.
Read more >
Angular HTTP Interceptors : Multiple Interceptors and 6 Code ...
Interceptors are used in Angular to Intercept HttpRequest/HttpResponse . We can use interceptors to log HttpRequest/HttpResponse ...
Read more >
How to intercept request in Puppeteer before current page is ...
We need to capture all outbound routes from a page. Some of them may not be implemented using link elements <a src="..."> but...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found