From 37a97c4201b3cc93a03513d6e0185e307711b642 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 7 Jan 2022 11:22:01 -0800 Subject: [PATCH] feat(tracing): capture network failures (#11237) --- .../src/server/supplements/har/har.ts | 1 + .../src/server/supplements/har/harTracer.ts | 12 ++++++++++ .../traceViewer/ui/networkResourceDetails.tsx | 22 ++++++++++++++----- tests/tracing.spec.ts | 11 ++++++++++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/playwright-core/src/server/supplements/har/har.ts b/packages/playwright-core/src/server/supplements/har/har.ts index 2e003b9bbe..82389c19fb 100644 --- a/packages/playwright-core/src/server/supplements/har/har.ts +++ b/packages/playwright-core/src/server/supplements/har/har.ts @@ -85,6 +85,7 @@ export type Response = { headersSize: number; bodySize: number; _transferSize: number; + _failureText?: string }; export type Cookie = { diff --git a/packages/playwright-core/src/server/supplements/har/harTracer.ts b/packages/playwright-core/src/server/supplements/har/harTracer.ts index 07cfdf84ee..4233f1744b 100644 --- a/packages/playwright-core/src/server/supplements/har/harTracer.ts +++ b/packages/playwright-core/src/server/supplements/har/harTracer.ts @@ -64,6 +64,7 @@ export class HarTracer { eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, (page: Page) => this._ensurePageEntry(page)), eventsHelper.addEventListener(this._context, BrowserContext.Events.Request, (request: network.Request) => this._onRequest(request)), eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFinished, ({ request, response }) => this._onRequestFinished(request, response).catch(() => {})), + eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFailed, request => this._onRequestFailed(request)), eventsHelper.addEventListener(this._context, BrowserContext.Events.Response, (response: network.Response) => this._onResponse(response)), eventsHelper.addEventListener(this._context.fetchRequest, APIRequestContext.Events.Request, (event: APIRequestEvent) => this._onAPIRequest(event)), eventsHelper.addEventListener(this._context.fetchRequest, APIRequestContext.Events.RequestFinished, (event: APIRequestFinishedEvent) => this._onAPIRequestFinished(event)), @@ -264,6 +265,17 @@ export class HarTracer { })); } + private async _onRequestFailed(request: network.Request) { + const harEntry = this._entryForRequest(request); + if (!harEntry) + return; + + if (request._failureText !== null) + harEntry.response._failureText = request._failureText; + if (this._started) + this._delegate.onEntryFinished(harEntry); + } + private _storeResponseContent(buffer: Buffer | undefined, content: har.Content) { if (!buffer) { content.size = 0; diff --git a/packages/playwright-core/src/web/traceViewer/ui/networkResourceDetails.tsx b/packages/playwright-core/src/web/traceViewer/ui/networkResourceDetails.tsx index 37126fe988..77b03a5452 100644 --- a/packages/playwright-core/src/web/traceViewer/ui/networkResourceDetails.tsx +++ b/packages/playwright-core/src/web/traceViewer/ui/networkResourceDetails.tsx @@ -106,16 +106,26 @@ export const NetworkResourceDetails: React.FunctionComponent<{ if (charset) contentType = charset[1]; - return
setSelected(index)}> - + const renderTitle = () => { + if (resource.response._failureText) { + return
+
{resource.response._failureText}
+
{resource.request.method}
+
{resource.request.url}
+
; + } else { + return
{resource.response.status}
{resource.request.method}
{resourceName}
{contentType}
-
- } body={ +
; + } + }; + + return
setSelected(index)}> +
URL
{resource.request.url}
diff --git a/tests/tracing.spec.ts b/tests/tracing.spec.ts index c4291eec10..e575f69825 100644 --- a/tests/tracing.spec.ts +++ b/tests/tracing.spec.ts @@ -205,6 +205,17 @@ test('should collect sources', async ({ context, page, server }, testInfo) => { expect(sourceFile).toEqual(thisFile); }); +test('should record network failures', async ({ context, page, server }, testInfo) => { + await context.tracing.start({ snapshots: true }); + await page.route('**/*', route => route.abort('connectionaborted')); + await page.goto(server.EMPTY_PAGE).catch(e => {}); + await context.tracing.stop({ path: testInfo.outputPath('trace1.zip') }); + + const { events } = await parseTrace(testInfo.outputPath('trace1.zip')); + const requestEvent = events.find(e => e.type === 'resource-snapshot' && !!e.snapshot.response._failureText); + expect(requestEvent).toBeTruthy(); +}); + test('should not stall on dialogs', async ({ page, context, server }) => { await context.tracing.start({ screenshots: true, snapshots: true }); await page.goto(server.EMPTY_PAGE);