From 09fad7dc16e59403df300bb692473d81eae3e0ce Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Tue, 22 Oct 2024 15:44:09 +0200 Subject: [PATCH] fix: support parent and child frames with msw --- src/browser/setupWorker/glossary.ts | 7 +++- .../setupWorker/start/utils/enableMocking.ts | 3 +- .../start/utils/printStartMessage.ts | 7 ++++ src/mockServiceWorker.js | 11 +++++- .../iframe-isolated-response.mocks.ts | 2 +- .../iframe-isolated-response.test.ts | 2 +- .../iframe/multiple-workers/child.mocks.ts | 10 ++++++ .../iframe-multiple-workers.test.ts | 34 +++++++++++++++++++ .../iframe/multiple-workers/parent.mocks.ts | 7 ++++ 9 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/child.mocks.ts create mode 100644 test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/iframe-multiple-workers.test.ts create mode 100644 test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/parent.mocks.ts diff --git a/src/browser/setupWorker/glossary.ts b/src/browser/setupWorker/glossary.ts index a51d0f73c..70951ede9 100644 --- a/src/browser/setupWorker/glossary.ts +++ b/src/browser/setupWorker/glossary.ts @@ -53,7 +53,12 @@ export type ServiceWorkerIncomingResponse = Pick< * Map of the events that can be received from the Service Worker. */ export interface ServiceWorkerIncomingEventsMap { - MOCKING_ENABLED: boolean + MOCKING_ENABLED: { + client: { + id: string + frameType: string + } + } INTEGRITY_CHECK_RESPONSE: { packageVersion: string checksum: string diff --git a/src/browser/setupWorker/start/utils/enableMocking.ts b/src/browser/setupWorker/start/utils/enableMocking.ts index c0f19f314..f895ee2d5 100644 --- a/src/browser/setupWorker/start/utils/enableMocking.ts +++ b/src/browser/setupWorker/start/utils/enableMocking.ts @@ -10,7 +10,7 @@ export async function enableMocking( options: StartOptions, ) { context.workerChannel.send('MOCK_ACTIVATE') - await context.events.once('MOCKING_ENABLED') + const { payload } = await context.events.once('MOCKING_ENABLED') // Warn the developer on multiple "worker.start()" calls. // While this will not affect the worker in any way, @@ -28,5 +28,6 @@ export async function enableMocking( quiet: options.quiet, workerScope: context.registration?.scope, workerUrl: context.worker?.scriptURL, + client: payload.client, }) } diff --git a/src/browser/setupWorker/start/utils/printStartMessage.ts b/src/browser/setupWorker/start/utils/printStartMessage.ts index e49a77046..1d087fc14 100644 --- a/src/browser/setupWorker/start/utils/printStartMessage.ts +++ b/src/browser/setupWorker/start/utils/printStartMessage.ts @@ -1,3 +1,4 @@ +import type { ServiceWorkerIncomingEventsMap } from 'browser/setupWorker/glossary' import { devUtils } from '~/core/utils/internal/devUtils' export interface PrintStartMessageArgs { @@ -5,6 +6,7 @@ export interface PrintStartMessageArgs { message?: string workerUrl?: string workerScope?: string + client?: ServiceWorkerIncomingEventsMap['MOCKING_ENABLED']['client'] } /** @@ -41,6 +43,11 @@ export function printStartMessage(args: PrintStartMessageArgs = {}) { console.log('Worker scope:', args.workerScope) } + if (args.client) { + // eslint-disable-next-line no-console + console.log('Client ID: %s (%s)', args.client.id, args.client.frameType) + } + // eslint-disable-next-line no-console console.groupEnd() } diff --git a/src/mockServiceWorker.js b/src/mockServiceWorker.js index 04132a385..18592f3d5 100644 --- a/src/mockServiceWorker.js +++ b/src/mockServiceWorker.js @@ -62,7 +62,12 @@ self.addEventListener('message', async function (event) { sendToClient(client, { type: 'MOCKING_ENABLED', - payload: true, + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, }) break } @@ -155,6 +160,10 @@ async function handleRequest(event, requestId) { async function resolveMainClient(event) { const client = await self.clients.get(event.clientId) + if (activeClientIds.has(event.clientId)) { + return client + } + if (client?.frameType === 'top-level') { return client } diff --git a/test/browser/msw-api/setup-worker/scenarios/iframe-isolated-response/iframe-isolated-response.mocks.ts b/test/browser/msw-api/setup-worker/scenarios/iframe-isolated-response/iframe-isolated-response.mocks.ts index 8204158dc..f3f46a72d 100644 --- a/test/browser/msw-api/setup-worker/scenarios/iframe-isolated-response/iframe-isolated-response.mocks.ts +++ b/test/browser/msw-api/setup-worker/scenarios/iframe-isolated-response/iframe-isolated-response.mocks.ts @@ -4,8 +4,8 @@ import { setupWorker } from 'msw/browser' const worker = setupWorker() worker.start() -// @ts-ignore window.msw = { + // @ts-ignore worker, http, } diff --git a/test/browser/msw-api/setup-worker/scenarios/iframe-isolated-response/iframe-isolated-response.test.ts b/test/browser/msw-api/setup-worker/scenarios/iframe-isolated-response/iframe-isolated-response.test.ts index ef97469a6..bcb25543e 100644 --- a/test/browser/msw-api/setup-worker/scenarios/iframe-isolated-response/iframe-isolated-response.test.ts +++ b/test/browser/msw-api/setup-worker/scenarios/iframe-isolated-response/iframe-isolated-response.test.ts @@ -7,7 +7,7 @@ const staticMiddleware = (router: express.Router) => { router.use(express.static(__dirname)) } -function getFrameById(id: string, page: Page): Frame { +export function getFrameById(id: string, page: Page): Frame { const frame = page .mainFrame() .childFrames() diff --git a/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/child.mocks.ts b/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/child.mocks.ts new file mode 100644 index 000000000..ae748687e --- /dev/null +++ b/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/child.mocks.ts @@ -0,0 +1,10 @@ +import { http } from 'msw' +import { setupWorker } from 'msw/browser' + +const worker = setupWorker( + http.get('/resource', () => { + return new Response('hello world') + }), +) + +worker.start() diff --git a/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/iframe-multiple-workers.test.ts b/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/iframe-multiple-workers.test.ts new file mode 100644 index 000000000..8c6331cf1 --- /dev/null +++ b/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/iframe-multiple-workers.test.ts @@ -0,0 +1,34 @@ +import { test, expect } from '../../../../../playwright.extend' +import { getFrameById } from '../../iframe-isolated-response/iframe-isolated-response.test' + +test('intercepts a request issued by child frame when both child and parent have MSW', async ({ + webpackServer, + waitForMswActivation, + page, +}) => { + const parentCompilation = await webpackServer.compile([ + require.resolve('./parent.mocks.ts'), + ]) + const childCompilation = await webpackServer.compile([ + require.resolve('./child.mocks.ts'), + ]) + + await page.goto(parentCompilation.previewUrl, { waitUntil: 'networkidle' }) + + await page.evaluate((childFrameUrl) => { + const iframe = document.createElement('iframe') + iframe.setAttribute('id', 'child-frame') + iframe.setAttribute('src', childFrameUrl) + document.body.appendChild(iframe) + }, childCompilation.previewUrl) + + await page.waitForSelector('#child-frame') + + const childFrame = getFrameById('child-frame', page) + const responseText = await childFrame.evaluate(async () => { + const response = await fetch('/resource') + return response.text() + }) + + expect(responseText).toBe('hello world') +}) diff --git a/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/parent.mocks.ts b/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/parent.mocks.ts new file mode 100644 index 000000000..45e1b3f71 --- /dev/null +++ b/test/browser/msw-api/setup-worker/scenarios/iframe/multiple-workers/parent.mocks.ts @@ -0,0 +1,7 @@ +import { setupWorker } from 'msw/browser' + +// The parent frame has a worker without any handlers. +const worker = setupWorker() + +// This registration is awaited by the `loadExample` command in the test. +worker.start()