Add VerifyLogger to JS tests (#2472)
This commit is contained in:
parent
7317762b29
commit
cb8264321d
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { EOL } from "os";
|
||||
import { ILogger, LogLevel } from "../src/ILogger";
|
||||
import { HttpTransportType } from "../src/ITransport";
|
||||
|
||||
export function eachTransport(action: (transport: HttpTransportType) => void) {
|
||||
|
|
@ -21,3 +23,38 @@ export function eachEndpointUrl(action: (givenUrl: string, expectedUrl: string)
|
|||
|
||||
urls.forEach((t) => action(t[0], t[1]));
|
||||
}
|
||||
|
||||
type ErrorMatchFunction = (error: string) => boolean;
|
||||
|
||||
export class VerifyLogger implements ILogger {
|
||||
public unexpectedErrors: string[];
|
||||
private expectedErrors: ErrorMatchFunction[];
|
||||
|
||||
public constructor(...expectedErrors: Array<RegExp | string | ErrorMatchFunction>) {
|
||||
this.unexpectedErrors = [];
|
||||
this.expectedErrors = [];
|
||||
expectedErrors.forEach((element) => {
|
||||
if (element instanceof RegExp) {
|
||||
this.expectedErrors.push((e) => element.test(e));
|
||||
} else if (typeof element === "string") {
|
||||
this.expectedErrors.push((e) => element === e);
|
||||
} else {
|
||||
this.expectedErrors.push(element);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
public static async run(fn: (logger: VerifyLogger) => Promise<void>, ...expectedErrors: Array<RegExp | string | ErrorMatchFunction>): Promise<void> {
|
||||
const logger = new VerifyLogger(...expectedErrors);
|
||||
await fn(logger);
|
||||
expect(logger.unexpectedErrors.join(EOL)).toBe("");
|
||||
}
|
||||
|
||||
public log(logLevel: LogLevel, message: string): void {
|
||||
if (logLevel >= LogLevel.Error) {
|
||||
if (!this.expectedErrors.some((fn) => fn(message))) {
|
||||
this.unexpectedErrors.push(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import { HttpTransportType, ITransport, TransferFormat } from "../src/ITransport
|
|||
|
||||
import { HttpError } from "../src/Errors";
|
||||
import { NullLogger } from "../src/Loggers";
|
||||
import { EventSourceConstructor } from "../src/Polyfills";
|
||||
import { EventSourceConstructor, WebSocketConstructor } from "../src/Polyfills";
|
||||
|
||||
import { eachEndpointUrl, eachTransport } from "./Common";
|
||||
import { eachEndpointUrl, eachTransport, VerifyLogger } from "./Common";
|
||||
import { TestHttpClient } from "./TestHttpClient";
|
||||
import { PromiseSource } from "./Utils";
|
||||
|
||||
|
|
@ -42,11 +42,13 @@ describe("HttpConnection", () => {
|
|||
});
|
||||
|
||||
it("starting connection fails if getting id fails", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => Promise.reject("error"))
|
||||
.on("GET", () => ""),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -54,9 +56,13 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("error");
|
||||
},
|
||||
"Failed to start the connection: error",
|
||||
"Failed to complete negotiation with the server: error");
|
||||
});
|
||||
|
||||
it("cannot start a running connection", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const negotiating = new PromiseSource();
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
|
|
@ -65,6 +71,7 @@ describe("HttpConnection", () => {
|
|||
negotiating.resolve();
|
||||
return defaultNegotiateResponse;
|
||||
}),
|
||||
logger,
|
||||
transport: {
|
||||
connect() {
|
||||
return Promise.resolve();
|
||||
|
|
@ -91,8 +98,10 @@ describe("HttpConnection", () => {
|
|||
await connection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can start a stopped connection", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
|
|
@ -100,6 +109,7 @@ describe("HttpConnection", () => {
|
|||
return Promise.reject("reached negotiate");
|
||||
})
|
||||
.on("GET", () => ""),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -111,9 +121,13 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("reached negotiate");
|
||||
},
|
||||
"Failed to complete negotiation with the server: reached negotiate",
|
||||
"Failed to start the connection: reached negotiate");
|
||||
});
|
||||
|
||||
it("can stop a starting connection", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
|
|
@ -125,24 +139,30 @@ describe("HttpConnection", () => {
|
|||
await connection.stop();
|
||||
return "";
|
||||
}),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
await connection.start(TransferFormat.Text);
|
||||
});
|
||||
});
|
||||
|
||||
it("can stop a non-started connection", async () => {
|
||||
const connection = new HttpConnection("http://tempuri.org", commonOptions);
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new HttpConnection("http://tempuri.org", { ...commonOptions, logger });
|
||||
await connection.stop();
|
||||
});
|
||||
});
|
||||
|
||||
it("start throws after all transports fail", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [] }))
|
||||
.on("GET", () => { throw new Error("fail"); }),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org?q=myData", options);
|
||||
|
|
@ -150,9 +170,12 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("Unable to initialize any of the available transports.");
|
||||
},
|
||||
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
|
||||
});
|
||||
|
||||
it("preserves user's query string", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connectUrl = new PromiseSource<string>();
|
||||
const fakeTransport: ITransport = {
|
||||
connect(url: string): Promise<void> {
|
||||
|
|
@ -174,6 +197,7 @@ describe("HttpConnection", () => {
|
|||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => "{ \"connectionId\": \"42\" }")
|
||||
.on("GET", () => ""),
|
||||
logger,
|
||||
transport: fakeTransport,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -188,9 +212,11 @@ describe("HttpConnection", () => {
|
|||
await connection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
eachEndpointUrl((givenUrl: string, expectedUrl: string) => {
|
||||
it(`negotiate request for '${givenUrl}' puts 'negotiate' at the end of the path`, async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const negotiateUrl = new PromiseSource<string>();
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
|
|
@ -203,6 +229,7 @@ describe("HttpConnection", () => {
|
|||
return new HttpResponse(204);
|
||||
})
|
||||
.on("DELETE", () => new HttpResponse(202)),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection(givenUrl, options);
|
||||
|
|
@ -215,11 +242,15 @@ describe("HttpConnection", () => {
|
|||
} finally {
|
||||
await connection.stop();
|
||||
}
|
||||
},
|
||||
"Failed to complete negotiation with the server: Error: We don't care how this turns out",
|
||||
"Failed to start the connection: Error: We don't care how this turns out");
|
||||
});
|
||||
});
|
||||
|
||||
eachTransport((requestedTransport: HttpTransportType) => {
|
||||
it(`cannot be started if requested ${HttpTransportType[requestedTransport]} transport not available on server`, async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
// Clone the default response
|
||||
const negotiateResponse = { ...defaultNegotiateResponse };
|
||||
|
||||
|
|
@ -232,6 +263,7 @@ describe("HttpConnection", () => {
|
|||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => negotiateResponse)
|
||||
.on("GET", () => new HttpResponse(204)),
|
||||
logger,
|
||||
transport: requestedTransport,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -240,34 +272,12 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("Unable to initialize any of the available transports.");
|
||||
},
|
||||
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
|
||||
});
|
||||
|
||||
for (const [val, name] of [[null, "null"], [undefined, "undefined"], [0, "0"]]) {
|
||||
it(`can be started using ${HttpTransportType[requestedTransport]} transport when transport mask is ${name}`, async () => {
|
||||
const negotiateResponse = {
|
||||
availableTransports: [
|
||||
{ transport: "WebSockets", transferFormats: ["Text", "Binary"] },
|
||||
{ transport: "ServerSentEvents", transferFormats: ["Text"] },
|
||||
{ transport: "LongPolling", transferFormats: ["Text", "Binary"] },
|
||||
],
|
||||
connectionId: "abc123",
|
||||
};
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => negotiateResponse)
|
||||
.on("GET", () => new HttpResponse(204)),
|
||||
transport: val,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
await connection.start(TransferFormat.Text);
|
||||
});
|
||||
}
|
||||
|
||||
it(`cannot be started if server's only transport (${HttpTransportType[requestedTransport]}) is masked out by the transport option`, async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const negotiateResponse = {
|
||||
availableTransports: [
|
||||
{ transport: "WebSockets", transferFormats: ["Text", "Binary"] },
|
||||
|
|
@ -289,6 +299,7 @@ describe("HttpConnection", () => {
|
|||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => negotiateResponse)
|
||||
.on("GET", () => new HttpResponse(204)),
|
||||
logger,
|
||||
transport: transportMask,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -300,27 +311,57 @@ describe("HttpConnection", () => {
|
|||
} catch (e) {
|
||||
expect(e.message).toBe("Unable to initialize any of the available transports.");
|
||||
}
|
||||
},
|
||||
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
|
||||
});
|
||||
});
|
||||
|
||||
for (const [val, name] of [[null, "null"], [undefined, "undefined"], [0, "0"]]) {
|
||||
it(`can be started when transport mask is ${name}`, async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => defaultNegotiateResponse)
|
||||
.on("GET", () => new HttpResponse(200))
|
||||
.on("DELETE", () => new HttpResponse(202)),
|
||||
logger,
|
||||
transport: val,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
await connection.start(TransferFormat.Text);
|
||||
|
||||
await connection.stop();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it("cannot be started if no transport available on server and no transport requested", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [] }))
|
||||
.on("GET", () => ""),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("Unable to initialize any of the available transports.");
|
||||
},
|
||||
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
|
||||
});
|
||||
|
||||
it("does not send negotiate request if WebSockets transport requested explicitly and skipNegotiation is true", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient(),
|
||||
logger,
|
||||
skipNegotiation: true,
|
||||
transport: HttpTransportType.WebSockets,
|
||||
} as IHttpConnectionOptions;
|
||||
|
|
@ -329,12 +370,16 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("'WebSocket' is not supported in your environment.");
|
||||
},
|
||||
"Failed to start the connection: Error: 'WebSocket' is not supported in your environment.");
|
||||
});
|
||||
|
||||
it("does not start non WebSockets transport requested explicitly and skipNegotiation is true", async () => {
|
||||
it("does not start non WebSockets transport if requested explicitly and skipNegotiation is true", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient(),
|
||||
logger,
|
||||
skipNegotiation: true,
|
||||
transport: HttpTransportType.LongPolling,
|
||||
} as IHttpConnectionOptions;
|
||||
|
|
@ -343,9 +388,12 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("Negotiation can only be skipped when using the WebSocket transport directly.");
|
||||
},
|
||||
"Failed to start the connection: Error: Negotiation can only be skipped when using the WebSocket transport directly.");
|
||||
});
|
||||
|
||||
it("redirects to url when negotiate returns it", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let firstNegotiate = true;
|
||||
let firstPoll = true;
|
||||
const httpClient = new TestHttpClient()
|
||||
|
|
@ -371,6 +419,7 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient,
|
||||
logger,
|
||||
transport: HttpTransportType.LongPolling,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -387,14 +436,17 @@ describe("HttpConnection", () => {
|
|||
await connection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("fails to start if negotiate redirects more than 100 times", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const httpClient = new TestHttpClient()
|
||||
.on("POST", /negotiate$/, () => ({ url: "https://another.domain.url/chat" }));
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient,
|
||||
logger,
|
||||
transport: HttpTransportType.LongPolling,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -402,9 +454,12 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("Negotiate redirection limit exceeded.");
|
||||
},
|
||||
"Failed to start the connection: Error: Negotiate redirection limit exceeded.");
|
||||
});
|
||||
|
||||
it("redirects to url when negotiate returns it with access token", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let firstNegotiate = true;
|
||||
let firstPoll = true;
|
||||
const httpClient = new TestHttpClient()
|
||||
|
|
@ -445,6 +500,7 @@ describe("HttpConnection", () => {
|
|||
...commonOptions,
|
||||
accessTokenFactory: () => "firstSecret",
|
||||
httpClient,
|
||||
logger,
|
||||
transport: HttpTransportType.LongPolling,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -461,8 +517,10 @@ describe("HttpConnection", () => {
|
|||
await connection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("authorization header removed when token factory returns null and using LongPolling", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const availableTransport = { transport: "LongPolling", transferFormats: ["Text"] };
|
||||
|
||||
let httpClientGetCount = 0;
|
||||
|
|
@ -495,10 +553,10 @@ describe("HttpConnection", () => {
|
|||
if (authorizationValue) {
|
||||
fail("Second long poll request should have no authorization header.");
|
||||
}
|
||||
throw new Error("fail");
|
||||
}
|
||||
})
|
||||
.on("DELETE", () => new HttpResponse(202)),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -511,8 +569,10 @@ describe("HttpConnection", () => {
|
|||
await connection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("sets inherentKeepAlive feature when using LongPolling", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const availableTransport = { transport: "LongPolling", transferFormats: ["Text"] };
|
||||
|
||||
let httpClientGetCount = 0;
|
||||
|
|
@ -525,11 +585,10 @@ describe("HttpConnection", () => {
|
|||
if (httpClientGetCount === 1) {
|
||||
// First long polling request must succeed so start completes
|
||||
return "";
|
||||
} else {
|
||||
throw new Error("fail");
|
||||
}
|
||||
})
|
||||
.on("DELETE", () => new HttpResponse(202)),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -540,14 +599,17 @@ describe("HttpConnection", () => {
|
|||
await connection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("does not select ServerSentEvents transport when not available in environment", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const serverSentEventsTransport = { transport: "ServerSentEvents", transferFormats: ["Text"] };
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [serverSentEventsTransport] })),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -555,15 +617,19 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("Unable to initialize any of the available transports.");
|
||||
},
|
||||
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
|
||||
});
|
||||
|
||||
it("does not select WebSockets transport when not available in environment", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const webSocketsTransport = { transport: "WebSockets", transferFormats: ["Text"] };
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [webSocketsTransport] })),
|
||||
logger,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -571,6 +637,8 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("Unable to initialize any of the available transports.");
|
||||
},
|
||||
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
|
||||
});
|
||||
|
||||
describe(".constructor", () => {
|
||||
|
|
@ -580,6 +648,7 @@ describe("HttpConnection", () => {
|
|||
});
|
||||
|
||||
it("uses global WebSocket if defined", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
global["WebSocket"] = class WebSocket {
|
||||
constructor() {
|
||||
|
|
@ -589,6 +658,7 @@ describe("HttpConnection", () => {
|
|||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
logger,
|
||||
skipNegotiation: true,
|
||||
transport: HttpTransportType.WebSockets,
|
||||
} as IHttpConnectionOptions;
|
||||
|
|
@ -601,9 +671,12 @@ describe("HttpConnection", () => {
|
|||
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
delete global["WebSocket"];
|
||||
},
|
||||
"Failed to start the connection: Error: WebSocket constructor called.");
|
||||
});
|
||||
|
||||
it("uses global EventSource if defined", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let eventSourceConstructorCalled: boolean = false;
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
global["EventSource"] = class EventSource {
|
||||
|
|
@ -623,6 +696,7 @@ describe("HttpConnection", () => {
|
|||
connectionId: defaultConnectionId,
|
||||
};
|
||||
}),
|
||||
logger,
|
||||
transport: HttpTransportType.ServerSentEvents,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -636,9 +710,13 @@ describe("HttpConnection", () => {
|
|||
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
delete global["EventSource"];
|
||||
},
|
||||
"Failed to start the transport 'ServerSentEvents': Error: EventSource constructor called.",
|
||||
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
|
||||
});
|
||||
|
||||
it("uses EventSource constructor from options if provided", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let eventSourceConstructorCalled: boolean = false;
|
||||
|
||||
class TestEventSource {
|
||||
|
|
@ -661,6 +739,7 @@ describe("HttpConnection", () => {
|
|||
connectionId: defaultConnectionId,
|
||||
};
|
||||
}),
|
||||
logger,
|
||||
transport: HttpTransportType.ServerSentEvents,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -671,9 +750,13 @@ describe("HttpConnection", () => {
|
|||
.toThrow("Unable to initialize any of the available transports.");
|
||||
|
||||
expect(eventSourceConstructorCalled).toEqual(true);
|
||||
},
|
||||
"Failed to start the transport 'ServerSentEvents': Error: EventSource constructor called.",
|
||||
"Failed to start the connection: Error: Unable to initialize any of the available transports.");
|
||||
});
|
||||
|
||||
it("uses WebSocket constructor from options if provided", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
class TestWebSocket {
|
||||
// The "_" prefix tell TypeScript not to worry about unused parameter, but tslint doesn't like it.
|
||||
// tslint:disable-next-line:variable-name
|
||||
|
|
@ -684,7 +767,8 @@ describe("HttpConnection", () => {
|
|||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
WebSocket: TestWebSocket,
|
||||
WebSocket: TestWebSocket as WebSocketConstructor,
|
||||
logger,
|
||||
skipNegotiation: true,
|
||||
transport: HttpTransportType.WebSockets,
|
||||
} as IHttpConnectionOptions;
|
||||
|
|
@ -694,15 +778,19 @@ describe("HttpConnection", () => {
|
|||
await expect(connection.start())
|
||||
.rejects
|
||||
.toThrow("WebSocket constructor called.");
|
||||
},
|
||||
"Failed to start the connection: Error: WebSocket constructor called.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("startAsync", () => {
|
||||
it("throws if an unsupported TransferFormat is provided", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
// Force TypeScript to let us call start incorrectly
|
||||
const connection: any = new HttpConnection("http://tempuri.org", commonOptions);
|
||||
const connection: any = new HttpConnection("http://tempuri.org", { ...commonOptions, logger });
|
||||
|
||||
expect(() => connection.start(42)).toThrowError("Unknown transferFormat value: 42.");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { NullLogger } from "../src/Loggers";
|
|||
import { IStreamSubscriber } from "../src/Stream";
|
||||
import { TextMessageFormat } from "../src/TextMessageFormat";
|
||||
|
||||
import { VerifyLogger } from "./Common";
|
||||
import { delay, PromiseSource } from "./Utils";
|
||||
|
||||
function createHubConnection(connection: IConnection, logger?: ILogger | null, protocol?: IHubProtocol | null) {
|
||||
|
|
@ -21,8 +22,9 @@ describe("HubConnection", () => {
|
|||
|
||||
describe("start", () => {
|
||||
it("sends negotiation message", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
await hubConnection.start();
|
||||
expect(connection.sentData.length).toBe(1);
|
||||
|
|
@ -34,10 +36,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("state connected", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
expect(hubConnection.state).toBe(HubConnectionState.Disconnected);
|
||||
try {
|
||||
await hubConnection.start();
|
||||
|
|
@ -47,11 +51,13 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("ping", () => {
|
||||
it("automatically sends multiple pings", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
|
||||
hubConnection.keepAliveIntervalInMilliseconds = 5;
|
||||
|
||||
|
|
@ -66,11 +72,13 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("stop", () => {
|
||||
it("state disconnected", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
expect(hubConnection.state).toBe(HubConnectionState.Disconnected);
|
||||
try {
|
||||
await hubConnection.start();
|
||||
|
|
@ -81,12 +89,14 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("send", () => {
|
||||
it("sends a non blocking invocation", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
// We don't actually care to wait for the send.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
|
|
@ -109,12 +119,14 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("invoke", () => {
|
||||
it("sends an invocation", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
// We don't actually care to wait for the send.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
|
|
@ -138,8 +150,10 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can process handshake from text", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let protocolCalled = false;
|
||||
|
||||
const mockProtocol = new TestProtocol(TransferFormat.Text);
|
||||
|
|
@ -148,7 +162,7 @@ describe("HubConnection", () => {
|
|||
};
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection, null, mockProtocol);
|
||||
const hubConnection = createHubConnection(connection, logger, mockProtocol);
|
||||
try {
|
||||
const data = "{}" + TextMessageFormat.RecordSeparator;
|
||||
|
||||
|
|
@ -160,8 +174,10 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can process handshake from binary", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let protocolCalled = false;
|
||||
|
||||
const mockProtocol = new TestProtocol(TransferFormat.Binary);
|
||||
|
|
@ -170,7 +186,7 @@ describe("HubConnection", () => {
|
|||
};
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection, null, mockProtocol);
|
||||
const hubConnection = createHubConnection(connection, logger, mockProtocol);
|
||||
try {
|
||||
// handshake response + message separator
|
||||
const data = [0x7b, 0x7d, 0x1e];
|
||||
|
|
@ -183,15 +199,17 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can process handshake and additional messages from binary", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let receivedProcotolData: ArrayBuffer | undefined;
|
||||
|
||||
const mockProtocol = new TestProtocol(TransferFormat.Binary);
|
||||
mockProtocol.onreceive = (d) => receivedProcotolData = d as ArrayBuffer;
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection, null, mockProtocol);
|
||||
const hubConnection = createHubConnection(connection, logger, mockProtocol);
|
||||
try {
|
||||
// handshake response + message separator + message pack message
|
||||
const data = [
|
||||
|
|
@ -211,15 +229,17 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can process handshake and additional messages from text", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let receivedProcotolData: string | undefined;
|
||||
|
||||
const mockProtocol = new TestProtocol(TransferFormat.Text);
|
||||
mockProtocol.onreceive = (d) => receivedProcotolData = d as string;
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection, null, mockProtocol);
|
||||
const hubConnection = createHubConnection(connection, logger, mockProtocol);
|
||||
try {
|
||||
const data = "{}" + TextMessageFormat.RecordSeparator + "{\"type\":6}" + TextMessageFormat.RecordSeparator;
|
||||
|
||||
|
|
@ -230,10 +250,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects the promise when an error is received", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -246,10 +268,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves the promise when a result is received", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -262,11 +286,13 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("completes pending invocations when stopped", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -275,11 +301,13 @@ describe("HubConnection", () => {
|
|||
|
||||
await expect(invokePromise).rejects.toThrow("Invocation canceled due to connection being closed.");
|
||||
});
|
||||
});
|
||||
|
||||
it("completes pending invocations when connection is lost", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -293,19 +321,22 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("on", () => {
|
||||
it("invocations ignored in callbacks not registered", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const warnings: string[] = [];
|
||||
const logger = {
|
||||
const wrappingLogger = {
|
||||
log: (logLevel: LogLevel, message: string) => {
|
||||
if (logLevel === LogLevel.Warning) {
|
||||
warnings.push(message);
|
||||
}
|
||||
logger.log(logLevel, message);
|
||||
},
|
||||
} as ILogger;
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
const hubConnection = createHubConnection(connection, wrappingLogger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -321,18 +352,21 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("invocations ignored in callbacks that have registered then unregistered", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const warnings: string[] = [];
|
||||
const logger = {
|
||||
const wrappingLogger = {
|
||||
log: (logLevel: LogLevel, message: string) => {
|
||||
if (logLevel === LogLevel.Warning) {
|
||||
warnings.push(message);
|
||||
}
|
||||
logger.log(logLevel, message);
|
||||
},
|
||||
} as ILogger;
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
const hubConnection = createHubConnection(connection, wrappingLogger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -353,10 +387,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("all handlers can be unregistered with just the method name", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -368,7 +404,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: [],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "inc",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -378,7 +413,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: [],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "inc",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -389,10 +423,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("a single handler can be unregistered with the method name and handler", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -404,7 +440,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: [],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "inc",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -414,7 +449,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: [],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "inc",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -425,10 +459,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can't register the same handler multiple times", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -439,7 +475,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: [],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "inc",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -450,10 +485,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("callback invoked when servers invokes a method on the client", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -462,7 +499,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: ["test"],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -473,10 +509,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("stop on handshake error", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
let closeError: Error | undefined;
|
||||
hubConnection.onclose((e) => closeError = e);
|
||||
|
|
@ -487,11 +525,14 @@ describe("HubConnection", () => {
|
|||
} finally {
|
||||
await hubConnection.stop();
|
||||
}
|
||||
},
|
||||
"Server returned handshake error: Error!");
|
||||
});
|
||||
|
||||
it("stop on close message", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
let isClosed = false;
|
||||
let closeError: Error | undefined;
|
||||
|
|
@ -512,10 +553,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("stop on error close message", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
let isClosed = false;
|
||||
let closeError: Error | undefined;
|
||||
|
|
@ -537,10 +580,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can have multiple callbacks", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -551,7 +596,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: [],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -563,10 +607,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can unsubscribe from on", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -576,7 +622,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: [],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -586,7 +631,6 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({
|
||||
arguments: [],
|
||||
invocationId: "0",
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
|
|
@ -597,10 +641,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("unsubscribing from non-existing callbacks no-ops", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
hubConnection.off("_", () => { });
|
||||
hubConnection.on("message", (t) => { });
|
||||
|
|
@ -609,20 +655,22 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("using null/undefined for methodName or method no-ops", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const warnings: string[] = [];
|
||||
const logger = {
|
||||
const wrappingLogger = {
|
||||
log(logLevel: LogLevel, message: string) {
|
||||
if (logLevel === LogLevel.Warning) {
|
||||
warnings.push(message);
|
||||
}
|
||||
|
||||
logger.log(logLevel, message);
|
||||
},
|
||||
} as ILogger;
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
const hubConnection = createHubConnection(connection, wrappingLogger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -655,12 +703,14 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("stream", () => {
|
||||
it("sends an invocation", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
hubConnection.stream("testStream", "arg", 42);
|
||||
|
||||
|
|
@ -682,10 +732,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("completes with an error when an error is yielded", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -700,10 +752,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("completes the observer when a completion is received", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -718,11 +772,13 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("completes pending streams when stopped", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
const observer = new TestObserver();
|
||||
hubConnection.stream<any>("testMethod")
|
||||
|
|
@ -734,11 +790,13 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("completes pending streams when connection is lost", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
const observer = new TestObserver();
|
||||
hubConnection.stream<any>("testMethod")
|
||||
|
|
@ -752,10 +810,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("yields items as they arrive", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -778,11 +838,13 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("does not require error function registered", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
hubConnection.stream("testMethod").subscribe(NullSubscriber.instance);
|
||||
|
||||
|
|
@ -793,11 +855,13 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("does not require complete function registered", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
hubConnection.stream("testMethod").subscribe(NullSubscriber.instance);
|
||||
|
||||
|
|
@ -808,10 +872,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("can be canceled", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
connection.receiveHandshakeResponse();
|
||||
|
||||
|
|
@ -839,11 +905,13 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("onClose", () => {
|
||||
it("can have multiple callbacks", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
let invocations = 0;
|
||||
hubConnection.onclose((e) => invocations++);
|
||||
|
|
@ -855,10 +923,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("callbacks receive error", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
let error: Error | undefined;
|
||||
hubConnection.onclose((e) => error = e);
|
||||
|
|
@ -870,10 +940,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores null callbacks", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
hubConnection.onclose(null!);
|
||||
hubConnection.onclose(undefined!);
|
||||
|
|
@ -884,10 +956,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("state disconnected", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
let state: HubConnectionState | undefined;
|
||||
hubConnection.onclose((e) => state = hubConnection.state);
|
||||
|
|
@ -900,13 +974,15 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("keepAlive", () => {
|
||||
it("can receive ping messages", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
// Receive the ping mid-invocation so we can see that the rest of the flow works fine
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
const invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||
|
||||
|
|
@ -918,10 +994,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("does not terminate if messages are received", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
hubConnection.serverTimeoutInMilliseconds = 200;
|
||||
|
||||
|
|
@ -943,10 +1021,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("does not timeout if message was received before HubConnection.start", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
hubConnection.serverTimeoutInMilliseconds = 200;
|
||||
|
||||
|
|
@ -972,10 +1052,12 @@ describe("HubConnection", () => {
|
|||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("terminates if no messages received within timeout interval", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
const hubConnection = createHubConnection(connection, logger);
|
||||
try {
|
||||
hubConnection.serverTimeoutInMilliseconds = 100;
|
||||
|
||||
|
|
@ -992,6 +1074,7 @@ describe("HubConnection", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function pingAndWait(connection: TestConnection): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { ILogger, LogLevel } from "../src/ILogger";
|
|||
import { HttpTransportType, TransferFormat } from "../src/ITransport";
|
||||
import { NullLogger } from "../src/Loggers";
|
||||
|
||||
import { VerifyLogger } from "./Common";
|
||||
import { TestHttpClient } from "./TestHttpClient";
|
||||
import { PromiseSource } from "./Utils";
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ describe("HubConnectionBuilder", () => {
|
|||
});
|
||||
|
||||
it("builds HubConnection with HttpConnection using provided URL", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const pollSent = new PromiseSource<HttpRequest>();
|
||||
const pollCompleted = new PromiseSource<HttpResponse>();
|
||||
const testClient = createTestClient(pollSent, pollCompleted.promise)
|
||||
|
|
@ -55,6 +57,7 @@ describe("HubConnectionBuilder", () => {
|
|||
.withUrl("http://example.com", {
|
||||
...commonHttpOptions,
|
||||
httpClient: testClient,
|
||||
logger,
|
||||
})
|
||||
.build();
|
||||
|
||||
|
|
@ -67,6 +70,7 @@ describe("HubConnectionBuilder", () => {
|
|||
|
||||
await closed;
|
||||
});
|
||||
});
|
||||
|
||||
it("can configure transport type", async () => {
|
||||
const protocol = new TestProtocol();
|
||||
|
|
@ -78,6 +82,7 @@ describe("HubConnectionBuilder", () => {
|
|||
});
|
||||
|
||||
it("can configure hub protocol", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const protocol = new TestProtocol();
|
||||
|
||||
const pollSent = new PromiseSource<HttpRequest>();
|
||||
|
|
@ -95,6 +100,7 @@ describe("HubConnectionBuilder", () => {
|
|||
.withUrl("http://example.com", {
|
||||
...commonHttpOptions,
|
||||
httpClient: testClient,
|
||||
logger,
|
||||
})
|
||||
.withHubProtocol(protocol)
|
||||
.build();
|
||||
|
|
@ -108,6 +114,7 @@ describe("HubConnectionBuilder", () => {
|
|||
|
||||
await closed;
|
||||
});
|
||||
});
|
||||
|
||||
it("allows logger to be replaced", async () => {
|
||||
let loggedMessages = 0;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
|
||||
import { CompletionMessage, InvocationMessage, MessageType, StreamItemMessage } from "../src/IHubProtocol";
|
||||
import { JsonHubProtocol } from "../src/JsonHubProtocol";
|
||||
import { NullLogger } from "../src/Loggers";
|
||||
import { TextMessageFormat } from "../src/TextMessageFormat";
|
||||
import { VerifyLogger } from "./Common";
|
||||
|
||||
describe("JsonHubProtocol", () => {
|
||||
it("can write/read non-blocking Invocation message", () => {
|
||||
it("can write/read non-blocking Invocation message", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const invocation = {
|
||||
arguments: [42, true, "test", ["x1", "y2"], null],
|
||||
headers: {},
|
||||
|
|
@ -16,11 +17,13 @@ describe("JsonHubProtocol", () => {
|
|||
} as InvocationMessage;
|
||||
|
||||
const protocol = new JsonHubProtocol();
|
||||
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), NullLogger.instance);
|
||||
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), logger);
|
||||
expect(parsedMessages).toEqual([invocation]);
|
||||
});
|
||||
});
|
||||
|
||||
it("can read Invocation message with Date argument", () => {
|
||||
it("can read Invocation message with Date argument", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const invocation = {
|
||||
arguments: [Date.UTC(2018, 1, 1, 12, 34, 56)],
|
||||
headers: {},
|
||||
|
|
@ -29,11 +32,13 @@ describe("JsonHubProtocol", () => {
|
|||
} as InvocationMessage;
|
||||
|
||||
const protocol = new JsonHubProtocol();
|
||||
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), NullLogger.instance);
|
||||
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), logger);
|
||||
expect(parsedMessages).toEqual([invocation]);
|
||||
});
|
||||
});
|
||||
|
||||
it("can write/read Invocation message with headers", () => {
|
||||
it("can write/read Invocation message with headers", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const invocation = {
|
||||
arguments: [42, true, "test", ["x1", "y2"], null],
|
||||
headers: {
|
||||
|
|
@ -44,11 +49,13 @@ describe("JsonHubProtocol", () => {
|
|||
} as InvocationMessage;
|
||||
|
||||
const protocol = new JsonHubProtocol();
|
||||
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), NullLogger.instance);
|
||||
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), logger);
|
||||
expect(parsedMessages).toEqual([invocation]);
|
||||
});
|
||||
});
|
||||
|
||||
it("can write/read Invocation message", () => {
|
||||
it("can write/read Invocation message", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const invocation = {
|
||||
arguments: [42, true, "test", ["x1", "y2"], null],
|
||||
headers: {},
|
||||
|
|
@ -58,9 +65,10 @@ describe("JsonHubProtocol", () => {
|
|||
} as InvocationMessage;
|
||||
|
||||
const protocol = new JsonHubProtocol();
|
||||
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), NullLogger.instance);
|
||||
const parsedMessages = protocol.parseMessages(protocol.writeMessage(invocation), logger);
|
||||
expect(parsedMessages).toEqual([invocation]);
|
||||
});
|
||||
});
|
||||
|
||||
([
|
||||
[`{"type":3, "invocationId": "abc", "error": "Err", "result": null, "headers": {}}${TextMessageFormat.RecordSeparator}`,
|
||||
|
|
@ -101,9 +109,11 @@ describe("JsonHubProtocol", () => {
|
|||
type: MessageType.Completion,
|
||||
} as CompletionMessage],
|
||||
] as Array<[string, CompletionMessage]>).forEach(([payload, expectedMessage]) =>
|
||||
it("can read Completion message", () => {
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
|
||||
it("can read Completion message", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, logger);
|
||||
expect(messages).toEqual([expectedMessage]);
|
||||
});
|
||||
}));
|
||||
|
||||
([
|
||||
|
|
@ -122,9 +132,11 @@ describe("JsonHubProtocol", () => {
|
|||
type: MessageType.StreamItem,
|
||||
} as StreamItemMessage],
|
||||
] as Array<[string, StreamItemMessage]>).forEach(([payload, expectedMessage]) =>
|
||||
it("can read StreamItem message", () => {
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
|
||||
it("can read StreamItem message", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, logger);
|
||||
expect(messages).toEqual([expectedMessage]);
|
||||
});
|
||||
}));
|
||||
|
||||
([
|
||||
|
|
@ -138,9 +150,11 @@ describe("JsonHubProtocol", () => {
|
|||
type: MessageType.StreamItem,
|
||||
} as StreamItemMessage],
|
||||
] as Array<[string, StreamItemMessage]>).forEach(([payload, expectedMessage]) =>
|
||||
it("can read message with headers", () => {
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
|
||||
it("can read message with headers", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, logger);
|
||||
expect(messages).toEqual([expectedMessage]);
|
||||
});
|
||||
}));
|
||||
|
||||
([
|
||||
|
|
@ -155,14 +169,17 @@ describe("JsonHubProtocol", () => {
|
|||
["Completion message with result and error", `{"type":3,"invocationId":"1","result":2,"error":"error"}${TextMessageFormat.RecordSeparator}`, "Invalid payload for Completion message."],
|
||||
["Completion message with non-string error", `{"type":3,"invocationId":"1","error":21}${TextMessageFormat.RecordSeparator}`, "Invalid payload for Completion message."],
|
||||
] as Array<[string, string, string]>).forEach(([name, payload, expectedError]) =>
|
||||
it("throws for " + name, () => {
|
||||
expect(() => new JsonHubProtocol().parseMessages(payload, NullLogger.instance))
|
||||
it("throws for " + name, async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
expect(() => new JsonHubProtocol().parseMessages(payload, logger))
|
||||
.toThrow(expectedError);
|
||||
});
|
||||
}));
|
||||
|
||||
it("can read multiple messages", () => {
|
||||
it("can read multiple messages", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const payload = `{"type":2, "invocationId": "abc", "headers": {}, "item": 8}${TextMessageFormat.RecordSeparator}{"type":3, "invocationId": "abc", "headers": {}, "result": "OK"}${TextMessageFormat.RecordSeparator}`;
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, logger);
|
||||
expect(messages).toEqual([
|
||||
{
|
||||
headers: {},
|
||||
|
|
@ -178,14 +195,17 @@ describe("JsonHubProtocol", () => {
|
|||
} as CompletionMessage,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it("can read ping message", () => {
|
||||
it("can read ping message", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
const payload = `{"type":6}${TextMessageFormat.RecordSeparator}`;
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
|
||||
const messages = new JsonHubProtocol().parseMessages(payload, logger);
|
||||
expect(messages).toEqual([
|
||||
{
|
||||
type: MessageType.Ping,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@
|
|||
|
||||
import { HttpResponse } from "../src/HttpClient";
|
||||
import { TransferFormat } from "../src/ITransport";
|
||||
import { NullLogger } from "../src/Loggers";
|
||||
import { LongPollingTransport } from "../src/LongPollingTransport";
|
||||
|
||||
import { VerifyLogger } from "./Common";
|
||||
import { TestHttpClient } from "./TestHttpClient";
|
||||
import { PromiseSource, SyncPoint } from "./Utils";
|
||||
|
||||
describe("LongPollingTransport", () => {
|
||||
it("shuts down polling by aborting in-progress request", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let firstPoll = true;
|
||||
const pollCompleted = new PromiseSource();
|
||||
const client = new TestHttpClient()
|
||||
|
|
@ -20,7 +21,13 @@ describe("LongPollingTransport", () => {
|
|||
return new HttpResponse(200);
|
||||
} else {
|
||||
// Turn 'onabort' into a promise.
|
||||
const abort = new Promise((resolve, reject) => r.abortSignal!.onabort = resolve);
|
||||
const abort = new Promise((resolve, reject) => {
|
||||
if (r.abortSignal!.aborted) {
|
||||
resolve();
|
||||
} else {
|
||||
r.abortSignal!.onabort = resolve;
|
||||
}
|
||||
});
|
||||
await abort;
|
||||
|
||||
// Signal that the poll has completed.
|
||||
|
|
@ -30,7 +37,7 @@ describe("LongPollingTransport", () => {
|
|||
}
|
||||
})
|
||||
.on("DELETE", () => new HttpResponse(202));
|
||||
const transport = new LongPollingTransport(client, undefined, NullLogger.instance, false);
|
||||
const transport = new LongPollingTransport(client, undefined, logger, false);
|
||||
|
||||
await transport.connect("http://example.com", TransferFormat.Text);
|
||||
const stopPromise = transport.stop();
|
||||
|
|
@ -39,8 +46,10 @@ describe("LongPollingTransport", () => {
|
|||
|
||||
await stopPromise;
|
||||
});
|
||||
});
|
||||
|
||||
it("204 server response stops polling and raises onClose", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let firstPoll = true;
|
||||
const client = new TestHttpClient()
|
||||
.on("GET", async () => {
|
||||
|
|
@ -52,7 +61,7 @@ describe("LongPollingTransport", () => {
|
|||
return new HttpResponse(204);
|
||||
}
|
||||
});
|
||||
const transport = new LongPollingTransport(client, undefined, NullLogger.instance, false);
|
||||
const transport = new LongPollingTransport(client, undefined, logger, false);
|
||||
|
||||
const stopPromise = makeClosedPromise(transport);
|
||||
|
||||
|
|
@ -61,8 +70,10 @@ describe("LongPollingTransport", () => {
|
|||
// Close will be called on transport because of 204 result from polling
|
||||
await stopPromise;
|
||||
});
|
||||
});
|
||||
|
||||
it("sends DELETE on stop after polling has finished", async () => {
|
||||
await VerifyLogger.run(async (logger) => {
|
||||
let firstPoll = true;
|
||||
let deleteSent = false;
|
||||
const pollingPromiseSource = new PromiseSource();
|
||||
|
|
@ -83,7 +94,7 @@ describe("LongPollingTransport", () => {
|
|||
return new HttpResponse(202);
|
||||
});
|
||||
|
||||
const transport = new LongPollingTransport(httpClient, undefined, NullLogger.instance, false);
|
||||
const transport = new LongPollingTransport(httpClient, undefined, logger, false);
|
||||
|
||||
await transport.connect("http://tempuri.org", TransferFormat.Text);
|
||||
|
||||
|
|
@ -106,6 +117,7 @@ describe("LongPollingTransport", () => {
|
|||
// Wait for stop to complete
|
||||
await stopPromise;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function makeClosedPromise(transport: LongPollingTransport): Promise<void> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue