Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/web-worker/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand Down
49 changes: 37 additions & 12 deletions test/core/test/web-worker-node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import { version } from 'node:process'

import { beforeEach, describe, expect, it, vi } from 'vitest'
import MyEventListenerWorker from '../src/web-worker/eventListenerWorker?worker'
import { defineWebWorkers } from '@vitest/web-worker/pure'

import { afterAll, beforeAll, beforeEach, describe, expect, it, suite, 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'
Expand Down Expand Up @@ -49,7 +50,7 @@ describe.runIf(major >= 17)('when node supports structuredClone', () => {
expect(structuredClone).toBeDefined()

const worker = new MyObjectWorker()
const obj = { hello: 'world', name() {} }
const obj = { hello: 'world', name() { } }
worker.postMessage(obj)

return new Promise<void>((resolve, reject) => {
Expand Down Expand Up @@ -211,17 +212,41 @@ 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<NonNullable<Parameters<typeof defineWebWorkers>[0]>['clone']>
cloneTypes.forEach((clone) => {
suite(`defineWebWorkers with clone=${clone}`, () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not mix describe and suite. describe was already imported, let's use it instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done.

let originalWorker: typeof Worker
let originalSharedWorker: typeof SharedWorker
beforeAll(async () => {
originalWorker = globalThis.Worker
originalSharedWorker = globalThis.SharedWorker
delete (globalThis as any).Worker
delete (globalThis as any).SharedWorker

defineWebWorkers({ clone })
})
afterAll(() => {
globalThis.Worker = originalWorker
globalThis.SharedWorker = originalSharedWorker
})

const worker = new MyWorker()
const channel = new MessageChannel()
const promise = new Promise<string>((resolve, reject) => {
channel.port1.onmessage = e => resolve(e.data as string)
channel.port1.onmessageerror = reject
it('transfer MessagePort objects to worker as event.ports', async () => {
expect.assertions(1)

const worker = new MyWorker()
const channel = new MessageChannel()
const promise = new Promise<string>((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', () => {
Expand Down
Loading