diff --git a/lib/socket.ts b/lib/socket.ts index 0365c72e..761ab71e 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -313,19 +313,19 @@ export class SocketWithoutUpgrade extends Emitter< protected upgrading: boolean; protected setTimeoutFn: typeof setTimeout; - private prevBufferLen: number; - private pingInterval: number; - private pingTimeout: number; - private pingTimeoutTimer: NodeJS.Timer; + private _prevBufferLen: number = 0; + private _pingInterval: number = -1; + private _pingTimeout: number = -1; + private _maxPayload?: number = -1; + private _pingTimeoutTimer: NodeJS.Timer; private clearTimeoutFn: typeof clearTimeout; - private readonly beforeunloadEventListener: () => void; - private readonly offlineEventListener: () => void; - private maxPayload?: number; + private readonly _beforeunloadEventListener: () => void; + private readonly _offlineEventListener: () => void; private readonly secure: boolean; private readonly hostname: string; private readonly port: string | number; - private readonly transportsByName: Record; + private readonly _transportsByName: Record; /** * The cookie jar will store the cookies sent by the server (Node. js only). */ @@ -383,16 +383,13 @@ export class SocketWithoutUpgrade extends Emitter< : "80"); this.transports = []; - this.transportsByName = {}; + this._transportsByName = {}; opts.transports.forEach((t) => { const transportName = t.prototype.name; this.transports.push(transportName); - this.transportsByName[transportName] = t; + this._transportsByName[transportName] = t; }); - this.writeBuffer = []; - this.prevBufferLen = 0; - this.opts = Object.assign( { path: "/engine.io", @@ -420,35 +417,31 @@ export class SocketWithoutUpgrade extends Emitter< this.opts.query = decode(this.opts.query); } - // set on handshake - this.id = null; - this.pingInterval = null; - this.pingTimeout = null; - - // set on heartbeat - this.pingTimeoutTimer = null; - if (typeof addEventListener === "function") { if (this.opts.closeOnBeforeunload) { // Firefox closes the connection when the "beforeunload" event is emitted but not Chrome. This event listener // ensures every browser behaves the same (no "disconnect" event at the Socket.IO level when the page is // closed/reloaded) - this.beforeunloadEventListener = () => { + this._beforeunloadEventListener = () => { if (this.transport) { // silently close the transport this.transport.removeAllListeners(); this.transport.close(); } }; - addEventListener("beforeunload", this.beforeunloadEventListener, false); + addEventListener( + "beforeunload", + this._beforeunloadEventListener, + false + ); } if (this.hostname !== "localhost") { - this.offlineEventListener = () => { - this.onClose("transport close", { + this._offlineEventListener = () => { + this._onClose("transport close", { description: "network connection lost", }); }; - addEventListener("offline", this.offlineEventListener, false); + addEventListener("offline", this._offlineEventListener, false); } } @@ -456,7 +449,7 @@ export class SocketWithoutUpgrade extends Emitter< this._cookieJar = createCookieJar(); } - this.open(); + this._open(); } /** @@ -494,7 +487,7 @@ export class SocketWithoutUpgrade extends Emitter< debug("options: %j", opts); - return new this.transportsByName[name](opts); + return new this._transportsByName[name](opts); } /** @@ -502,7 +495,7 @@ export class SocketWithoutUpgrade extends Emitter< * * @private */ - private open() { + private _open() { if (this.transports.length === 0) { // Emit error on next tick so it can be listened to this.setTimeoutFn(() => { @@ -542,10 +535,10 @@ export class SocketWithoutUpgrade extends Emitter< // set up transport listeners transport - .on("drain", this.onDrain.bind(this)) - .on("packet", this.onPacket.bind(this)) - .on("error", this.onError.bind(this)) - .on("close", (reason) => this.onClose("transport close", reason)); + .on("drain", this._onDrain.bind(this)) + .on("packet", this._onPacket.bind(this)) + .on("error", this._onError.bind(this)) + .on("close", (reason) => this._onClose("transport close", reason)); } /** @@ -567,7 +560,7 @@ export class SocketWithoutUpgrade extends Emitter< * * @private */ - private onPacket(packet: Packet) { + private _onPacket(packet: Packet) { if ( "opening" === this.readyState || "open" === this.readyState || @@ -579,7 +572,7 @@ export class SocketWithoutUpgrade extends Emitter< // Socket is live - any packet counts this.emitReserved("heartbeat"); - this.resetPingTimeout(); + this._resetPingTimeout(); switch (packet.type) { case "open": @@ -587,7 +580,7 @@ export class SocketWithoutUpgrade extends Emitter< break; case "ping": - this.sendPacket("pong"); + this._sendPacket("pong"); this.emitReserved("ping"); this.emitReserved("pong"); break; @@ -596,7 +589,7 @@ export class SocketWithoutUpgrade extends Emitter< const err = new Error("server error"); // @ts-ignore err.code = packet.data; - this.onError(err); + this._onError(err); break; case "message": @@ -619,13 +612,13 @@ export class SocketWithoutUpgrade extends Emitter< this.emitReserved("handshake", data); this.id = data.sid; this.transport.query.sid = data.sid; - this.pingInterval = data.pingInterval; - this.pingTimeout = data.pingTimeout; - this.maxPayload = data.maxPayload; + this._pingInterval = data.pingInterval; + this._pingTimeout = data.pingTimeout; + this._maxPayload = data.maxPayload; this.onOpen(); // In case open handler closes socket if ("closed" === this.readyState) return; - this.resetPingTimeout(); + this._resetPingTimeout(); } /** @@ -633,13 +626,13 @@ export class SocketWithoutUpgrade extends Emitter< * * @private */ - private resetPingTimeout() { - this.clearTimeoutFn(this.pingTimeoutTimer); - this.pingTimeoutTimer = this.setTimeoutFn(() => { - this.onClose("ping timeout"); - }, this.pingInterval + this.pingTimeout); + private _resetPingTimeout() { + this.clearTimeoutFn(this._pingTimeoutTimer); + this._pingTimeoutTimer = this.setTimeoutFn(() => { + this._onClose("ping timeout"); + }, this._pingInterval + this._pingTimeout); if (this.opts.autoUnref) { - this.pingTimeoutTimer.unref(); + this._pingTimeoutTimer.unref(); } } @@ -648,13 +641,13 @@ export class SocketWithoutUpgrade extends Emitter< * * @private */ - private onDrain() { - this.writeBuffer.splice(0, this.prevBufferLen); + private _onDrain() { + this.writeBuffer.splice(0, this._prevBufferLen); // setting prevBufferLen = 0 is very important // for example, when upgrading, upgrade packet is sent over, // and a nonzero prevBufferLen could cause problems on `drain` - this.prevBufferLen = 0; + this._prevBufferLen = 0; if (0 === this.writeBuffer.length) { this.emitReserved("drain"); @@ -675,12 +668,12 @@ export class SocketWithoutUpgrade extends Emitter< !this.upgrading && this.writeBuffer.length ) { - const packets = this.getWritablePackets(); + const packets = this._getWritablePackets(); debug("flushing %d packets in socket", packets.length); this.transport.send(packets); // keep track of current length of writeBuffer // splice writeBuffer and callbackBuffer on `drain` - this.prevBufferLen = packets.length; + this._prevBufferLen = packets.length; this.emitReserved("flush"); } } @@ -691,9 +684,9 @@ export class SocketWithoutUpgrade extends Emitter< * * @private */ - private getWritablePackets() { + private _getWritablePackets() { const shouldCheckPayloadSize = - this.maxPayload && + this._maxPayload && this.transport.name === "polling" && this.writeBuffer.length > 1; if (!shouldCheckPayloadSize) { @@ -705,13 +698,13 @@ export class SocketWithoutUpgrade extends Emitter< if (data) { payloadSize += byteLength(data); } - if (i > 0 && payloadSize > this.maxPayload) { + if (i > 0 && payloadSize > this._maxPayload) { debug("only send %d out of %d packets", i, this.writeBuffer.length); return this.writeBuffer.slice(0, i); } payloadSize += 2; // separator + packet type } - debug("payload size is %d (max: %d)", payloadSize, this.maxPayload); + debug("payload size is %d (max: %d)", payloadSize, this._maxPayload); return this.writeBuffer; } @@ -724,7 +717,7 @@ export class SocketWithoutUpgrade extends Emitter< * @return {Socket} for chaining. */ public write(msg: RawData, options?: WriteOptions, fn?: () => void) { - this.sendPacket("message", msg, options, fn); + this._sendPacket("message", msg, options, fn); return this; } @@ -737,7 +730,7 @@ export class SocketWithoutUpgrade extends Emitter< * @return {Socket} for chaining. */ public send(msg: RawData, options?: WriteOptions, fn?: () => void) { - this.sendPacket("message", msg, options, fn); + this._sendPacket("message", msg, options, fn); return this; } @@ -750,7 +743,7 @@ export class SocketWithoutUpgrade extends Emitter< * @param {Function} fn - callback function. * @private */ - private sendPacket( + private _sendPacket( type: PacketType, data?: RawData, options?: WriteOptions, @@ -789,7 +782,7 @@ export class SocketWithoutUpgrade extends Emitter< */ public close() { const close = () => { - this.onClose("forced close"); + this._onClose("forced close"); debug("socket closing - telling transport to close"); this.transport.close(); }; @@ -832,7 +825,7 @@ export class SocketWithoutUpgrade extends Emitter< * * @private */ - private onError(err: Error) { + private _onError(err: Error) { debug("socket error %j", err); SocketWithoutUpgrade.priorWebsocketSuccess = false; @@ -843,11 +836,11 @@ export class SocketWithoutUpgrade extends Emitter< ) { debug("trying next transport"); this.transports.shift(); - return this.open(); + return this._open(); } this.emitReserved("error", err); - this.onClose("transport error", err); + this._onClose("transport error", err); } /** @@ -855,7 +848,7 @@ export class SocketWithoutUpgrade extends Emitter< * * @private */ - private onClose(reason: string, description?: CloseDetails | Error) { + private _onClose(reason: string, description?: CloseDetails | Error) { if ( "opening" === this.readyState || "open" === this.readyState || @@ -864,7 +857,7 @@ export class SocketWithoutUpgrade extends Emitter< debug('socket close with reason: "%s"', reason); // clear timers - this.clearTimeoutFn(this.pingTimeoutTimer); + this.clearTimeoutFn(this._pingTimeoutTimer); // stop event from firing again for transport this.transport.removeAllListeners("close"); @@ -878,10 +871,10 @@ export class SocketWithoutUpgrade extends Emitter< if (typeof removeEventListener === "function") { removeEventListener( "beforeunload", - this.beforeunloadEventListener, + this._beforeunloadEventListener, false ); - removeEventListener("offline", this.offlineEventListener, false); + removeEventListener("offline", this._offlineEventListener, false); } // set ready state @@ -896,7 +889,7 @@ export class SocketWithoutUpgrade extends Emitter< // clean buffers after, so users can still // grab the buffers on `close` event this.writeBuffer = []; - this.prevBufferLen = 0; + this._prevBufferLen = 0; } } } @@ -925,17 +918,15 @@ export class SocketWithoutUpgrade extends Emitter< * @see Socket */ export class SocketWithUpgrade extends SocketWithoutUpgrade { - private upgrades: string[] = []; + private _upgrades: string[] = []; override onOpen() { super.onOpen(); if ("open" === this.readyState && this.opts.upgrade) { debug("starting upgrade probes"); - let i = 0; - const l = this.upgrades.length; - for (; i < l; i++) { - this.probe(this.upgrades[i]); + for (let i = 0; i < this._upgrades.length; i++) { + this._probe(this._upgrades[i]); } } } @@ -946,7 +937,7 @@ export class SocketWithUpgrade extends SocketWithoutUpgrade { * @param {String} name - transport name * @private */ - private probe(name: string) { + private _probe(name: string) { debug('probing transport "%s"', name); let transport = this.createTransport(name); let failed = false; @@ -1052,7 +1043,7 @@ export class SocketWithUpgrade extends SocketWithoutUpgrade { this.once("upgrading", onupgrade); if ( - this.upgrades.indexOf("webtransport") !== -1 && + this._upgrades.indexOf("webtransport") !== -1 && name !== "webtransport" ) { // favor WebTransport @@ -1067,7 +1058,7 @@ export class SocketWithUpgrade extends SocketWithoutUpgrade { } override onHandshake(data: HandshakeData) { - this.upgrades = this.filterUpgrades(data.upgrades); + this._upgrades = this._filterUpgrades(data.upgrades); super.onHandshake(data); } @@ -1077,7 +1068,7 @@ export class SocketWithUpgrade extends SocketWithoutUpgrade { * @param {Array} upgrades - server upgrades * @private */ - private filterUpgrades(upgrades: string[]) { + private _filterUpgrades(upgrades: string[]) { const filteredUpgrades = []; for (let i = 0; i < upgrades.length; i++) { if (~this.transports.indexOf(upgrades[i])) diff --git a/lib/transports/polling-xhr.ts b/lib/transports/polling-xhr.ts index ac213546..d24ef75e 100644 --- a/lib/transports/polling-xhr.ts +++ b/lib/transports/polling-xhr.ts @@ -2,10 +2,7 @@ import { Polling } from "./polling.js"; import { Emitter } from "@socket.io/component-emitter"; import type { SocketOptions } from "../socket.js"; import { installTimerFunctions, pick } from "../util.js"; -import { - globalThisShim as globalThis, - createCookieJar, -} from "../globals.node.js"; +import { globalThisShim as globalThis } from "../globals.node.js"; import type { CookieJar } from "../globals.node.js"; import type { RawData } from "engine.io-parser"; import { hasCORS } from "../contrib/has-cors.js"; @@ -104,14 +101,14 @@ export class Request extends Emitter< Record, RequestReservedEvents > { - private readonly opts: RequestOptions; - private readonly method: string; - private readonly uri: string; - private readonly data: string | ArrayBuffer; + private readonly _opts: RequestOptions; + private readonly _method: string; + private readonly _uri: string; + private readonly _data: string | ArrayBuffer; - private xhr: any; + private _xhr: any; private setTimeoutFn: typeof setTimeout; - private index: number; + private _index: number; static requestsCount = 0; static requests = {}; @@ -129,13 +126,13 @@ export class Request extends Emitter< ) { super(); installTimerFunctions(this, opts); - this.opts = opts; + this._opts = opts; - this.method = opts.method || "GET"; - this.uri = uri; - this.data = undefined !== opts.data ? opts.data : null; + this._method = opts.method || "GET"; + this._uri = uri; + this._data = undefined !== opts.data ? opts.data : null; - this.create(); + this._create(); } /** @@ -143,9 +140,9 @@ export class Request extends Emitter< * * @private */ - private create() { + private _create() { const opts = pick( - this.opts, + this._opts, "agent", "pfx", "key", @@ -156,26 +153,26 @@ export class Request extends Emitter< "rejectUnauthorized", "autoUnref" ); - opts.xdomain = !!this.opts.xd; + opts.xdomain = !!this._opts.xd; - const xhr = (this.xhr = this.createRequest(opts)); + const xhr = (this._xhr = this.createRequest(opts)); try { - debug("xhr open %s: %s", this.method, this.uri); - xhr.open(this.method, this.uri, true); + debug("xhr open %s: %s", this._method, this._uri); + xhr.open(this._method, this._uri, true); try { - if (this.opts.extraHeaders) { + if (this._opts.extraHeaders) { // @ts-ignore xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true); - for (let i in this.opts.extraHeaders) { - if (this.opts.extraHeaders.hasOwnProperty(i)) { - xhr.setRequestHeader(i, this.opts.extraHeaders[i]); + for (let i in this._opts.extraHeaders) { + if (this._opts.extraHeaders.hasOwnProperty(i)) { + xhr.setRequestHeader(i, this._opts.extraHeaders[i]); } } } } catch (e) {} - if ("POST" === this.method) { + if ("POST" === this._method) { try { xhr.setRequestHeader("Content-type", "text/plain;charset=UTF-8"); } catch (e) {} @@ -185,20 +182,20 @@ export class Request extends Emitter< xhr.setRequestHeader("Accept", "*/*"); } catch (e) {} - this.opts.cookieJar?.addCookies(xhr); + this._opts.cookieJar?.addCookies(xhr); // ie6 check if ("withCredentials" in xhr) { - xhr.withCredentials = this.opts.withCredentials; + xhr.withCredentials = this._opts.withCredentials; } - if (this.opts.requestTimeout) { - xhr.timeout = this.opts.requestTimeout; + if (this._opts.requestTimeout) { + xhr.timeout = this._opts.requestTimeout; } xhr.onreadystatechange = () => { if (xhr.readyState === 3) { - this.opts.cookieJar?.parseCookies( + this._opts.cookieJar?.parseCookies( // @ts-ignore xhr.getResponseHeader("set-cookie") ); @@ -206,31 +203,31 @@ export class Request extends Emitter< if (4 !== xhr.readyState) return; if (200 === xhr.status || 1223 === xhr.status) { - this.onLoad(); + this._onLoad(); } else { // make sure the `error` event handler that's user-set // does not throw in the same tick and gets caught here this.setTimeoutFn(() => { - this.onError(typeof xhr.status === "number" ? xhr.status : 0); + this._onError(typeof xhr.status === "number" ? xhr.status : 0); }, 0); } }; - debug("xhr data %s", this.data); - xhr.send(this.data); + debug("xhr data %s", this._data); + xhr.send(this._data); } catch (e) { // Need to defer since .create() is called directly from the constructor // and thus the 'error' event can only be only bound *after* this exception // occurs. Therefore, also, we cannot throw here at all. this.setTimeoutFn(() => { - this.onError(e); + this._onError(e); }, 0); return; } if (typeof document !== "undefined") { - this.index = Request.requestsCount++; - Request.requests[this.index] = this; + this._index = Request.requestsCount++; + Request.requests[this._index] = this; } } @@ -239,9 +236,9 @@ export class Request extends Emitter< * * @private */ - private onError(err: number | Error) { - this.emitReserved("error", err, this.xhr); - this.cleanup(true); + private _onError(err: number | Error) { + this.emitReserved("error", err, this._xhr); + this._cleanup(true); } /** @@ -249,23 +246,23 @@ export class Request extends Emitter< * * @private */ - private cleanup(fromError?) { - if ("undefined" === typeof this.xhr || null === this.xhr) { + private _cleanup(fromError?) { + if ("undefined" === typeof this._xhr || null === this._xhr) { return; } - this.xhr.onreadystatechange = empty; + this._xhr.onreadystatechange = empty; if (fromError) { try { - this.xhr.abort(); + this._xhr.abort(); } catch (e) {} } if (typeof document !== "undefined") { - delete Request.requests[this.index]; + delete Request.requests[this._index]; } - this.xhr = null; + this._xhr = null; } /** @@ -273,12 +270,12 @@ export class Request extends Emitter< * * @private */ - private onLoad() { - const data = this.xhr.responseText; + private _onLoad() { + const data = this._xhr.responseText; if (data !== null) { this.emitReserved("data", data); this.emitReserved("success"); - this.cleanup(); + this._cleanup(); } } @@ -288,7 +285,7 @@ export class Request extends Emitter< * @package */ public abort() { - this.cleanup(); + this._cleanup(); } } diff --git a/lib/transports/polling.ts b/lib/transports/polling.ts index 0750c733..12e270ff 100644 --- a/lib/transports/polling.ts +++ b/lib/transports/polling.ts @@ -6,7 +6,7 @@ import debugModule from "debug"; // debug() const debug = debugModule("engine.io-client:polling"); // debug() export abstract class Polling extends Transport { - private polling: boolean = false; + private _polling: boolean = false; override get name() { return "polling"; @@ -19,7 +19,7 @@ export abstract class Polling extends Transport { * @protected */ override doOpen() { - this.poll(); + this._poll(); } /** @@ -37,10 +37,10 @@ export abstract class Polling extends Transport { onPause(); }; - if (this.polling || !this.writable) { + if (this._polling || !this.writable) { let total = 0; - if (this.polling) { + if (this._polling) { debug("we are currently polling - waiting to pause"); total++; this.once("pollComplete", function () { @@ -67,9 +67,9 @@ export abstract class Polling extends Transport { * * @private */ - poll() { + private _poll() { debug("polling"); - this.polling = true; + this._polling = true; this.doPoll(); this.emitReserved("poll"); } @@ -103,11 +103,11 @@ export abstract class Polling extends Transport { // if an event did not trigger closing if ("closed" !== this.readyState) { // if we got data we're not polling - this.polling = false; + this._polling = false; this.emitReserved("pollComplete"); if ("open" === this.readyState) { - this.poll(); + this._poll(); } else { debug('ignoring poll - transport state "%s"', this.readyState); } diff --git a/lib/transports/webtransport.ts b/lib/transports/webtransport.ts index 74238197..0062c3e5 100644 --- a/lib/transports/webtransport.ts +++ b/lib/transports/webtransport.ts @@ -18,8 +18,8 @@ const debug = debugModule("engine.io-client:webtransport"); // debug() * @see https://caniuse.com/webtransport */ export class WT extends Transport { - private transport: any; - private writer: any; + private _transport: any; + private _writer: any; get name() { return "webtransport"; @@ -28,7 +28,7 @@ export class WT extends Transport { protected doOpen() { try { // @ts-ignore - this.transport = new WebTransport( + this._transport = new WebTransport( this.createUri("https"), this.opts.transportOptions[this.name] ); @@ -36,7 +36,7 @@ export class WT extends Transport { return this.emitReserved("error", err); } - this.transport.closed + this._transport.closed .then(() => { debug("transport closed gracefully"); this.onClose(); @@ -47,8 +47,8 @@ export class WT extends Transport { }); // note: we could have used async/await, but that would require some additional polyfills - this.transport.ready.then(() => { - this.transport.createBidirectionalStream().then((stream) => { + this._transport.ready.then(() => { + this._transport.createBidirectionalStream().then((stream) => { const decoderStream = createPacketDecoderStream( Number.MAX_SAFE_INTEGER, this.socket.binaryType @@ -57,7 +57,7 @@ export class WT extends Transport { const encoderStream = createPacketEncoderStream(); encoderStream.readable.pipeTo(stream.writable); - this.writer = encoderStream.writable.getWriter(); + this._writer = encoderStream.writable.getWriter(); const read = () => { reader @@ -82,7 +82,7 @@ export class WT extends Transport { if (this.query.sid) { packet.data = `{"sid":"${this.query.sid}"}`; } - this.writer.write(packet).then(() => this.onOpen()); + this._writer.write(packet).then(() => this.onOpen()); }); }); } @@ -94,7 +94,7 @@ export class WT extends Transport { const packet = packets[i]; const lastPacket = i === packets.length - 1; - this.writer.write(packet).then(() => { + this._writer.write(packet).then(() => { if (lastPacket) { nextTick(() => { this.writable = true; @@ -106,6 +106,6 @@ export class WT extends Transport { } protected doClose() { - this.transport?.close(); + this._transport?.close(); } } diff --git a/support/rollup.config.esm.js b/support/rollup.config.esm.js index 9f910341..44f7837b 100644 --- a/support/rollup.config.esm.js +++ b/support/rollup.config.esm.js @@ -14,7 +14,15 @@ module.exports = { file: "./dist/engine.io.esm.min.js", format: "esm", sourcemap: true, - plugins: [terser()], + plugins: [ + terser({ + mangle: { + properties: { + regex: /^_/, + }, + }, + }), + ], banner, }, plugins: [ diff --git a/support/rollup.config.umd.js b/support/rollup.config.umd.js index d87c25cc..2b8a5300 100644 --- a/support/rollup.config.umd.js +++ b/support/rollup.config.umd.js @@ -47,7 +47,15 @@ module.exports = [ format: "umd", name: "eio", sourcemap: true, - plugins: [terser()], + plugins: [ + terser({ + mangle: { + properties: { + regex: /^_/, + }, + }, + }), + ], banner, }, plugins: [ diff --git a/test/socket.js b/test/socket.js index ea4de6c0..e1f999f1 100644 --- a/test/socket.js +++ b/test/socket.js @@ -16,7 +16,7 @@ describe("Socket", function () { describe("filterUpgrades", () => { it("should return only available transports", () => { const socket = new Socket({ transports: ["polling"] }); - expect(socket.filterUpgrades(["polling", "websocket"])).to.eql([ + expect(socket._filterUpgrades(["polling", "websocket"])).to.eql([ "polling", ]); socket.close(); @@ -29,7 +29,6 @@ describe("Socket", function () { socket.on("error", (error) => { errorMessage = error; }); - socket.open(); setTimeout(() => { expect(errorMessage).to.be("No transports available"); socket.close(); @@ -135,7 +134,6 @@ describe("Socket", function () { socket.on("error", (error) => { errorMessage = error; }); - socket.open(); clock.tick(1); // Should trigger error emit. expect(errorMessage).to.be("No transports available"); clock.uninstall();