From de74647c434a0188951f7615b0e12f457a435186 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti <24919330+marcalexiei@users.noreply.github.com> Date: Sun, 22 Dec 2024 09:30:18 +0100 Subject: [PATCH] test(packages/tuono): messageChannel - use `vi.fn` (#241) --- .../src/ssr/polyfills/messageChannel.spec.ts | 118 ++++++++++-------- .../tuono/src/ssr/polyfills/messageChannel.ts | 12 +- 2 files changed, 72 insertions(+), 58 deletions(-) diff --git a/packages/tuono/src/ssr/polyfills/messageChannel.spec.ts b/packages/tuono/src/ssr/polyfills/messageChannel.spec.ts index cae5c5b9..58d21fa7 100644 --- a/packages/tuono/src/ssr/polyfills/messageChannel.spec.ts +++ b/packages/tuono/src/ssr/polyfills/messageChannel.spec.ts @@ -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): void => { - messageReceived = event.data - } + const onmessageMock = vi.fn() + + 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 = [] - 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() + const listener2 = vi.fn() 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 = [] - const listener = ((event: MessageEvent): void => { - messages.push(event.data) - }) as EventListener + const listener = vi.fn() 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): void => { - messageReceived = event.data - } + const listener = vi.fn() + + 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 = [] - channel.port1.onmessage = (event: MessageEvent): void => { - messages.push(event.data) - } + const listener = vi.fn() + + 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 = [] - const listener = ((event: MessageEvent): void => { - messages.push(event.data) - }) as EventListener + const listener = vi.fn() 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 = [] - const messagesPort2: Array = [] - channel.port1.onmessage = (event: MessageEvent): void => { - messagesPort1.push(event.data) - } + const listener1 = vi.fn() + const listener2 = vi.fn() - channel.port2.onmessage = (event: MessageEvent): 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 = [] - port1.onmessage = (event: MessageEvent): void => { - messages.push(event.data) - } + const listener = vi.fn() + + 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) }) }) diff --git a/packages/tuono/src/ssr/polyfills/messageChannel.ts b/packages/tuono/src/ssr/polyfills/messageChannel.ts index 65b614c0..ec888666 100644 --- a/packages/tuono/src/ssr/polyfills/messageChannel.ts +++ b/packages/tuono/src/ssr/polyfills/messageChannel.ts @@ -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 }