UNPKG

data-transport

Version:
390 lines (340 loc) 9.56 kB
import { Transport, listen, createTransport, mockPorts } from '../src'; test('base', async () => { type Internal = { hello(options: { num: number }, word: string): Promise<{ text: string }>; }; const ports = mockPorts(); class InternalTransport extends Transport<{ emit: Internal }> implements Internal { constructor() { super(ports.main); } async hello(options: { num: number }, word: string) { const response = await this.emit('hello', options, word); return response; } } class ExternalTransport extends Transport implements Internal { constructor() { super(ports.create()); } @listen async hello(options: { num: number }, word: string) { return { text: `hello ${options.num} ${word}`, }; } } const internal = new InternalTransport(); const external = new ExternalTransport(); expect(await internal.hello({ num: 42 }, 'Universe')).toEqual({ text: 'hello 42 Universe', }); }); test('base with `{ hasRespond: false }`', async () => { type Internal = { hello(options: { num: number }): Promise<void>; }; let mockExternalSend: ((...args: any) => void) | undefined; let mockInternalSend: (...args: any) => void; class InternalTransport extends Transport<{ emit: Internal }> implements Internal { constructor() { super({ listener: (callback) => { mockExternalSend = callback; return () => { mockExternalSend = undefined; }; }, sender: (message) => { mockInternalSend(JSON.parse(JSON.stringify(message))); }, }); } async hello(options: { num: number }) { const response = await this.emit( { name: 'hello', respond: false, timeout: 42 }, options ); return response; } } class ExternalTransport extends Transport implements Internal { constructor() { super({ listener: (callback) => { mockInternalSend = callback; }, sender: (message) => { mockExternalSend?.(JSON.parse(JSON.stringify(message))); }, }); } @listen async hello(options: { num: number }) { expect(options.num).toBe(42); } } const internal = new InternalTransport(); const external = new ExternalTransport(); expect(await internal.hello({ num: 42 })).toBeUndefined(); }); test('base with two-way', async () => { type Internal = { hello(options: { num: number }): Promise<{ text: string }>; }; type External = { help(options: { key: number }): Promise<{ text: string }>; }; let mockExternalSend: (...args: any) => void; let mockInternalSend: (...args: any) => void; const externalTransportListener = jest.fn(); const internalTransportListener = jest.fn(); const serializer = { parse: JSON.parse, stringify: JSON.stringify, }; class InternalTransport extends Transport<{ emit: Internal }> implements External, Internal { constructor() { super({ listener: (callback) => { mockExternalSend = (data: any) => { callback(data); internalTransportListener(data); }; }, sender: (message) => { mockInternalSend(JSON.parse(JSON.stringify(message))); }, serializer, }); } @listen async help(options: { key: number }) { return { text: String.fromCharCode(options.key), }; } async hello(options: { num: number }) { const response = await this.emit('hello', options); return response; } } class ExternalTransport extends Transport<{ emit: External }> implements External, Internal { constructor() { super({ listener: (callback) => { mockInternalSend = (data: any) => { callback(data); externalTransportListener(data); }; }, sender: (message) => { mockExternalSend(JSON.parse(JSON.stringify(message))); }, serializer, }); } async help(options: { key: number }) { return await this.emit('help', options); } @listen async hello(options: { num: number }) { return { text: `hello ${options.num}`, }; } } const internal = new InternalTransport(); const external = new ExternalTransport(); expect(await internal.hello({ num: 42 })).toEqual({ text: 'hello 42' }); expect(externalTransportListener).toBeCalledTimes(1); expect( Object.prototype.hasOwnProperty.call( externalTransportListener.mock.calls[0][0], 'response' ) ).toBe(false); expect( Object.prototype.hasOwnProperty.call( externalTransportListener.mock.calls[0][0], 'request' ) ).toBe(true); expect(typeof externalTransportListener.mock.calls[0][0].request).toBe( 'string' ); expect(internalTransportListener).toBeCalledTimes(1); expect( Object.prototype.hasOwnProperty.call( internalTransportListener.mock.calls[0][0], 'request' ) ).toBe(false); expect( Object.prototype.hasOwnProperty.call( internalTransportListener.mock.calls[0][0], 'response' ) ).toBe(true); expect(typeof internalTransportListener.mock.calls[0][0].response).toBe( 'string' ); // clean up serializer // @ts-ignore delete serializer.parse; // @ts-ignore delete serializer.stringify; expect(await internal.hello({ num: 42 })).toEqual({ text: 'hello 42' }); expect(typeof externalTransportListener.mock.calls[1][0].request).toBe( 'object' ); expect(typeof internalTransportListener.mock.calls[1][0].response).toBe( 'object' ); expect(await external.help({ key: 65 })).toEqual({ text: 'A' }); expect(() => { external.hello({ num: 42 }); }).toThrowError( "The method 'hello' is a listen function that can NOT be actively called." ); expect(() => { internal.help({ key: 1 }); }).toThrowError( "The method 'help' is a listen function that can NOT be actively called." ); }); test('base with non-decorator', async () => { type Internal = { hello(options: { num: number }): Promise<{ text: string }>; }; let mockExternalSend: (...args: any) => void; let mockInternalSend: (...args: any) => void; class InternalTransport extends Transport<{ emit: Internal }> implements Internal { constructor() { super({ listener: (callback) => { mockExternalSend = callback; }, sender: (message) => { mockInternalSend(JSON.parse(JSON.stringify(message))); }, }); } async hello(options: { num: number }) { const response = await this.emit('hello', options); return response; } } class ExternalTransport extends Transport implements Internal { constructor() { super({ listener: (callback) => { mockInternalSend = callback; }, sender: (message) => { mockExternalSend(JSON.parse(JSON.stringify(message))); }, listenKeys: ['hello'], }); } async hello(options: { num: number }) { return { text: `hello ${options.num}`, }; } } const internal = new InternalTransport(); const external = new ExternalTransport(); expect(await internal.hello({ num: 42 })).toEqual({ text: 'hello 42' }); }); test('base with `undefined`', async () => { type Internal = { hello(): Promise<void>; }; let mockExternalSend: (...args: any) => void; let mockInternalSend: (...args: any) => void; class InternalTransport extends Transport<{ emit: Internal }> implements Internal { constructor() { super({ listener: (callback) => { mockExternalSend = callback; }, sender: (message) => { mockInternalSend(JSON.parse(JSON.stringify(message))); }, }); } async hello() { const response = await this.emit('hello'); return response; } } class ExternalTransport extends Transport implements Internal { constructor() { super({ listener: (callback) => { mockInternalSend = callback; }, sender: (message) => { mockExternalSend(JSON.parse(JSON.stringify(message))); }, }); } @listen async hello() { // } } const internal = new InternalTransport(); const external = new ExternalTransport(); expect(await internal.hello()).toBeUndefined(); }); test('base with createTransport', async () => { type Internal = { hello(options: { num: number }, word: string): Promise<{ text: string }>; }; const ports = mockPorts(); const internal: Transport<{ emit: Internal }> = createTransport( 'Base', ports.main ); const external: Transport<{ listen: Internal }> = createTransport( 'Base', ports.create() ); const dispose = external.listen('hello', async (options, word) => ({ text: `hello ${options.num} ${word}`, })); expect(await internal.emit('hello', { num: 42 }, 'Universe')).toEqual({ text: 'hello 42 Universe', }); const warn = jest.spyOn(console, 'warn').mockImplementation(() => {}); dispose?.(); const result = await internal.emit( { name: 'hello', timeout: 1000 }, { num: 42 }, 'Universe' ); expect(result).toBeUndefined(); expect(warn.mock.calls[0][0]).toBe( "The event 'DataTransport-hello' timed out for 1000 seconds..." ); });