mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-25 11:14:05 +00:00
@uppy/companion-client: revert breaking change (#4801)
Revert breaking change
This reverts parts of commit 17826da517.
This commit is contained in:
parent
c60ece4dc7
commit
a67f0ffdf2
3 changed files with 266 additions and 0 deletions
87
packages/@uppy/companion-client/src/Socket.js
Normal file
87
packages/@uppy/companion-client/src/Socket.js
Normal file
|
|
@ -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
packages/@uppy/companion-client/src/Socket.test.js
Normal file
176
packages/@uppy/companion-client/src/Socket.test.js
Normal file
|
|
@ -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],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue