Hub connection tests
This commit is contained in:
parent
d3e625c985
commit
46cf13b2fc
|
|
@ -35,6 +35,10 @@ export class HubConnection {
|
||||||
|
|
||||||
private dataReceived(data: any) {
|
private dataReceived(data: any) {
|
||||||
//TODO: separate JSON parsing
|
//TODO: separate JSON parsing
|
||||||
|
// Can happen if a poll request was cancelled
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var descriptor = JSON.parse(data);
|
var descriptor = JSON.parse(data);
|
||||||
if (descriptor.Method === undefined) {
|
if (descriptor.Method === undefined) {
|
||||||
let invocationResult: InvocationResultDescriptor = descriptor;
|
let invocationResult: InvocationResultDescriptor = descriptor;
|
||||||
|
|
@ -62,7 +66,7 @@ export class HubConnection {
|
||||||
return this.connection.stop();
|
return this.connection.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke(methodName: string, ...args: any[]): Promise<void> {
|
invoke(methodName: string, ...args: any[]): Promise<any> {
|
||||||
|
|
||||||
let id = this.id;
|
let id = this.id;
|
||||||
this.id++;
|
this.id++;
|
||||||
|
|
@ -76,7 +80,7 @@ export class HubConnection {
|
||||||
let p = new Promise<any>((resolve, reject) => {
|
let p = new Promise<any>((resolve, reject) => {
|
||||||
this.callbacks[id] = (invocationResult: InvocationResultDescriptor) => {
|
this.callbacks[id] = (invocationResult: InvocationResultDescriptor) => {
|
||||||
if (invocationResult.Error != null) {
|
if (invocationResult.Error != null) {
|
||||||
reject(invocationResult.Error);
|
reject(new Error(invocationResult.Error));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolve(invocationResult.Result);
|
resolve(invocationResult.Result);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export class WebSocketTransport implements ITransport {
|
||||||
private webSocket: WebSocket;
|
private webSocket: WebSocket;
|
||||||
|
|
||||||
connect(url: string, queryString: string = ""): Promise<void> {
|
connect(url: string, queryString: string = ""): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
url = url.replace(/^http/, "ws");
|
url = url.replace(/^http/, "ws");
|
||||||
let connectUrl = url + "/ws?" + queryString;
|
let connectUrl = url + "/ws?" + queryString;
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ export class WebSocketTransport implements ITransport {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject("WebSocket is not in OPEN state");
|
return Promise.reject("WebSocket is not in the OPEN state");
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): void {
|
stop(): void {
|
||||||
|
|
@ -81,7 +81,7 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
let tmp = `${this.url}/sse?${this.queryString}`;
|
let tmp = `${this.url}/sse?${this.queryString}`;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
let eventSource = new EventSource(`${this.url}/sse?${this.queryString}`);
|
let eventSource = new EventSource(`${this.url}/sse?${this.queryString}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -131,15 +131,21 @@ export class LongPollingTransport implements ITransport {
|
||||||
private url: string;
|
private url: string;
|
||||||
private queryString: string;
|
private queryString: string;
|
||||||
private pollXhr: XMLHttpRequest;
|
private pollXhr: XMLHttpRequest;
|
||||||
|
private shouldPoll: boolean;
|
||||||
|
|
||||||
connect(url: string, queryString: string): Promise<void> {
|
connect(url: string, queryString: string): Promise<void> {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.queryString = queryString;
|
this.queryString = queryString;
|
||||||
|
this.shouldPoll = true;
|
||||||
this.poll(url + "/poll?" + this.queryString)
|
this.poll(url + "/poll?" + this.queryString)
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
private poll(url: string): void {
|
private poll(url: string): void {
|
||||||
|
if (!this.shouldPoll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let thisLongPollingTransport = this;
|
let thisLongPollingTransport = this;
|
||||||
let pollXhr = new XMLHttpRequest();
|
let pollXhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
|
@ -188,6 +194,7 @@ export class LongPollingTransport implements ITransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): void {
|
stop(): void {
|
||||||
|
this.shouldPoll = false;
|
||||||
if (this.pollXhr) {
|
if (this.pollXhr) {
|
||||||
this.pollXhr.abort();
|
this.pollXhr.abort();
|
||||||
this.pollXhr = null;
|
this.pollXhr = null;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
using System;
|
// 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.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
using System;
|
// 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.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
using Microsoft.AspNetCore.Builder;
|
// 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.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -25,6 +28,10 @@ namespace Microsoft.AspNetCore.SignalR.Test.Server
|
||||||
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseSockets(options => options.MapEndpoint<EchoEndPoint>("/echo"));
|
app.UseSockets(options => options.MapEndpoint<EchoEndPoint>("/echo"));
|
||||||
|
app.UseSignalR(routes =>
|
||||||
|
{
|
||||||
|
routes.MapHub<TestHub>("/testhub");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Test.Server
|
||||||
|
{
|
||||||
|
public class TestHub : Hub
|
||||||
|
{
|
||||||
|
public string Echo(string message)
|
||||||
|
{
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ThrowException(string message)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InvokeWithString(string message)
|
||||||
|
{
|
||||||
|
return Clients.Client(Context.Connection.ConnectionId).InvokeAsync("Message", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
<script src="js/common.js"></script>
|
<script src="js/common.js"></script>
|
||||||
<script src="js/webSocketTests.js"></script>
|
<script src="js/webSocketTests.js"></script>
|
||||||
<script src="js/connectionTests.js"></script>
|
<script src="js/connectionTests.js"></script>
|
||||||
|
<script src="js/hubConnectionTests.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,8 @@ const ECHOENDPOINT_URL = `http://${document.location.host}/echo`;
|
||||||
function eachTransport(action) {
|
function eachTransport(action) {
|
||||||
let transportNames = ["webSockets", "serverSentEvents", "longPolling"];
|
let transportNames = ["webSockets", "serverSentEvents", "longPolling"];
|
||||||
transportNames.forEach(t => action(t));
|
transportNames.forEach(t => action(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
function fail() {
|
||||||
|
it.expect(true).toBe(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
describe('connection', () => {
|
describe('connection', () => {
|
||||||
eachTransport(transportName => {
|
eachTransport(transportName => {
|
||||||
it(`over ${transportName} can send and receive messages`, done => {
|
it(`over ${transportName} can send and receive messages`, done => {
|
||||||
|
|
@ -23,7 +22,7 @@ describe('connection', () => {
|
||||||
connection.send(message);
|
connection.send(message);
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
expect(true).toBe(false);
|
fail();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
const TESTHUBENDPOINT_URL = `http://${document.location.host}/testhub`;
|
||||||
|
|
||||||
|
describe('hubConnection', () => {
|
||||||
|
eachTransport(transportName => {
|
||||||
|
it(`over ${transportName} can invoke server method and receive result`, done => {
|
||||||
|
const message = "Hi";
|
||||||
|
let hubConnection = new signalR.HubConnection(TESTHUBENDPOINT_URL, 'formatType=json&format=text');
|
||||||
|
|
||||||
|
hubConnection.start(transportName)
|
||||||
|
.then(() => {
|
||||||
|
hubConnection.invoke('Echo', message)
|
||||||
|
.then(result => {
|
||||||
|
expect(result).toBe(message);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
fail();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
hubConnection.stop();
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
fail();
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`over ${transportName} rethrows an exception from the server`, done => {
|
||||||
|
const errorMessage = "An error occurred.";
|
||||||
|
let hubConnection = new signalR.HubConnection(TESTHUBENDPOINT_URL, 'formatType=json&format=text');
|
||||||
|
|
||||||
|
hubConnection.start(transportName)
|
||||||
|
.then(() => {
|
||||||
|
hubConnection.invoke('ThrowException', errorMessage)
|
||||||
|
.then(() => {
|
||||||
|
// exception expected but none thrown
|
||||||
|
fail();
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
expect(e.message).toBe(errorMessage);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
hubConnection.stop();
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
fail();
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`over ${transportName} can receive server calls`, done => {
|
||||||
|
let client = new signalR.HubConnection(TESTHUBENDPOINT_URL, 'formatType=json&format=text');
|
||||||
|
const message = "Hello SignalR";
|
||||||
|
|
||||||
|
client.on("Message", msg => {
|
||||||
|
expect(msg).toBe(message);
|
||||||
|
client.stop();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.start(transportName)
|
||||||
|
.then(() => {
|
||||||
|
client.invoke('InvokeWithString', message)
|
||||||
|
.catch(e => {
|
||||||
|
fail();
|
||||||
|
client.stop();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
fail();
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
webSocket.onerror = event => {
|
webSocket.onerror = event => {
|
||||||
expect(true).toBe(false);
|
fail();
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue