From 7ae3e7efa1522d75d9bddfc49c19b723afc1e358 Mon Sep 17 00:00:00 2001 From: Warren Day Date: Thu, 5 Sep 2024 15:57:06 +0100 Subject: [PATCH] added test for compression handler --- package.json | 3 +- src/helpers/gzip.ts | 2 - src/helpers/networkHelpers.test.ts | 67 +++++++++++++++++++++++++----- src/hooks/useNetworkMonitor.ts | 30 ++++++++----- yarn.lock | 5 +++ 5 files changed, 82 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index fc344a9..6c60133 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,8 @@ "tailwindcss": "^3.0.23", "typescript": "^4.0.3", "util": "^0.12.5", - "utility-types": "^3.11.0" + "utility-types": "^3.11.0", + "web-streams-polyfill": "^4.0.0" }, "_resolutions_comment_": "https://stackoverflow.com/a/71855781/2573621", "resolutions": { diff --git a/src/helpers/gzip.ts b/src/helpers/gzip.ts index 8987d49..5a3e006 100644 --- a/src/helpers/gzip.ts +++ b/src/helpers/gzip.ts @@ -42,8 +42,6 @@ export const decompress = async ( new (window as any).DecompressionStream(compressionType) ) - console.log({ raw, uint8Array }) - // Convert the decompressed stream back to a Uint8Array const decompressedArrayBuffer = await new Response( decompressedStream diff --git a/src/helpers/networkHelpers.test.ts b/src/helpers/networkHelpers.test.ts index bfe040a..8a85dfa 100644 --- a/src/helpers/networkHelpers.test.ts +++ b/src/helpers/networkHelpers.test.ts @@ -1,5 +1,4 @@ import { DeepPartial } from 'utility-types' -import { TextEncoder } from 'util' import { IHeader, getRequestBody, @@ -9,6 +8,17 @@ import { } from './networkHelpers' import dedent from 'dedent' +// Unable to test actual gzip decompression as the DecompressionStream is not available in JSDOM +// and the Response object from fetch is missing parts of the steam API +jest.mock('../helpers/gzip', () => ({ + __esModule: true, + decompress: (val: chrome.webRequest.UploadData[]) => { + return new Promise((resolve) => { + resolve(val[0].bytes) + }) + }, +})) + describe('networkHelpers.getRequestBodyFromUrl', () => { it('throws an error when no query is found in the URL', () => { expect(() => { @@ -116,7 +126,7 @@ describe('networkHelpers.getRequestBodyFromUrl', () => { }) describe('networkHelpers.matchWebAndNetworkRequest', () => { - it('matches a web request with a network request', () => { + it('matches a web request with a network request', async () => { const body = JSON.stringify({ query: 'query { user }', }) @@ -140,7 +150,7 @@ describe('networkHelpers.matchWebAndNetworkRequest', () => { }, } - const match = matchWebAndNetworkRequest( + const match = await matchWebAndNetworkRequest( networkRequest as any, webRequest as any, webRequestHeaders @@ -148,7 +158,7 @@ describe('networkHelpers.matchWebAndNetworkRequest', () => { expect(match).toBe(true) }) - it('does not match request with different URLs', () => { + it('does not match request with different URLs', async () => { const body = JSON.stringify({ query: 'query { user }', }) @@ -172,7 +182,7 @@ describe('networkHelpers.matchWebAndNetworkRequest', () => { }, } - const match = matchWebAndNetworkRequest( + const match = await matchWebAndNetworkRequest( networkRequest as any, webRequest as any, webRequestHeaders @@ -180,7 +190,7 @@ describe('networkHelpers.matchWebAndNetworkRequest', () => { expect(match).toBe(false) }) - it('does not match requests with different methods', () => { + it('does not match requests with different methods', async () => { const body = JSON.stringify({ query: 'query { user }', }) @@ -204,7 +214,7 @@ describe('networkHelpers.matchWebAndNetworkRequest', () => { }, } - const match = matchWebAndNetworkRequest( + const match = await matchWebAndNetworkRequest( networkRequest as any, webRequest as any, webRequestHeaders @@ -212,7 +222,7 @@ describe('networkHelpers.matchWebAndNetworkRequest', () => { expect(match).toBe(false) }) - it('does not match requests with different bodies', () => { + it('does not match requests with different bodies', async () => { const webRequest: DeepPartial = { url: 'http://example1.com', method: 'POST', @@ -242,7 +252,7 @@ describe('networkHelpers.matchWebAndNetworkRequest', () => { }, } - const match = matchWebAndNetworkRequest( + const match = await matchWebAndNetworkRequest( networkRequest as any, webRequest as any, webRequestHeaders @@ -275,7 +285,7 @@ describe('networkHelpers.getRequestBodyFromMultipartFormData', () => { }) describe('networkHelpers.getRequestBody', () => { - it('returns request body from a multipart form data request', () => { + it('returns request body from a multipart form data request', async () => { const details: Partial = { requestBody: { raw: [ @@ -296,7 +306,7 @@ describe('networkHelpers.getRequestBody', () => { }, ] - const result = getRequestBody(details as any, headers) + const result = await getRequestBody(details as any, headers) expect(result).toEqual( JSON.stringify({ @@ -313,4 +323,39 @@ describe('networkHelpers.getRequestBody', () => { }) ) }) + + it('returns request body from a compressed request', async () => { + const details: Partial = { + requestBody: { + raw: [ + { + bytes: new TextEncoder().encode( + JSON.stringify({ + query: 'query { user }', + }) + ), + }, + ], + }, + } + + const headers: IHeader[] = [ + { + name: 'content-type', + value: 'application/json', + }, + { + name: 'content-encoding', + value: 'deflate', + }, + ] + + const result = await getRequestBody(details as any, headers) + + expect(result).toEqual( + JSON.stringify({ + query: 'query { user }', + }) + ) + }) }) diff --git a/src/hooks/useNetworkMonitor.ts b/src/hooks/useNetworkMonitor.ts index a378e87..984091f 100644 --- a/src/hooks/useNetworkMonitor.ts +++ b/src/hooks/useNetworkMonitor.ts @@ -94,17 +94,21 @@ const findMatchingWebRequest = async ( webRequests: IIncompleteNetworkRequest[], details: chrome.devtools.network.Request ) => { - const res = await Promise.all( - webRequests.map(async (webRequest) => { - const isMatch = await matchWebAndNetworkRequest( - details, - webRequest.native?.webRequest, - webRequest.request?.headers || [] - ) - return isMatch ? webRequest : null - }) + const match = await Promise.all( + webRequests + // Don't target requests that already have a response + .filter((webRequest) => !webRequest.response) + .map(async (webRequest) => { + const isMatch = await matchWebAndNetworkRequest( + details, + webRequest.native?.webRequest, + webRequest.request?.headers || [] + ) + return isMatch ? webRequest : null + }) + .filter((r) => r) ) - return res.filter((r) => r)[0] + return match[0] } export const useNetworkMonitor = (): [ @@ -336,9 +340,13 @@ export const useNetworkMonitor = (): [ return onRequestFinished(handleRequestFinished) }, [handleRequestFinished]) - // Only return complete networkRequests. + // Only return webRequests where the request portion is complete. + // Since we build up the data from multiple events. We only want + // to display results that have enough data to be useful. const completeWebRequests = webRequests.filter(isRequestComplete) // @ts-ignore + // Ignored as completeWebRequests is readonly. Need to update type + // across app. return [completeWebRequests, clearWebRequests] as const } diff --git a/yarn.lock b/yarn.lock index eb4c5cb..5945ebb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9565,6 +9565,11 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +web-streams-polyfill@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0.tgz#74cedf168339ee6e709532f76c49313a8c7acdac" + integrity sha512-0zJXHRAYEjM2tUfZ2DiSOHAa2aw1tisnnhU3ufD57R8iefL+DcdJyRBRyJpG+NUimDgbTI/lH+gAE1PAvV3Cgw== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"