From 09bbca328522ec8dac1877da1724766ef9173a88 Mon Sep 17 00:00:00 2001 From: nilshah98 Date: Mon, 27 Nov 2023 11:06:00 +0530 Subject: [PATCH 1/5] feat: added new config discovery.captureServiceWorker --- packages/core/src/config.js | 5 +++++ packages/core/src/discovery.js | 1 + packages/core/src/snapshot.js | 1 + packages/core/types/index.d.ts | 1 + 4 files changed, 8 insertions(+) diff --git a/packages/core/src/config.js b/packages/core/src/config.js index ce1d5bef0..ade14bcf7 100644 --- a/packages/core/src/config.js +++ b/packages/core/src/config.js @@ -167,6 +167,10 @@ export const configSchema = { disableCache: { type: 'boolean' }, + captureServiceWorker: { + type: 'boolean', + default: false + }, requestHeaders: { type: 'object', normalize: false, @@ -249,6 +253,7 @@ export const snapshotSchema = { requestHeaders: { $ref: '/config/discovery#/properties/requestHeaders' }, authorization: { $ref: '/config/discovery#/properties/authorization' }, disableCache: { $ref: '/config/discovery#/properties/disableCache' }, + captureServiceWorker: { $ref: '/config/discovery#/properties/captureServiceWorker' }, userAgent: { $ref: '/config/discovery#/properties/userAgent' }, devicePixelRatio: { $ref: '/config/discovery#/properties/devicePixelRatio' } } diff --git a/packages/core/src/discovery.js b/packages/core/src/discovery.js index b0e4ca885..8856003be 100644 --- a/packages/core/src/discovery.js +++ b/packages/core/src/discovery.js @@ -52,6 +52,7 @@ function debugSnapshotOptions(snapshot) { debugProp(snapshot, 'discovery.requestHeaders', JSON.stringify); debugProp(snapshot, 'discovery.authorization', JSON.stringify); debugProp(snapshot, 'discovery.disableCache'); + debugProp(snapshot, 'discovery.captureServiceWorker'); debugProp(snapshot, 'discovery.userAgent'); debugProp(snapshot, 'clientInfo'); debugProp(snapshot, 'environmentInfo'); diff --git a/packages/core/src/snapshot.js b/packages/core/src/snapshot.js index a739f4ec9..0a5459852 100644 --- a/packages/core/src/snapshot.js +++ b/packages/core/src/snapshot.js @@ -114,6 +114,7 @@ function getSnapshotOptions(options, { config, meta }) { requestHeaders: config.discovery.requestHeaders, authorization: config.discovery.authorization, disableCache: config.discovery.disableCache, + captureServiceWorker: config.discovery.captureServiceWorker, userAgent: config.discovery.userAgent } }, options], (path, prev, next) => { diff --git a/packages/core/types/index.d.ts b/packages/core/types/index.d.ts index 79b170316..3753fb25c 100644 --- a/packages/core/types/index.d.ts +++ b/packages/core/types/index.d.ts @@ -18,6 +18,7 @@ interface DiscoveryOptions { authorization?: AuthCredentials; allowedHostnames?: string[]; disableCache?: boolean; + captureServiceWorker?: boolean; } interface DiscoveryLaunchOptions { From 21bbb47dfac55a1af91cef3828484c90f605102e Mon Sep 17 00:00:00 2001 From: nilshah98 Date: Mon, 27 Nov 2023 16:01:00 +0530 Subject: [PATCH 2/5] feat: use captureServiceWorker config to capture original requests --- packages/core/src/discovery.js | 1 + packages/core/src/network.js | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/core/src/discovery.js b/packages/core/src/discovery.js index 8856003be..6d88e104a 100644 --- a/packages/core/src/discovery.js +++ b/packages/core/src/discovery.js @@ -289,6 +289,7 @@ export function createDiscoveryQueue(percy) { requestHeaders: snapshot.discovery.requestHeaders, authorization: snapshot.discovery.authorization, userAgent: snapshot.discovery.userAgent, + captureServiceWorker: snapshot.discovery.captureServiceWorker, meta: snapshot.meta, // enable network inteception diff --git a/packages/core/src/network.js b/packages/core/src/network.js index a6f9ad00a..e3397d020 100644 --- a/packages/core/src/network.js +++ b/packages/core/src/network.js @@ -37,6 +37,7 @@ export class Network { this.timeout = options.networkIdleTimeout ?? 100; this.authorization = options.authorization; this.requestHeaders = options.requestHeaders ?? {}; + this.captureServiceWorker = options.captureServiceWorker ?? false; this.userAgent = options.userAgent ?? // by default, emulate a non-headless browser page.session.browser.version.userAgent.replace('Headless', ''); @@ -54,7 +55,7 @@ export class Network { let commands = [ session.send('Network.enable'), - session.send('Network.setBypassServiceWorker', { bypass: true }), + session.send('Network.setBypassServiceWorker', { bypass: !this.captureServiceWorker}), session.send('Network.setCacheDisabled', { cacheDisabled: true }), session.send('Network.setUserAgentOverride', { userAgent: this.userAgent }), session.send('Network.setExtraHTTPHeaders', { headers: this.requestHeaders }) @@ -178,13 +179,16 @@ export class Network { // Called when a request will be sent. If the request has already been intercepted, handle it; // otherwise set it to be pending until it is paused. _handleRequestWillBeSent = async event => { - let { requestId, request } = event; + let { requestId, request, type } = event; // do not handle data urls if (request.url.startsWith('data:')) return; if (this.intercept) { this.#pending.set(requestId, event); + if (this.captureServiceWorker) { + await this._handleRequest(undefined, {...event, resourceType: type, interceptId: requestId}, true); + } } // release request // note: we are releasing this, even if intercept is not set for network.js @@ -195,7 +199,7 @@ export class Network { // Called when a pending request is paused. Handles associating redirected requests with // responses and calls this.onrequest with request info and callbacks to continue, respond, // or abort a request. One of the callbacks is required to be called and only one. - _handleRequest = async (session, event) => { + _handleRequest = async (session, event, serviceWorker = false) => { let { request, requestId, interceptId, resourceType } = event; let redirectChain = []; @@ -213,7 +217,9 @@ export class Network { request.redirectChain = redirectChain; this.#requests.set(requestId, request); - await sendResponseResource(this, request, session); + if(!serviceWorker){ + await sendResponseResource(this, request, session); + } } // Called when a response has been received for a specific request. Associates the response with From 7db90934c366230e99fd2b68eef931fa541073df Mon Sep 17 00:00:00 2001 From: nilshah98 Date: Mon, 27 Nov 2023 16:02:32 +0530 Subject: [PATCH 3/5] chore: lint fix --- packages/core/src/network.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/network.js b/packages/core/src/network.js index e3397d020..6d86b7f62 100644 --- a/packages/core/src/network.js +++ b/packages/core/src/network.js @@ -55,7 +55,7 @@ export class Network { let commands = [ session.send('Network.enable'), - session.send('Network.setBypassServiceWorker', { bypass: !this.captureServiceWorker}), + session.send('Network.setBypassServiceWorker', { bypass: !this.captureServiceWorker }), session.send('Network.setCacheDisabled', { cacheDisabled: true }), session.send('Network.setUserAgentOverride', { userAgent: this.userAgent }), session.send('Network.setExtraHTTPHeaders', { headers: this.requestHeaders }) @@ -187,7 +187,7 @@ export class Network { if (this.intercept) { this.#pending.set(requestId, event); if (this.captureServiceWorker) { - await this._handleRequest(undefined, {...event, resourceType: type, interceptId: requestId}, true); + await this._handleRequest(undefined, { ...event, resourceType: type, interceptId: requestId }, true); } } // release request @@ -217,7 +217,7 @@ export class Network { request.redirectChain = redirectChain; this.#requests.set(requestId, request); - if(!serviceWorker){ + if (!serviceWorker) { await sendResponseResource(this, request, session); } } From ac51d95a07973fdf74f0253a07e038f646918b42 Mon Sep 17 00:00:00 2001 From: nilshah98 Date: Tue, 28 Nov 2023 07:53:21 +0530 Subject: [PATCH 4/5] test: added spec for captureServiceWorker --- packages/core/test/discovery.test.js | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/core/test/discovery.test.js b/packages/core/test/discovery.test.js index 490b8387a..eb70cc362 100644 --- a/packages/core/test/discovery.test.js +++ b/packages/core/test/discovery.test.js @@ -1970,4 +1970,68 @@ describe('Discovery', () => { }); }); }); + + describe('Service Worker =>', () => { + it('captures original request', async () => { + server.reply('/sw.js', () => [200, 'text/javascript', dedent` + const fetchUpstream = async(request) => { + return await fetch(request.clone()); + } + + self.addEventListener('fetch', (event) => { + const { request } = event + event.respondWith(fetchUpstream(request)); + }); + + self.addEventListener("activate", (event) => { + event.waitUntil(clients.claim()); + }); + `]); + + server.reply('/app.js', () => [200, 'text/javascript', dedent` + const registerServiceWorker = async () => { + await navigator.serviceWorker.register('sw.js',{ scope: './', }); + }; + + await registerServiceWorker(); + + // create and insert image element which will be intercepted and resolved by service worker + // adding a sleep of 1s for service worker to get activated + await new Promise(r => setTimeout(r, 1000)); + var img = document.createElement('img'); + img.id = 'injected-image'; + img.src = './img.gif'; + document.getElementById('container').appendChild(img); + `]); + + server.reply('/', () => [200, 'text/html', dedent` + +
+ + + `]); + + await percy.snapshot({ + name: 'first service worker snapshot', + url: 'http://localhost:8000', + waitForSelector: '#injected-image', + discovery: { + captureServiceWorker: true + } + }); + + await percy.idle(); + + let paths = server.requests.map(r => r[0]); + expect(paths).toContain('/img.gif'); + expect(captured).toContain(jasmine.arrayContaining([ + jasmine.objectContaining({ + id: sha256hash(pixel), + attributes: jasmine.objectContaining({ + 'resource-url': 'http://localhost:8000/img.gif' + }) + }) + ])); + }); + }); }); From 870f763d110e2cb81c4aa0b177b63ff7945a4a78 Mon Sep 17 00:00:00 2001 From: nilshah98 Date: Tue, 28 Nov 2023 15:50:41 +0530 Subject: [PATCH 5/5] chore: rename captureServiceWorker -> captureMockedServiceWorker --- packages/core/src/config.js | 4 ++-- packages/core/src/discovery.js | 4 ++-- packages/core/src/network.js | 6 +++--- packages/core/src/snapshot.js | 2 +- packages/core/test/discovery.test.js | 2 +- packages/core/types/index.d.ts | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/core/src/config.js b/packages/core/src/config.js index ade14bcf7..39a8f68ab 100644 --- a/packages/core/src/config.js +++ b/packages/core/src/config.js @@ -167,7 +167,7 @@ export const configSchema = { disableCache: { type: 'boolean' }, - captureServiceWorker: { + captureMockedServiceWorker: { type: 'boolean', default: false }, @@ -253,7 +253,7 @@ export const snapshotSchema = { requestHeaders: { $ref: '/config/discovery#/properties/requestHeaders' }, authorization: { $ref: '/config/discovery#/properties/authorization' }, disableCache: { $ref: '/config/discovery#/properties/disableCache' }, - captureServiceWorker: { $ref: '/config/discovery#/properties/captureServiceWorker' }, + captureMockedServiceWorker: { $ref: '/config/discovery#/properties/captureMockedServiceWorker' }, userAgent: { $ref: '/config/discovery#/properties/userAgent' }, devicePixelRatio: { $ref: '/config/discovery#/properties/devicePixelRatio' } } diff --git a/packages/core/src/discovery.js b/packages/core/src/discovery.js index 6d88e104a..a3a4fb6cf 100644 --- a/packages/core/src/discovery.js +++ b/packages/core/src/discovery.js @@ -52,7 +52,7 @@ function debugSnapshotOptions(snapshot) { debugProp(snapshot, 'discovery.requestHeaders', JSON.stringify); debugProp(snapshot, 'discovery.authorization', JSON.stringify); debugProp(snapshot, 'discovery.disableCache'); - debugProp(snapshot, 'discovery.captureServiceWorker'); + debugProp(snapshot, 'discovery.captureMockedServiceWorker'); debugProp(snapshot, 'discovery.userAgent'); debugProp(snapshot, 'clientInfo'); debugProp(snapshot, 'environmentInfo'); @@ -289,7 +289,7 @@ export function createDiscoveryQueue(percy) { requestHeaders: snapshot.discovery.requestHeaders, authorization: snapshot.discovery.authorization, userAgent: snapshot.discovery.userAgent, - captureServiceWorker: snapshot.discovery.captureServiceWorker, + captureMockedServiceWorker: snapshot.discovery.captureMockedServiceWorker, meta: snapshot.meta, // enable network inteception diff --git a/packages/core/src/network.js b/packages/core/src/network.js index 6d86b7f62..cff14b163 100644 --- a/packages/core/src/network.js +++ b/packages/core/src/network.js @@ -37,7 +37,7 @@ export class Network { this.timeout = options.networkIdleTimeout ?? 100; this.authorization = options.authorization; this.requestHeaders = options.requestHeaders ?? {}; - this.captureServiceWorker = options.captureServiceWorker ?? false; + this.captureMockedServiceWorker = options.captureMockedServiceWorker ?? false; this.userAgent = options.userAgent ?? // by default, emulate a non-headless browser page.session.browser.version.userAgent.replace('Headless', ''); @@ -55,7 +55,7 @@ export class Network { let commands = [ session.send('Network.enable'), - session.send('Network.setBypassServiceWorker', { bypass: !this.captureServiceWorker }), + session.send('Network.setBypassServiceWorker', { bypass: !this.captureMockedServiceWorker }), session.send('Network.setCacheDisabled', { cacheDisabled: true }), session.send('Network.setUserAgentOverride', { userAgent: this.userAgent }), session.send('Network.setExtraHTTPHeaders', { headers: this.requestHeaders }) @@ -186,7 +186,7 @@ export class Network { if (this.intercept) { this.#pending.set(requestId, event); - if (this.captureServiceWorker) { + if (this.captureMockedServiceWorker) { await this._handleRequest(undefined, { ...event, resourceType: type, interceptId: requestId }, true); } } diff --git a/packages/core/src/snapshot.js b/packages/core/src/snapshot.js index 0a5459852..5884a00ac 100644 --- a/packages/core/src/snapshot.js +++ b/packages/core/src/snapshot.js @@ -114,7 +114,7 @@ function getSnapshotOptions(options, { config, meta }) { requestHeaders: config.discovery.requestHeaders, authorization: config.discovery.authorization, disableCache: config.discovery.disableCache, - captureServiceWorker: config.discovery.captureServiceWorker, + captureMockedServiceWorker: config.discovery.captureMockedServiceWorker, userAgent: config.discovery.userAgent } }, options], (path, prev, next) => { diff --git a/packages/core/test/discovery.test.js b/packages/core/test/discovery.test.js index eb70cc362..4c9f40d68 100644 --- a/packages/core/test/discovery.test.js +++ b/packages/core/test/discovery.test.js @@ -2016,7 +2016,7 @@ describe('Discovery', () => { url: 'http://localhost:8000', waitForSelector: '#injected-image', discovery: { - captureServiceWorker: true + captureMockedServiceWorker: true } }); diff --git a/packages/core/types/index.d.ts b/packages/core/types/index.d.ts index 3753fb25c..a52b7ff09 100644 --- a/packages/core/types/index.d.ts +++ b/packages/core/types/index.d.ts @@ -18,7 +18,7 @@ interface DiscoveryOptions { authorization?: AuthCredentials; allowedHostnames?: string[]; disableCache?: boolean; - captureServiceWorker?: boolean; + captureMockedServiceWorker?: boolean; } interface DiscoveryLaunchOptions {