UNPKG

siesta-lite

Version:

Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers

150 lines (106 loc) 4.53 kB
import {Constructable, Mixin} from "../util/Common.js"; import {CanLog} from "../util/role/CanLog.js"; import {Channel, Envelop, EnvelopId, Message} from "./Types.js"; // globally unique, to up to repeated usages of this module in different JS context let ENVELOP_COUNTER : EnvelopId = 1 export const ChannelEndPoint = <T extends Constructable<CanLog>>(base : T) => class ChannelEndPoint extends base { connectionTimeout : number connectionInterval : number = 1000 awaitingResponses : Map<EnvelopId, [ Function, Function, Envelop, any ]> = new Map() doConnect (channel : Channel) : Promise<any> { throw "Abstract method `doConnect`" } doDisconnect () { throw "Abstract method `doDisconnect`" } sendMessage (message : Message) { throw "Abstract method `sendMessage`" } messageToEnvelop (message : Message) : Envelop | undefined { throw "Abstract method `messageToEnvelop`" } envelopToMessage (envelop : Envelop) : Message { throw "Abstract method `envelopToMessage`" } doProcessRawChannelMessage (message : Message, envelop : Envelop) { throw "Abstract method `doProcessRawChannelMessage`" } doDispatchEnvelop (envelop : Envelop) { throw "Abstract method `doDispatchEnvelop`" } connect(channel? : Channel) : Promise<any> { let start = new Date().getTime() return new Promise((resolve, reject) => { const connectionAttempt = () => { this.doConnect(channel).then( resolve, reason => { if (new Date().getTime() - start > (this.connectionTimeout || 3000)) reject(reason) else setTimeout(connectionAttempt, this.connectionInterval) } ) } connectionAttempt() }) } onRawChannelMessage (message : Message) { this.info("Received raw message: " + JSON.stringify(message)) const envelop = this.messageToEnvelop(message) if (envelop !== undefined && envelop.id != null) { if (envelop.inResponseOf != null) { let id = envelop.inResponseOf const handlers = this.awaitingResponses.get(id) if (!handlers) { this.warn("Response for unknown envelop, timeout occurred?:\n" + JSON.stringify(envelop)) } else { this.awaitingResponses.delete(id) handlers[ envelop.isRejection ? 1 : 0 ](envelop.payload) } } else this.doDispatchEnvelop(envelop) } else { this.doProcessRawChannelMessage(message, envelop) } } disconnect () { this.awaitingResponses.forEach((value, id) => { value[ 1 ]("Disconnecting") }) this.awaitingResponses.clear() return this.doDisconnect() } getFreshEnvelopId () : EnvelopId { return ENVELOP_COUNTER++ } replyWith (envelop : Envelop, result : any, isRejection : boolean = false) { this.sendMessage(this.envelopToMessage({ id : this.getFreshEnvelopId(), inResponseOf : envelop.id, isRejection : isRejection, payload : result })) } sendRpcCall (payload : object, timeout? : number) : Promise<any> { this.debug("Sending command: " + payload.toString()) const id = this.getFreshEnvelopId() const envelop : Envelop = { id : id, payload : payload } return new Promise((resolve, reject) => { let timeoutHandler if (timeout != null) { let timeoutHandler = setTimeout(() => { this.debug("Timeout occurred for: " + JSON.stringify(envelop)) if (this.awaitingResponses.has(id)) { this.awaitingResponses.delete(id) reject("Timeout while waiting for command result: " + JSON.stringify(payload)) } }, timeout) } this.awaitingResponses.set(id, [ resolve, reject, envelop, timeoutHandler ]) this.sendMessage(this.envelopToMessage(envelop)) }) } } export type ChannelEndPoint = Mixin<typeof ChannelEndPoint>