test(packages/tuono): messageChannel - use vi.fn (#241)

This commit is contained in:
Marco Pasqualetti
2024-12-22 09:30:18 +01:00
committed by GitHub
parent 498601c21a
commit de74647c43
2 changed files with 72 additions and 58 deletions
@@ -1,48 +1,43 @@
import { describe, it, expect } from 'vitest'
import { describe, it, expect, vi } from 'vitest'
import { MessageChannelPolyfill, MessagePortPolyfill } from './messageChannel'
describe('MessagePortPolyfill', () => {
it('should invoke onmessage when a message is posted', () => {
const port = new MessagePortPolyfill()
let messageReceived: string | null = null
port.onmessage = (event: MessageEvent<string>): void => {
messageReceived = event.data
}
const onmessageMock = vi.fn<EventListener>()
port.onmessage = onmessageMock
port.dispatchEvent({ data: 'Hello, world!' } as MessageEvent)
expect(messageReceived).toBe('Hello, world!')
expect(onmessageMock).toHaveBeenCalledOnce()
expect(onmessageMock).toHaveBeenCalledWith({ data: 'Hello, world!' })
})
it('should handle multiple event listeners', () => {
const port = new MessagePortPolyfill()
const messages: Array<string> = []
const listener1 = ((event: MessageEvent): void => {
messages.push('Listener1: ' + event.data)
}) as EventListener
const listener2 = ((event: MessageEvent): void => {
messages.push('Listener2: ' + event.data)
}) as EventListener
const listener1 = vi.fn<EventListener>()
const listener2 = vi.fn<EventListener>()
port.addEventListener('message', listener1)
port.addEventListener('message', listener2)
port.dispatchEvent({ data: 'Test message' } as MessageEvent)
expect(messages).toEqual([
'Listener1: Test message',
'Listener2: Test message',
])
expect(listener1).toHaveBeenCalledOnce()
expect(listener1).toHaveBeenCalledWith({ data: 'Test message' })
expect(listener2).toHaveBeenCalledOnce()
expect(listener2).toHaveBeenCalledWith({ data: 'Test message' })
})
it('should not invoke removed event listeners', () => {
const port = new MessagePortPolyfill()
const messages: Array<string> = []
const listener = ((event: MessageEvent<string>): void => {
messages.push(event.data)
}) as EventListener
const listener = vi.fn<EventListener>()
port.addEventListener('message', listener)
port.dispatchEvent({ data: 'First message' } as MessageEvent)
@@ -50,73 +45,85 @@ describe('MessagePortPolyfill', () => {
port.removeEventListener('message', listener)
port.dispatchEvent({ data: 'Second message' } as MessageEvent)
expect(messages).toEqual(['First message'])
expect(listener).toHaveBeenCalledOnce()
expect(listener).toHaveBeenCalledWith({ data: 'First message' })
})
it('should not post messages if otherPort is null', () => {
const port = new MessagePortPolyfill()
let messageReceived: string | null = null
port.onmessage = (event: MessageEvent<string>): void => {
messageReceived = event.data
}
const listener = vi.fn<EventListener>()
port.onmessage = listener
port.postMessage('Hello!')
expect(messageReceived).toBeNull()
expect(listener).not.toHaveBeenCalledOnce()
})
})
describe('MessageChannelPolyfill', () => {
it('should send and receive messages between ports', () => {
const channel = new MessageChannelPolyfill()
const messages: Array<string> = []
channel.port1.onmessage = (event: MessageEvent<string>): void => {
messages.push(event.data)
}
const listener = vi.fn<EventListener>()
channel.port1.onmessage = listener
channel.port2.postMessage('Hello, port1!')
channel.port2.postMessage('How are you?')
expect(messages).toEqual(['Hello, port1!', 'How are you?'])
expect(listener).toHaveBeenCalledTimes(2)
expect(listener).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ data: 'Hello, port1!' }),
)
expect(listener).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ data: 'How are you?' }),
)
})
it('should support addEventListener and removeEventListener', () => {
const channel = new MessageChannelPolyfill()
const messages: Array<string> = []
const listener = ((event: MessageEvent<string>): void => {
messages.push(event.data)
}) as EventListener
const listener = vi.fn<EventListener>()
channel.port1.addEventListener('message', listener)
channel.port2.postMessage('Hello, port1!')
expect(messages).toEqual(['Hello, port1!'])
expect(listener).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ data: 'Hello, port1!' }),
)
channel.port1.removeEventListener('message', listener)
channel.port2.postMessage('Message after removing listener')
expect(messages).toEqual(['Hello, port1!'])
expect(listener).not.toHaveBeenCalledTimes(2)
})
it('should handle bidirectional communication between ports', () => {
const channel = new MessageChannelPolyfill()
const messagesPort1: Array<string> = []
const messagesPort2: Array<string> = []
channel.port1.onmessage = (event: MessageEvent<string>): void => {
messagesPort1.push(event.data)
}
const listener1 = vi.fn<EventListener>()
const listener2 = vi.fn<EventListener>()
channel.port2.onmessage = (event: MessageEvent<string>): void => {
messagesPort2.push(event.data)
}
channel.port1.onmessage = listener1
channel.port2.onmessage = listener2
channel.port1.postMessage('Hello, port2!')
channel.port2.postMessage('Hello, port1!')
expect(messagesPort1).toEqual(['Hello, port1!'])
expect(messagesPort2).toEqual(['Hello, port2!'])
expect(listener1).toHaveBeenCalledOnce()
expect(listener1).toHaveBeenCalledWith(
expect.objectContaining({ data: 'Hello, port1!' }),
)
expect(listener2).toHaveBeenCalledOnce()
expect(listener2).toHaveBeenCalledWith(
expect.objectContaining({ data: 'Hello, port2!' }),
)
})
})
@@ -124,17 +131,20 @@ describe('MessagePort', () => {
it('should not send a message on close', () => {
const { port1, port2 } = new MessageChannelPolyfill()
const messages: Array<string> = []
port1.onmessage = (event: MessageEvent<string>): void => {
messages.push(event.data)
}
const listener = vi.fn<EventListener>()
port1.onmessage = listener
port2.postMessage('Test message')
expect(messages).toEqual(['Test message'])
expect(listener).toHaveBeenCalledOnce()
expect(listener).toHaveBeenCalledWith(
expect.objectContaining({ data: 'Test message' }),
)
port1.close()
port2.postMessage('Another message')
expect(messages).toEqual(['Test message'])
expect(listener).not.toHaveBeenCalledTimes(2)
})
})
@@ -24,10 +24,12 @@
export class MessagePortPolyfill implements MessagePort {
onmessage: ((this: MessagePort, ev: MessageEvent) => unknown) | null = null
/** @warning this is declared to satisfy {@link MessagePort} interface requirements but is never called */
onmessageerror: ((this: MessagePort, ev: MessageEvent) => unknown) | null =
null
otherPort: MessagePortPolyfill | null = null
private onmessageListeners: Array<(ev: MessageEvent) => void> = []
private isClosed = false
@@ -42,6 +44,7 @@ export class MessagePortPolyfill implements MessagePort {
postMessage(message: unknown): void {
if (this.isClosed || !this.otherPort) return
const event = new MessageEvent('message', { data: message })
this.otherPort.dispatchEvent(event)
}
@@ -51,11 +54,12 @@ export class MessagePortPolyfill implements MessagePort {
listener: EventListenerOrEventListenerObject,
): void {
if (this.isClosed || type !== 'message') return
if (
typeof listener === 'function' &&
!this.onmessageListeners.includes(listener)
) {
this.onmessageListeners.push(listener as (ev: MessageEvent) => void)
this.onmessageListeners.push(listener)
}
}
@@ -64,10 +68,9 @@ export class MessagePortPolyfill implements MessagePort {
listener: EventListenerOrEventListenerObject,
): void {
if (this.isClosed || type !== 'message') return
if (typeof listener === 'function') {
const index = this.onmessageListeners.indexOf(
listener as (ev: MessageEvent) => void,
)
const index = this.onmessageListeners.indexOf(listener)
if (index !== -1) {
this.onmessageListeners.splice(index, 1)
}
@@ -90,6 +93,7 @@ export class MessageChannelPolyfill implements MessageChannel {
constructor() {
this.port1 = new MessagePortPolyfill()
this.port2 = new MessagePortPolyfill()
this.port1.otherPort = this.port2
this.port2.otherPort = this.port1
}