-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Puppeteer adapter for Polly that works in web workers in order to test extensions #13870
Conversation
After some trial and error on this PR, I created The advantages of this new adapter over the earlier
What's left on
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skipped the console.logs and TODOs, this all looks really good!
…remove old polly puppeteer-adapter
The Polly adapter types definition (the Adapter base class that we use) is missing some types (namely the constructor argument and the entire I tried to fix the types by adding declarations, or inlining a fixed declaration, but I didn't find a working solution. I spent too much time on the problem already so I kept two |
* interception using the CDP "Fetch domain". | ||
*/ | ||
public async onConnect(): Promise<void> { | ||
console.log('CDP adapter connecting') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
console.log('CDP adapter connecting') |
@@ -96,7 +96,7 @@ export const createSharedIntegrationTestContext = async < | |||
directory, | |||
}: IntegrationTestOptions): Promise<IntegrationTestContext<TGraphQlOperations, TGraphQlOperationNames>> => { | |||
await driver.newPage() | |||
await driver.page.setRequestInterception(true) | |||
// await driver.page.setRequestInterception(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// await driver.page.setRequestInterception(true) |
test-puppeteer-adapter.ts
Outdated
@@ -0,0 +1,71 @@ | |||
import { Polly } from '@pollyjs/core' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this file be deleted?
* return a Promise of the Response for that request, which will be passed to | ||
* request.respond(). | ||
*/ | ||
public passthroughRequest(pollyRequest: PollyRequest & PollyRequestArguments): Promise<PollyResponse> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name of this method is a bit misleading. Its purpose is to create a brand new request given the pollyRequest
argument and return the response. This is used internally by both the record
and passthrough
handlers to get the response data for the given pollyRequest. The reason a duplicate request is necessary is because you don't want to respond to the original request until polly is done with it (i.e. via mutations with client side server events) and also the actual request could have been modified during the request
event which means you might be making a completely different request than the original.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the current implementation of this be an acceptable simplification if we don't make use of request mutation anywhere (we don't)? Or is there a reason that wouldn't work? Iirc the duplicate requests posed some problems because they can't be 1:1 identical in some regard but I don't fully remember
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the current implementation of this be an acceptable simplification if we don't make use of request mutation anywhere
I would say most likely not since this is how all adapters are currently expected to behave. I mostly see usages of the request
event when wanting to change auth headers or user credentials so I feel like this is pretty important to try incorporate. We could list this as a 3rd party adapter and document this as a caveat until we can get this working properly.
Iirc the duplicate requests posed some problems because they can't be 1:1 identical in some regard but I don't fully remember
The biggest reason we ran into issues was because we were making native browser fetch requests which were an issue when the intercepted request was made via XHR. I don't think you'll run into the same issues here but I'm sure there might be some other complexities as I'm not fully familiar with CDP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I see, I agree that it should be solved before we upstream this. Do you think there would be a problem with using this in our test suite this way though given that we know that we don't change auth headers or credentials etc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, i think it should be fine as long as you don't actually fully respond to the original request until respondToRequest
is called.
public async onConnect(): Promise<void> { | ||
this.cdpSession = await this.page.target().createCDPSession() | ||
|
||
// TODO: This is where we narrow down the interception with patterns. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also add a check to see if you're already intercepting requests on the page.
Fetch example:
this.assert(
'Running concurrent fetch adapters is unsupported, stop any running Polly instances.',
!context.fetch[IS_STUBBED] && !context.Request[IS_STUBBED]
);
requestId, | ||
responseCode: pollyResponse.statusCode, | ||
responseHeaders: headerObjectToHeaderEntries(headers), | ||
body: toBase64(body), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the body always have to be in base64? That will be unexpected for users that are expecting JSON or text. Can we detect if the body is utf8 via import { isBufferUtf8Representable } from '@pollyjs/utils'
?
Add a new
PuppeteerAdapter
for Polly for intercepting HTTP requests.Current status: this is a draft PR and includes a temporary test script (run with
ts-node test-puppeteer-adapter.ts
). There are still some errors that happen while intercepting some types of requests.Problem background
This solves the limitation that the default Puppeteer adapter for Polly does not work to intercept requests inside of web workers.
This limitation is blocking us from writing integration tests for Sourcegraph extensions.
Issues: #12194 #12180
PR which is blocked: #12195
Implementation
The new
PuppeteerAdapter
usespuppeteer-interceptor
which is a wrapper around the Chrome DevTools Protocol.It uses CDP's "Fetch domain" to intercept requests, which seems to be the best way to do it right now -- however this part of CDP is still flagged as
experimental.Actually, I don't see any mention in the docs of it being experimental, so that may have been outdated information.Alternatives to consider
Because
puppeteer-interceptor
makes it a bit difficult the follow the flow of the code, and because it's a relatively small wrapper around CDP calls, we could use CDP directly instead and it might result in a cleaner solution. This could be a next step after we get this current solution working properly.Update and resolution
The above is no longer current. I abandoned the
puppeteer-interceptor
approach and implemented the alternative instead. See the update comments on this PR for the details.