chore: PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS and doc clarifications (#15688)
This commit is contained in:
parent
1d415312fe
commit
732b8f4760
|
|
@ -871,3 +871,13 @@ page.WebSocket += (_, ws) =>
|
|||
- [`event: WebSocket.close`]
|
||||
|
||||
<br/>
|
||||
|
||||
## Missing Network Events and Service Workers
|
||||
|
||||
Playwright's built-in [`method: BrowserContext.route`] and [`method: Page.route`] allow your tests to natively route requests and perform mocking and interception.
|
||||
|
||||
1. If you're using Playwright's native [`method: BrowserContext.route`] and [`method: Page.route`], and it appears network events are missing, disable Service Workers by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
|
||||
1. It might be that you are using a mock tool such as Mock Service Worker (MSW). While this tool works out of the box for mocking responses, it adds its own Service Worker that takes over the network requests, hence making them invisible to [`method: BrowserContext.route`] and [`method: Page.route`]. If you are interested in both network testing and mocking, consider using built-in [`method: BrowserContext.route`] and [`method: Page.route`] for [response mocking](#handle-requests).
|
||||
1. If you're interested in not solely using Service Workers for testing and network mocking, but in routing and listening for requests made by Service Workers themselves, please see [this experimental feature](https://github.com/microsoft/playwright/issues/15684).
|
||||
|
||||
<br/>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
---
|
||||
id: service-workers
|
||||
title: "Service Workers"
|
||||
id: service-workers-experimental
|
||||
title: "(Experimental) Service Worker Network Events"
|
||||
---
|
||||
|
||||
:::warning
|
||||
If you're looking to do general network mocking, routing, and interception, please see the [Network Guide](./network.md) first. Playwright provides built-in APIs for this use case that don't require the information below. However, if you're interested in requests made by Service Workers themselves, please read below.
|
||||
:::
|
||||
|
||||
[Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) provide a browser-native method of handling requests made by a page with the native [Fetch API (`fetch`)](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) along with other network-requested assets (like scripts, css, and images).
|
||||
|
||||
|
|
@ -10,20 +13,16 @@ They can act as a **network proxy** between the page and the external network to
|
|||
|
||||
Many sites that use Service Workers simply use them as a transparent optimization technique. While users might notice a faster experience, the app's implementation is unaware of their existence. Running the app with or without Service Workers enabled appears functionally equivalent.
|
||||
|
||||
**If your app uses Service Workers**, here's the scenarios that Playwright supports:
|
||||
## How to Enable
|
||||
|
||||
1. Testing the page exactly like a user would experience it. This works out of the box in all supported browsers.
|
||||
1. Test your page without a Service Worker. Set [`option: Browser.newContext.serviceWorkers`] to `'block'`. You can test your page as if no Service Worker was registered.
|
||||
1. Listen for and route network traffic via Playwright, whether it comes from a Service Worker or not. In Firefox and WebKit, set [`option: Browser.newContext.serviceWorkers`] to `'block'` to avoid Service Worker network traffic entirely. In Chromium, either block Service Workers or use [`method: BrowserContext.route`].
|
||||
1. (Chromium-only) Test your Service Worker implementation itself. Use [`method: BrowserContext.serviceWorkers`] to get access to the Service Worker and evaluate there.
|
||||
Playwright's inspection and routing of requests made by Service Workers are **experimental** and disabled by default.
|
||||
|
||||
Set the `PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS` environment variable to `1` (or any other value) to enable the feature. Only Chrome/Chromium are currently supported.
|
||||
|
||||
If you're using (or are interested in using this this feature), please comment on [this issue](https://github.com/microsoft/playwright/issues/15684) letting us know your use case.
|
||||
|
||||
## Service Worker Fetch
|
||||
|
||||
:::note
|
||||
The next sections are only currently supported when using Playwright with Chrome/Chromium. In Firefox and WebKit, if a Service Worker has a FetchEvent handler, Playwright will **not** emit Network events for all network traffic.
|
||||
:::
|
||||
|
||||
### Accessing Service Workers and Waiting for Activation
|
||||
|
||||
You can use [`method: BrowserContext.serviceWorkers`] to list the Service [Worker]s, or specifically watch for the Service [Worker] if you anticipate a page will trigger its [registration](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register):
|
||||
|
|
@ -367,7 +367,10 @@ export class CRNetworkManager {
|
|||
// For frame-level Requests that are handled by a Service Worker's fetch handler, we'll never get a requestPaused event, so we need to
|
||||
// manually create the request. In an ideal world, crNetworkManager would be able to know this on Network.requestWillBeSent, but there
|
||||
// is not enough metadata there.
|
||||
if (!request && event.response.fromServiceWorker) {
|
||||
//
|
||||
// PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS we guard with, since this would fix an old bug where, when using routing,
|
||||
// request would not be emitted to the user for requests made by a page with a SW (and fetch handler) registered
|
||||
if (!!process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS && !request && event.response.fromServiceWorker) {
|
||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
|
||||
const frame = requestWillBeSentEvent?.frameId ? this._page?._frameManager.frame(requestWillBeSentEvent.frameId) : null;
|
||||
if (requestWillBeSentEvent && frame) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import { headersArrayToObject } from '../../utils';
|
|||
|
||||
export class CRServiceWorker extends Worker {
|
||||
readonly _browserContext: CRBrowserContext;
|
||||
readonly _networkManager: CRNetworkManager;
|
||||
readonly _networkManager?: CRNetworkManager;
|
||||
private _session: CRSession;
|
||||
private _extraHTTPHeaders: types.HeadersArray | null = null;
|
||||
|
||||
|
|
@ -33,12 +33,13 @@ export class CRServiceWorker extends Worker {
|
|||
super(browserContext, url);
|
||||
this._session = session;
|
||||
this._browserContext = browserContext;
|
||||
this._networkManager = new CRNetworkManager(session, null, this, null);
|
||||
if (!!process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS)
|
||||
this._networkManager = new CRNetworkManager(session, null, this, null);
|
||||
session.once('Runtime.executionContextCreated', event => {
|
||||
this._createExecutionContext(new CRExecutionContext(session, event.context));
|
||||
});
|
||||
|
||||
if (this._isNetworkInspectionEnabled()) {
|
||||
if (this._networkManager && this._isNetworkInspectionEnabled()) {
|
||||
this._networkManager.initialize().catch(() => {});
|
||||
this.updateRequestInterception();
|
||||
this.updateExtraHTTPHeaders(true);
|
||||
|
|
@ -56,7 +57,7 @@ export class CRServiceWorker extends Worker {
|
|||
|
||||
const offline = !!this._browserContext._options.offline;
|
||||
if (!initial || offline)
|
||||
await this._networkManager.setOffline(offline);
|
||||
await this._networkManager?.setOffline(offline);
|
||||
}
|
||||
|
||||
async updateHttpCredentials(initial: boolean): Promise<void> {
|
||||
|
|
@ -65,7 +66,7 @@ export class CRServiceWorker extends Worker {
|
|||
|
||||
const credentials = this._browserContext._options.httpCredentials || null;
|
||||
if (!initial || credentials)
|
||||
await this._networkManager.authenticate(credentials);
|
||||
await this._networkManager?.authenticate(credentials);
|
||||
}
|
||||
|
||||
async updateExtraHTTPHeaders(initial: boolean): Promise<void> {
|
||||
|
|
@ -81,7 +82,7 @@ export class CRServiceWorker extends Worker {
|
|||
}
|
||||
|
||||
updateRequestInterception(): Promise<void> {
|
||||
if (!this._isNetworkInspectionEnabled())
|
||||
if (!this._networkManager || !this._isNetworkInspectionEnabled())
|
||||
return Promise.resolve();
|
||||
|
||||
return this._networkManager.setRequestInterception(this.needsRequestInterception()).catch(e => { });
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue