Skip to content

Commit

Permalink
Revert breaking change
Browse files Browse the repository at this point in the history
This reverts commit 17826da.
  • Loading branch information
aduh95 committed Nov 20, 2023
1 parent c60ece4 commit 5079311
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 0 deletions.
87 changes: 87 additions & 0 deletions packages/@uppy/companion-client/src/Socket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import ee from 'namespace-emitter'

export default class UppySocket {
#queued = []

#emitter = ee()

#isOpen = false

#socket

constructor (opts) {
this.opts = opts

if (!opts || opts.autoOpen !== false) {
this.open()
}
}

get isOpen () { return this.#isOpen }

[Symbol.for('uppy test: getSocket')] () { return this.#socket }

[Symbol.for('uppy test: getQueued')] () { return this.#queued }

open () {
if (this.#socket != null) return

this.#socket = new WebSocket(this.opts.target)

this.#socket.onopen = () => {
this.#isOpen = true

while (this.#queued.length > 0 && this.#isOpen) {
const first = this.#queued.shift()
this.send(first.action, first.payload)
}
}

this.#socket.onclose = () => {
this.#isOpen = false
this.#socket = null
}

this.#socket.onmessage = this.#handleMessage
}

close () {
this.#socket?.close()
}

send (action, payload) {
// attach uuid

if (!this.#isOpen) {
this.#queued.push({ action, payload })
return
}

this.#socket.send(JSON.stringify({
action,
payload,
}))
}

on (action, handler) {
this.#emitter.on(action, handler)
}

emit (action, payload) {
this.#emitter.emit(action, payload)
}

once (action, handler) {
this.#emitter.once(action, handler)
}

#handleMessage = (e) => {
try {
const message = JSON.parse(e.data)
this.emit(message.action, message.payload)
} catch (err) {
// TODO: use a more robust error handler.
console.log(err) // eslint-disable-line no-console
}
}
}
176 changes: 176 additions & 0 deletions packages/@uppy/companion-client/src/Socket.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { afterEach, beforeEach, vi, describe, it, expect } from 'vitest'
import UppySocket from './Socket.js'

describe('Socket', () => {
let webSocketConstructorSpy
let webSocketCloseSpy
let webSocketSendSpy

beforeEach(() => {
webSocketConstructorSpy = vi.fn()
webSocketCloseSpy = vi.fn()
webSocketSendSpy = vi.fn()

globalThis.WebSocket = class WebSocket {
constructor (target) {
webSocketConstructorSpy(target)
}

// eslint-disable-next-line class-methods-use-this
close (args) {
webSocketCloseSpy(args)
}

// eslint-disable-next-line class-methods-use-this
send (json) {
webSocketSendSpy(json)
}

triggerOpen () {
this.onopen()
}

triggerClose () {
this.onclose()
}
}
})
afterEach(() => {
globalThis.WebSocket = undefined
})

it('should expose a class', () => {
expect(UppySocket.name).toEqual('UppySocket')
expect(
new UppySocket({
target: 'foo',
}) instanceof UppySocket,
)
})

it('should setup a new WebSocket', () => {
new UppySocket({ target: 'foo' }) // eslint-disable-line no-new
expect(webSocketConstructorSpy.mock.calls[0][0]).toEqual('foo')
})

it('should send a message via the websocket if the connection is open', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]()
webSocketInstance.triggerOpen()

uppySocket.send('bar', 'boo')
expect(webSocketSendSpy.mock.calls.length).toEqual(1)
expect(webSocketSendSpy.mock.calls[0]).toEqual([
JSON.stringify({ action: 'bar', payload: 'boo' }),
])
})

it('should queue the message for the websocket if the connection is not open', () => {
const uppySocket = new UppySocket({ target: 'foo' })

uppySocket.send('bar', 'boo')
expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([{ action: 'bar', payload: 'boo' }])
expect(webSocketSendSpy.mock.calls.length).toEqual(0)
})

it('should queue any messages for the websocket if the connection is not open, then send them when the connection is open', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]()

uppySocket.send('bar', 'boo')
uppySocket.send('moo', 'baa')
expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([
{ action: 'bar', payload: 'boo' },
{ action: 'moo', payload: 'baa' },
])
expect(webSocketSendSpy.mock.calls.length).toEqual(0)

webSocketInstance.triggerOpen()

expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([])
expect(webSocketSendSpy.mock.calls.length).toEqual(2)
expect(webSocketSendSpy.mock.calls[0]).toEqual([
JSON.stringify({ action: 'bar', payload: 'boo' }),
])
expect(webSocketSendSpy.mock.calls[1]).toEqual([
JSON.stringify({ action: 'moo', payload: 'baa' }),
])
})

it('should start queuing any messages when the websocket connection is closed', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]()
webSocketInstance.triggerOpen()
uppySocket.send('bar', 'boo')
expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([])

webSocketInstance.triggerClose()
uppySocket.send('bar', 'boo')
expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([{ action: 'bar', payload: 'boo' }])
})

it('should close the websocket when it is force closed', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]()
webSocketInstance.triggerOpen()

uppySocket.close()
expect(webSocketCloseSpy.mock.calls.length).toEqual(1)
})

it('should be able to subscribe to messages received on the websocket', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]()

const emitterListenerMock = vi.fn()
uppySocket.on('hi', emitterListenerMock)

webSocketInstance.triggerOpen()
webSocketInstance.onmessage({
data: JSON.stringify({ action: 'hi', payload: 'ho' }),
})
expect(emitterListenerMock.mock.calls).toEqual([
['ho', undefined, undefined, undefined, undefined, undefined],
])
})

it('should be able to emit messages and subscribe to them', () => {
const uppySocket = new UppySocket({ target: 'foo' })

const emitterListenerMock = vi.fn()
uppySocket.on('hi', emitterListenerMock)

uppySocket.emit('hi', 'ho')
uppySocket.emit('hi', 'ho')
uppySocket.emit('hi', 'off to work we go')

expect(emitterListenerMock.mock.calls).toEqual([
['ho', undefined, undefined, undefined, undefined, undefined],
['ho', undefined, undefined, undefined, undefined, undefined],
[
'off to work we go',
undefined,
undefined,
undefined,
undefined,
undefined,
],
])
})

it('should be able to subscribe to the first event for a particular action', () => {
const uppySocket = new UppySocket({ target: 'foo' })

const emitterListenerMock = vi.fn()
uppySocket.once('hi', emitterListenerMock)

uppySocket.emit('hi', 'ho')
uppySocket.emit('hi', 'ho')
uppySocket.emit('hi', 'off to work we go')

expect(emitterListenerMock.mock.calls.length).toEqual(1)
expect(emitterListenerMock.mock.calls).toEqual([
['ho', undefined, undefined, undefined, undefined, undefined],
])
})
})
3 changes: 3 additions & 0 deletions packages/@uppy/companion-client/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
export { default as RequestClient } from './RequestClient.js'
export { default as Provider } from './Provider.js'
export { default as SearchProvider } from './SearchProvider.js'

// TODO: remove in the next major
export { default as Socket } from './Socket.js'

0 comments on commit 5079311

Please sign in to comment.