diff --git a/packages/web-worker/src/utils.ts b/packages/web-worker/src/utils.ts index 43a0cbde270e..2da71f188c1b 100644 --- a/packages/web-worker/src/utils.ts +++ b/packages/web-worker/src/utils.ts @@ -31,11 +31,14 @@ function createClonedMessageEvent( debug('clone worker message %o', data) const origin = typeof location === 'undefined' ? undefined : location.origin const ports = transfer?.filter((t): t is MessagePort => t instanceof MessagePort) + const transferWithoutPorts = transfer?.filter( // `ports` must be excluded from the `transfer` option passed to `structuredClone` to keep the MessagePort objects working correctly in the same thread. + t => !(t instanceof MessagePort), + ) if (typeof structuredClone === 'function' && clone === 'native') { debug('create message event, using native structured clone') return new MessageEvent('message', { - data: structuredClone(data, { transfer }), + data: structuredClone(data, { transfer: transferWithoutPorts }), origin, ports, }) diff --git a/test/core/test/web-worker-node.test.ts b/test/core/test/web-worker-node.test.ts index 1977446feca3..11b527f219ae 100644 --- a/test/core/test/web-worker-node.test.ts +++ b/test/core/test/web-worker-node.test.ts @@ -1,10 +1,11 @@ // @vitest-environment node +import type { defineWebWorkers } from '@vitest/web-worker/pure' + import { version } from 'node:process' -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import MyEventListenerWorker from '../src/web-worker/eventListenerWorker?worker' - import MyObjectWorker from '../src/web-worker/objectWorker?worker' import MySelfWorker from '../src/web-worker/selfWorker?worker' import MySharedWorker from '../src/web-worker/sharedWorker?sharedworker' @@ -211,17 +212,33 @@ it('self injected into worker and its deps should be equal', async () => { expect(await testSelfWorker(new Worker(new URL('../src/web-worker/selfWorker.ts', import.meta.url)))).toBeTruthy() }) -it('transfer MessagePort objects to worker as event.ports', async () => { - expect.assertions(1) +const cloneTypes = [ + 'native', + 'ponyfill', + 'none', +] satisfies Array[0]>['clone']> +cloneTypes.forEach((clone) => { + describe(`defineWebWorkers with clone=${clone}`, () => { + beforeEach(() => { + process.env.VITEST_WEB_WORKER_CLONE = clone + }) + afterEach(() => { + process.env.VITEST_WEB_WORKER_CLONE = undefined + }) - const worker = new MyWorker() - const channel = new MessageChannel() - const promise = new Promise((resolve, reject) => { - channel.port1.onmessage = e => resolve(e.data as string) - channel.port1.onmessageerror = reject + it('transfers MessagePort objects to worker as event.ports', async () => { + expect.assertions(1) + + const worker = new MyWorker() + const channel = new MessageChannel() + const promise = new Promise((resolve, reject) => { + channel.port1.onmessage = e => resolve(e.data as string) + channel.port1.onmessageerror = reject + }) + worker.postMessage('hello', [channel.port2]) + await expect(promise).resolves.toBe('hello world via port') + }) }) - worker.postMessage('hello', [channel.port2]) - await expect(promise).resolves.toBe('hello world via port') }) it('throws syntax error if no arguments are provided', () => {