From 26788a33335d6ea15b8bfb81e81fdbed60d2d70e Mon Sep 17 00:00:00 2001 From: Steven Gum <14935595+stevengum@users.noreply.github.com> Date: Tue, 14 Jan 2020 13:31:20 -0800 Subject: [PATCH] remove "dom" from bf-streaming tsconfig and refactor code (#1575) * remove "dom" from bf-streaming tsconfig and refactor code * remove isBrowser, look for global members instead --- .../src/interfaces/IBrowserFileReader.ts | 19 ++++++++ .../src/interfaces/IBrowserWebSocket.ts | 24 ++++++++++ .../src/interfaces/index.ts | 2 + .../utilities/doesGlobalFileReaderExist.ts | 9 ++++ .../src/utilities/doesGlobalWebSocketExist.ts | 9 ++++ .../src/utilities/index.ts | 2 + .../src/webSocket/browserWebSocket.ts | 30 ++++++++++--- .../src/webSocket/webSocketClient.ts | 3 +- .../tests/InternalUtilities.test.js | 45 +++++++++++++++++++ .../botframework-streaming/tsconfig.json | 2 +- 10 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 libraries/botframework-streaming/src/interfaces/IBrowserFileReader.ts create mode 100644 libraries/botframework-streaming/src/interfaces/IBrowserWebSocket.ts create mode 100644 libraries/botframework-streaming/src/utilities/doesGlobalFileReaderExist.ts create mode 100644 libraries/botframework-streaming/src/utilities/doesGlobalWebSocketExist.ts create mode 100644 libraries/botframework-streaming/tests/InternalUtilities.test.js diff --git a/libraries/botframework-streaming/src/interfaces/IBrowserFileReader.ts b/libraries/botframework-streaming/src/interfaces/IBrowserFileReader.ts new file mode 100644 index 0000000000..3d02975727 --- /dev/null +++ b/libraries/botframework-streaming/src/interfaces/IBrowserFileReader.ts @@ -0,0 +1,19 @@ +/** + * @module botframework-streaming + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +/** + * Partially represents a FileReader from the W3C FileAPI Working Draft. + * For more information, see https://w3c.github.io/FileAPI/#APIASynch. + * + * This interface supports the framework and is not intended to be called directly for your code. + */ +export interface IBrowserFileReader { + result: any; + onload: (event: any) => void; + readAsArrayBuffer: (blobOrFile: any) => void; +} diff --git a/libraries/botframework-streaming/src/interfaces/IBrowserWebSocket.ts b/libraries/botframework-streaming/src/interfaces/IBrowserWebSocket.ts new file mode 100644 index 0000000000..b9b1448de8 --- /dev/null +++ b/libraries/botframework-streaming/src/interfaces/IBrowserWebSocket.ts @@ -0,0 +1,24 @@ +/** + * @module botframework-streaming + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +/** + * Partially represents a WebSocket from the HTML Living Standard. + * For more information, see https://html.spec.whatwg.org/multipage/web-sockets.html. + * + * This interface supports the framework and is not intended to be called directly for your code. + */ +export interface IBrowserWebSocket { + onclose: (event: any) => void; + onerror: (event: any) => void; + onmessage: (event: any) => void; + onopen: (event: any) => void; + readyState: number; + + close(): void; + send(buffer: any): void; +} diff --git a/libraries/botframework-streaming/src/interfaces/index.ts b/libraries/botframework-streaming/src/interfaces/index.ts index 6da74891e6..ae75d8102d 100644 --- a/libraries/botframework-streaming/src/interfaces/index.ts +++ b/libraries/botframework-streaming/src/interfaces/index.ts @@ -6,6 +6,8 @@ * Licensed under the MIT License. */ +export * from './IBrowserFileReader'; +export * from './IBrowserWebSocket'; export * from './INodeBuffer'; export * from './INodeIncomingMessage'; export * from './INodeSocket'; diff --git a/libraries/botframework-streaming/src/utilities/doesGlobalFileReaderExist.ts b/libraries/botframework-streaming/src/utilities/doesGlobalFileReaderExist.ts new file mode 100644 index 0000000000..dbf3572194 --- /dev/null +++ b/libraries/botframework-streaming/src/utilities/doesGlobalFileReaderExist.ts @@ -0,0 +1,9 @@ +/** + * @module botframework-streaming + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +export const doesGlobalFileReaderExist = new Function('try {return typeof FileReader !== "undefined" && FileReader !== null;}catch(e){ return false;}'); diff --git a/libraries/botframework-streaming/src/utilities/doesGlobalWebSocketExist.ts b/libraries/botframework-streaming/src/utilities/doesGlobalWebSocketExist.ts new file mode 100644 index 0000000000..4c202ee025 --- /dev/null +++ b/libraries/botframework-streaming/src/utilities/doesGlobalWebSocketExist.ts @@ -0,0 +1,9 @@ +/** + * @module botframework-streaming + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +export const doesGlobalWebSocketExist = new Function('try {return typeof WebSocket !== "undefined" && WebSocket !== null;}catch(e){ return false;}'); diff --git a/libraries/botframework-streaming/src/utilities/index.ts b/libraries/botframework-streaming/src/utilities/index.ts index 68108e31c9..2a0738cff8 100644 --- a/libraries/botframework-streaming/src/utilities/index.ts +++ b/libraries/botframework-streaming/src/utilities/index.ts @@ -6,4 +6,6 @@ * Licensed under the MIT License. */ +export * from './doesGlobalFileReaderExist'; +export * from './doesGlobalWebSocketExist'; export * from './protocol-base'; diff --git a/libraries/botframework-streaming/src/webSocket/browserWebSocket.ts b/libraries/botframework-streaming/src/webSocket/browserWebSocket.ts index 8e2b0073cd..228f7ca0fa 100644 --- a/libraries/botframework-streaming/src/webSocket/browserWebSocket.ts +++ b/libraries/botframework-streaming/src/webSocket/browserWebSocket.ts @@ -5,17 +5,35 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ -import { ISocket } from '../interfaces/ISocket'; +import { IBrowserFileReader, IBrowserWebSocket, ISocket } from '../interfaces'; +import { doesGlobalFileReaderExist, doesGlobalWebSocketExist } from '../utilities'; + +const createWebSocket = function(url: string): IBrowserWebSocket { + if (!url) { + throw new TypeError('Unable to create WebSocket without url.'); + } + if (doesGlobalWebSocketExist()) { + return new Function(`return new WebSocket('${ url }');`)(); + } + throw new ReferenceError('Unable to find global.WebSocket which is required for constructing a BrowserWebSocket.'); +}; + +const createFileReader = function(): IBrowserFileReader { + if (doesGlobalFileReaderExist()) { + return new Function(`return new FileReader();`)(); + } + throw new ReferenceError('Unable to find global.FileReader. Unable to create FileReader for BrowserWebSocket.'); +}; export class BrowserWebSocket implements ISocket { - private webSocket: WebSocket; + private webSocket: IBrowserWebSocket; /** * Creates a new instance of the [BrowserWebSocket](xref:botframework-streaming.BrowserWebSocket) class. * * @param socket The socket object to build this connection on. */ - public constructor(socket?: WebSocket) { + public constructor(socket?: IBrowserWebSocket) { if (socket) { this.webSocket = socket; } @@ -31,7 +49,7 @@ export class BrowserWebSocket implements ISocket { let rejector; if (!this.webSocket) { - this.webSocket = new WebSocket(serverAddress); + this.webSocket = createWebSocket(serverAddress); } this.webSocket.onerror = (e): void => { @@ -79,11 +97,11 @@ export class BrowserWebSocket implements ISocket { const bufferKey: string = 'buffer'; let packets = []; this.webSocket.onmessage = (evt): void => { - let fileReader = new FileReader(); + let fileReader = createFileReader(); let queueEntry = {buffer: null}; packets.push(queueEntry); fileReader.onload = (e): void => { - let t: FileReader = e.target as FileReader; + let t = e.target as IBrowserFileReader; queueEntry[bufferKey] = t.result; if (packets[0] === queueEntry) { while(0 < packets.length && packets[0][bufferKey]) { diff --git a/libraries/botframework-streaming/src/webSocket/webSocketClient.ts b/libraries/botframework-streaming/src/webSocket/webSocketClient.ts index 0f7c076bd9..69616f18c7 100644 --- a/libraries/botframework-streaming/src/webSocket/webSocketClient.ts +++ b/libraries/botframework-streaming/src/webSocket/webSocketClient.ts @@ -16,6 +16,7 @@ import { } from '../payloadTransport'; import { BrowserWebSocket } from './browserWebSocket'; import { NodeWebSocket } from './nodeWebSocket'; +import { doesGlobalWebSocketExist } from '../utilities'; import { WebSocketTransport } from './webSocketTransport'; import { IStreamingTransportClient, IReceiveResponse } from '../interfaces'; @@ -59,7 +60,7 @@ export class WebSocketClient implements IStreamingTransportClient { * @returns A promise that will not resolve until the client stops listening for incoming messages. */ public async connect(): Promise { - if (typeof WebSocket !== 'undefined') { + if (doesGlobalWebSocketExist()) { const ws = new BrowserWebSocket(); await ws.connect(this._url); const transport = new WebSocketTransport(ws); diff --git a/libraries/botframework-streaming/tests/InternalUtilities.test.js b/libraries/botframework-streaming/tests/InternalUtilities.test.js new file mode 100644 index 0000000000..d5636b8b8b --- /dev/null +++ b/libraries/botframework-streaming/tests/InternalUtilities.test.js @@ -0,0 +1,45 @@ +const { doesGlobalFileReaderExist, doesGlobalWebSocketExist } = require('../lib/utilities'); +const chai = require('chai'); +const expect = chai.expect; + +describe('Internal Utilities', () => { + it('doesGlobalWebSocketExist() should return true if global.WebSocket is truthy', () => { + global.WebSocket = {}; + try { + expect(doesGlobalWebSocketExist()).to.be.true; + } finally { + global.WebSocket = undefined; + } + }); + + it('doesGlobalWebSocketExist() should return false if global.WebSocket is null or undefined', () => { + expect(doesGlobalWebSocketExist()).to.be.false; + + global.WebSocket = null; + try { + expect(doesGlobalWebSocketExist()).to.be.false; + } finally { + global.WebSocket = undefined; + } + }); + + it('doesGlobalFileReaderExist() should return true if global.FileReader is truthy', () => { + global.FileReader = {}; + try { + expect(doesGlobalFileReaderExist()).to.be.true; + } finally { + global.FileReader = undefined; + } + }); + + it('doesGlobalFileReaderExist() should return false if global.FileReader is null or undefined', () => { + expect(doesGlobalFileReaderExist()).to.be.false; + + global.FileReader = null; + try { + expect(doesGlobalFileReaderExist()).to.be.false; + } finally { + global.FileReader = undefined; + } + }); +}); diff --git a/libraries/botframework-streaming/tsconfig.json b/libraries/botframework-streaming/tsconfig.json index 74e5f85fc1..f903672323 100644 --- a/libraries/botframework-streaming/tsconfig.json +++ b/libraries/botframework-streaming/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es6", - "lib": ["es2015", "dom"], + "lib": ["es2015"], "module": "commonjs", "declaration": true, "sourceMap": true,