@eleven-am/pondsocket 0.1.43 → 0.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/channel.js +15 -19
- package/client/channel.test.js +528 -0
- package/client.js +5 -4
- package/enums.d.ts +48 -0
- package/enums.js +24 -1
- package/node.js +3 -3
- package/package.json +3 -3
- package/server/channel/channelEngine.js +8 -7
- package/server/channel/channelEngine.test.js +5 -5
- package/server/channel/channelResponse.test.js +36 -6
- package/server/channel/eventResponse.js +11 -6
- package/server/endpoint/endpoint.js +10 -14
- package/server/endpoint/endpoint.test.js +38 -29
- package/server/pondChannel/joinResponse.js +3 -2
- package/server/pondChannel/pondChannelResponse.test.js +2 -1
- package/server/presence/presenceEngine.js +5 -10
- package/server/presence/presenceEngine.test.js +5 -4
- package/server/server/server.test.js +2 -1
- package/server/utils/matchPattern.test.js +12 -0
- package/server/utils/subjectUtils.js +3 -3
- package/server/utils/subjectUtils.test.js +128 -0
- package/types.d.ts +3 -8
package/client/channel.js
CHANGED
|
@@ -8,19 +8,18 @@ class Channel {
|
|
|
8
8
|
this._name = name;
|
|
9
9
|
this._queue = [];
|
|
10
10
|
this._presence = [];
|
|
11
|
-
this._finished = false;
|
|
12
11
|
this._joinParams = params;
|
|
13
12
|
this._publisher = publisher;
|
|
14
13
|
this._clientState = clientState;
|
|
15
14
|
this._receiver = new subjectUtils_1.SimpleSubject();
|
|
16
|
-
this._joinState = new subjectUtils_1.SimpleBehaviorSubject(
|
|
15
|
+
this._joinState = new subjectUtils_1.SimpleBehaviorSubject(enums_1.ChannelState.IDLE);
|
|
17
16
|
this._presenceSub = this._init(receiver);
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
20
19
|
* @desc Connects to the channel.
|
|
21
20
|
*/
|
|
22
21
|
join() {
|
|
23
|
-
if (this.
|
|
22
|
+
if (this._joinState.value === enums_1.ChannelState.CLOSED) {
|
|
24
23
|
throw new Error('This channel has been closed');
|
|
25
24
|
}
|
|
26
25
|
const joinMessage = {
|
|
@@ -29,6 +28,7 @@ class Channel {
|
|
|
29
28
|
event: enums_1.ClientActions.JOIN_CHANNEL,
|
|
30
29
|
payload: this._joinParams,
|
|
31
30
|
};
|
|
31
|
+
this._joinState.next(enums_1.ChannelState.JOINING);
|
|
32
32
|
if (this._clientState.value === enums_1.PondState.OPEN) {
|
|
33
33
|
this._publisher(joinMessage);
|
|
34
34
|
}
|
|
@@ -47,7 +47,7 @@ class Channel {
|
|
|
47
47
|
payload: {},
|
|
48
48
|
};
|
|
49
49
|
this._publish(leaveMessage);
|
|
50
|
-
this.
|
|
50
|
+
this._joinState.next(enums_1.ChannelState.CLOSED);
|
|
51
51
|
this._presenceSub();
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
@@ -143,15 +143,9 @@ class Channel {
|
|
|
143
143
|
/**
|
|
144
144
|
* @desc Gets the current connection state of the channel.
|
|
145
145
|
*/
|
|
146
|
-
|
|
146
|
+
get channelState() {
|
|
147
147
|
return this._joinState.value;
|
|
148
148
|
}
|
|
149
|
-
/**
|
|
150
|
-
* @desc check is the channel has been closed.
|
|
151
|
-
*/
|
|
152
|
-
hasClosed() {
|
|
153
|
-
return this._finished;
|
|
154
|
-
}
|
|
155
149
|
/**
|
|
156
150
|
* @desc Gets the current presence of the channel.
|
|
157
151
|
*/
|
|
@@ -163,7 +157,7 @@ class Channel {
|
|
|
163
157
|
* @param callback - The callback to call when the presence changes.
|
|
164
158
|
*/
|
|
165
159
|
onUsersChange(callback) {
|
|
166
|
-
return this._subscribeToPresence((
|
|
160
|
+
return this._subscribeToPresence((_event, payload) => callback(payload.presence));
|
|
167
161
|
}
|
|
168
162
|
_send(event, payload, receivers = 'all_users') {
|
|
169
163
|
const message = {
|
|
@@ -176,8 +170,10 @@ class Channel {
|
|
|
176
170
|
this._publish(message);
|
|
177
171
|
}
|
|
178
172
|
_publish(data) {
|
|
179
|
-
if (this.
|
|
180
|
-
this.
|
|
173
|
+
if (this._clientState.value === enums_1.PondState.OPEN) {
|
|
174
|
+
if (this._joinState.value === enums_1.ChannelState.JOINED || this._joinState.value === enums_1.ChannelState.JOINING) {
|
|
175
|
+
this._publisher(data);
|
|
176
|
+
}
|
|
181
177
|
return;
|
|
182
178
|
}
|
|
183
179
|
this._queue.push(data);
|
|
@@ -192,10 +188,10 @@ class Channel {
|
|
|
192
188
|
_init(receiver) {
|
|
193
189
|
const unsubMessages = receiver.subscribe((data) => {
|
|
194
190
|
if (data.channelName === this._name) {
|
|
195
|
-
if (
|
|
196
|
-
this._joinState.
|
|
191
|
+
if (this._joinState.value !== enums_1.ChannelState.JOINED) {
|
|
192
|
+
this._joinState.next(enums_1.ChannelState.JOINED);
|
|
197
193
|
}
|
|
198
|
-
this._receiver.
|
|
194
|
+
this._receiver.next(data);
|
|
199
195
|
}
|
|
200
196
|
});
|
|
201
197
|
const unsubStateChange = this._clientState.subscribe((state) => {
|
|
@@ -212,11 +208,11 @@ class Channel {
|
|
|
212
208
|
.forEach((message) => {
|
|
213
209
|
this._publisher(message);
|
|
214
210
|
});
|
|
215
|
-
this._joinState.
|
|
211
|
+
this._joinState.next(enums_1.ChannelState.JOINED);
|
|
216
212
|
this._queue = [];
|
|
217
213
|
}
|
|
218
214
|
else if (state !== enums_1.PondState.OPEN) {
|
|
219
|
-
this._joinState.
|
|
215
|
+
this._joinState.next(enums_1.ChannelState.STALLED);
|
|
220
216
|
}
|
|
221
217
|
});
|
|
222
218
|
const unsubPresence = this._subscribeToPresence((_, payload) => {
|
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const channel_1 = require("./channel");
|
|
4
|
+
const enums_1 = require("../enums");
|
|
5
|
+
const channelEngine_1 = require("../server/channel/channelEngine");
|
|
6
|
+
const subjectUtils_1 = require("../server/utils/subjectUtils");
|
|
7
|
+
const createChannel = (params) => {
|
|
8
|
+
const publisher = jest.fn();
|
|
9
|
+
const state = new subjectUtils_1.SimpleBehaviorSubject(enums_1.PondState.OPEN);
|
|
10
|
+
const receiver = new subjectUtils_1.SimpleSubject();
|
|
11
|
+
const channel = new channel_1.Channel(publisher, state, 'test', receiver, params);
|
|
12
|
+
return {
|
|
13
|
+
channel,
|
|
14
|
+
publisher,
|
|
15
|
+
state,
|
|
16
|
+
receiver,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
describe('Channel', () => {
|
|
20
|
+
it('should correctly send a join message', () => {
|
|
21
|
+
const { channel, publisher, state } = createChannel();
|
|
22
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
23
|
+
expect(state.value).toBe(enums_1.PondState.OPEN);
|
|
24
|
+
channel.join();
|
|
25
|
+
expect(publisher).toHaveBeenCalledWith({
|
|
26
|
+
action: enums_1.ClientActions.JOIN_CHANNEL,
|
|
27
|
+
channelName: 'test',
|
|
28
|
+
event: enums_1.ClientActions.JOIN_CHANNEL,
|
|
29
|
+
payload: {},
|
|
30
|
+
});
|
|
31
|
+
const { channel: channel2, publisher: publisher2, state: state2 } = createChannel();
|
|
32
|
+
state2.next(enums_1.PondState.CLOSED);
|
|
33
|
+
expect(state2.value).toBe(enums_1.PondState.CLOSED);
|
|
34
|
+
expect(publisher2).not.toHaveBeenCalled();
|
|
35
|
+
expect(channel2['_queue']).toEqual([]);
|
|
36
|
+
channel2.join();
|
|
37
|
+
expect(publisher2).not.toHaveBeenCalled();
|
|
38
|
+
expect(channel2['_queue']).toEqual([
|
|
39
|
+
{
|
|
40
|
+
action: enums_1.ClientActions.JOIN_CHANNEL,
|
|
41
|
+
channelName: 'test',
|
|
42
|
+
event: enums_1.ClientActions.JOIN_CHANNEL,
|
|
43
|
+
payload: {},
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
state2.next(enums_1.PondState.OPEN);
|
|
47
|
+
expect(publisher2).toHaveBeenCalledWith({
|
|
48
|
+
action: enums_1.ClientActions.JOIN_CHANNEL,
|
|
49
|
+
channelName: 'test',
|
|
50
|
+
event: enums_1.ClientActions.JOIN_CHANNEL,
|
|
51
|
+
payload: {},
|
|
52
|
+
});
|
|
53
|
+
expect(channel2['_queue']).toEqual([]);
|
|
54
|
+
const { channel: channel3 } = createChannel();
|
|
55
|
+
channel3.leave();
|
|
56
|
+
expect(() => channel3.join())
|
|
57
|
+
.toThrowError('This channel has been closed');
|
|
58
|
+
});
|
|
59
|
+
it('should correctly send a leave message', () => {
|
|
60
|
+
const { channel, publisher, receiver } = createChannel();
|
|
61
|
+
expect(channel.channelState).toBe(enums_1.ChannelState.IDLE);
|
|
62
|
+
channel.join();
|
|
63
|
+
expect(channel.channelState).toBe(enums_1.ChannelState.JOINING);
|
|
64
|
+
// once the server responds with a join message, the channel should be connected
|
|
65
|
+
receiver.next({
|
|
66
|
+
action: 'SYSTEM',
|
|
67
|
+
channelName: 'test',
|
|
68
|
+
event: 'SYSTEM',
|
|
69
|
+
payload: {
|
|
70
|
+
event: 'JOIN',
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
expect(channel.channelState).toBe(enums_1.ChannelState.JOINED);
|
|
74
|
+
channel.leave();
|
|
75
|
+
expect(channel.channelState).toBe(enums_1.ChannelState.CLOSED);
|
|
76
|
+
expect(publisher).toHaveBeenCalledWith({
|
|
77
|
+
action: enums_1.ClientActions.LEAVE_CHANNEL,
|
|
78
|
+
channelName: 'test',
|
|
79
|
+
event: enums_1.ClientActions.LEAVE_CHANNEL,
|
|
80
|
+
payload: {},
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
it('should notify subscribers when the channel state changes', () => {
|
|
84
|
+
const { channel, receiver } = createChannel();
|
|
85
|
+
const stateListener = jest.fn();
|
|
86
|
+
channel.onConnectionChange(stateListener);
|
|
87
|
+
expect(stateListener).toHaveBeenCalledWith(enums_1.ChannelState.IDLE);
|
|
88
|
+
channel.join();
|
|
89
|
+
receiver.next({
|
|
90
|
+
action: 'SYSTEM',
|
|
91
|
+
channelName: 'test',
|
|
92
|
+
event: 'SYSTEM',
|
|
93
|
+
payload: {
|
|
94
|
+
event: 'JOIN',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
expect(stateListener).toHaveBeenCalledWith(enums_1.ChannelState.JOINED);
|
|
98
|
+
channel.leave();
|
|
99
|
+
expect(stateListener).toHaveBeenCalledWith(enums_1.ChannelState.CLOSED);
|
|
100
|
+
});
|
|
101
|
+
it('should correctly send a message', () => {
|
|
102
|
+
const { channel, publisher, state } = createChannel();
|
|
103
|
+
expect(channel.channelState).toBe(enums_1.ChannelState.IDLE);
|
|
104
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
105
|
+
expect(channel['_queue']).toEqual([]);
|
|
106
|
+
// when the socket is connected but the channel is not joined, the message would not be queued
|
|
107
|
+
channel.broadcast('test', { test: true });
|
|
108
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
109
|
+
expect(channel['_queue']).toEqual([]);
|
|
110
|
+
channel.join();
|
|
111
|
+
publisher.mockClear();
|
|
112
|
+
// however once the channel is joined, the message should be sent
|
|
113
|
+
channel.broadcast('test', { test: true });
|
|
114
|
+
expect(publisher).toHaveBeenCalledWith({
|
|
115
|
+
action: enums_1.ClientActions.BROADCAST,
|
|
116
|
+
addresses: 'all_users',
|
|
117
|
+
channelName: 'test',
|
|
118
|
+
event: 'test',
|
|
119
|
+
payload: {
|
|
120
|
+
test: true,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
// if for some reason the socket is disconnected, the message should be queued
|
|
124
|
+
publisher.mockClear();
|
|
125
|
+
state.next(enums_1.PondState.CLOSED);
|
|
126
|
+
expect(state.value).toBe(enums_1.PondState.CLOSED);
|
|
127
|
+
channel.broadcast('test', { test: true });
|
|
128
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
129
|
+
expect(channel['_queue']).toEqual([
|
|
130
|
+
{
|
|
131
|
+
action: enums_1.ClientActions.BROADCAST,
|
|
132
|
+
addresses: 'all_users',
|
|
133
|
+
channelName: 'test',
|
|
134
|
+
event: 'test',
|
|
135
|
+
payload: {
|
|
136
|
+
test: true,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
// once the socket is reconnected, a join message should be sent and the queued messages should be sent
|
|
141
|
+
publisher.mockClear();
|
|
142
|
+
state.next(enums_1.PondState.OPEN);
|
|
143
|
+
expect(publisher).toHaveBeenCalledWith({
|
|
144
|
+
action: enums_1.ClientActions.JOIN_CHANNEL,
|
|
145
|
+
channelName: 'test',
|
|
146
|
+
event: enums_1.ClientActions.JOIN_CHANNEL,
|
|
147
|
+
payload: {},
|
|
148
|
+
});
|
|
149
|
+
expect(publisher).toHaveBeenCalledWith({
|
|
150
|
+
action: enums_1.ClientActions.BROADCAST,
|
|
151
|
+
addresses: 'all_users',
|
|
152
|
+
channelName: 'test',
|
|
153
|
+
event: 'test',
|
|
154
|
+
payload: {
|
|
155
|
+
test: true,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
expect(channel['_queue']).toEqual([]);
|
|
159
|
+
// if the channel is closed, the message should not be sent, and should not be queued
|
|
160
|
+
publisher.mockClear();
|
|
161
|
+
channel.leave();
|
|
162
|
+
// the leave message should be sent
|
|
163
|
+
expect(publisher).toHaveBeenCalledWith({
|
|
164
|
+
action: enums_1.ClientActions.LEAVE_CHANNEL,
|
|
165
|
+
channelName: 'test',
|
|
166
|
+
event: enums_1.ClientActions.LEAVE_CHANNEL,
|
|
167
|
+
payload: {},
|
|
168
|
+
});
|
|
169
|
+
expect(channel.channelState).toBe(enums_1.ChannelState.CLOSED);
|
|
170
|
+
channel.broadcast('test', { test: true });
|
|
171
|
+
expect(channel['_queue']).toEqual([]);
|
|
172
|
+
expect(publisher).toBeCalledTimes(1);
|
|
173
|
+
});
|
|
174
|
+
// The presence system tests
|
|
175
|
+
it('should notify subscribers when a user joins the channel', () => {
|
|
176
|
+
const { channel, receiver } = createChannel();
|
|
177
|
+
const presenceListener = jest.fn();
|
|
178
|
+
channel.onJoin(presenceListener);
|
|
179
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
180
|
+
// usually the server wouldn't send a presence if the channel is not joined
|
|
181
|
+
// but for testing purposes, we'll just send it anyway
|
|
182
|
+
receiver.next({
|
|
183
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
184
|
+
event: enums_1.PresenceEventTypes.JOIN,
|
|
185
|
+
channelName: 'test',
|
|
186
|
+
payload: {
|
|
187
|
+
changed: {
|
|
188
|
+
id: 'test',
|
|
189
|
+
status: 'online',
|
|
190
|
+
},
|
|
191
|
+
presence: [
|
|
192
|
+
{
|
|
193
|
+
id: 'test',
|
|
194
|
+
status: 'online',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: 'test2',
|
|
198
|
+
status: 'online',
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
expect(presenceListener).toHaveBeenCalledWith({
|
|
204
|
+
id: 'test',
|
|
205
|
+
status: 'online',
|
|
206
|
+
});
|
|
207
|
+
presenceListener.mockClear();
|
|
208
|
+
receiver.next({
|
|
209
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
210
|
+
event: enums_1.PresenceEventTypes.UPDATE,
|
|
211
|
+
channelName: 'test',
|
|
212
|
+
payload: {
|
|
213
|
+
changed: {
|
|
214
|
+
id: 'test',
|
|
215
|
+
status: 'online',
|
|
216
|
+
},
|
|
217
|
+
presence: [
|
|
218
|
+
{
|
|
219
|
+
id: 'test',
|
|
220
|
+
status: 'online',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: 'test2',
|
|
224
|
+
status: 'online',
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
230
|
+
// also, if a presence event is received for a different channel, it should not be sent to the listener
|
|
231
|
+
receiver.next({
|
|
232
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
233
|
+
event: enums_1.PresenceEventTypes.JOIN,
|
|
234
|
+
channelName: 'test2',
|
|
235
|
+
payload: {
|
|
236
|
+
changed: {
|
|
237
|
+
id: 'test',
|
|
238
|
+
status: 'online',
|
|
239
|
+
},
|
|
240
|
+
presence: [
|
|
241
|
+
{
|
|
242
|
+
id: 'test',
|
|
243
|
+
status: 'online',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: 'test2',
|
|
247
|
+
status: 'online',
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
253
|
+
});
|
|
254
|
+
it('should notify subscribers when a user leaves the channel', () => {
|
|
255
|
+
const { channel, receiver } = createChannel();
|
|
256
|
+
const presenceListener = jest.fn();
|
|
257
|
+
channel.onLeave(presenceListener);
|
|
258
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
259
|
+
// usually the server wouldn't send a presence if the channel is not joined
|
|
260
|
+
// but for testing purposes, we'll just send it anyway
|
|
261
|
+
receiver.next({
|
|
262
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
263
|
+
event: enums_1.PresenceEventTypes.LEAVE,
|
|
264
|
+
channelName: 'test',
|
|
265
|
+
payload: {
|
|
266
|
+
changed: {
|
|
267
|
+
id: 'test',
|
|
268
|
+
status: 'online',
|
|
269
|
+
},
|
|
270
|
+
presence: [
|
|
271
|
+
{
|
|
272
|
+
id: 'test',
|
|
273
|
+
status: 'online',
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: 'test2',
|
|
277
|
+
status: 'online',
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
expect(presenceListener).toHaveBeenCalledWith({
|
|
283
|
+
id: 'test',
|
|
284
|
+
status: 'online',
|
|
285
|
+
});
|
|
286
|
+
presenceListener.mockClear();
|
|
287
|
+
receiver.next({
|
|
288
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
289
|
+
event: enums_1.PresenceEventTypes.UPDATE,
|
|
290
|
+
channelName: 'test',
|
|
291
|
+
payload: {
|
|
292
|
+
changed: {
|
|
293
|
+
id: 'test',
|
|
294
|
+
status: 'online',
|
|
295
|
+
},
|
|
296
|
+
presence: [
|
|
297
|
+
{
|
|
298
|
+
id: 'test',
|
|
299
|
+
status: 'online',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
id: 'test2',
|
|
303
|
+
status: 'online',
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
309
|
+
// also, if a presence event is received for a different channel, it should not be sent to the listener
|
|
310
|
+
receiver.next({
|
|
311
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
312
|
+
event: enums_1.PresenceEventTypes.LEAVE,
|
|
313
|
+
channelName: 'test2',
|
|
314
|
+
payload: {
|
|
315
|
+
changed: {
|
|
316
|
+
id: 'test',
|
|
317
|
+
status: 'online',
|
|
318
|
+
},
|
|
319
|
+
presence: [
|
|
320
|
+
{
|
|
321
|
+
id: 'test',
|
|
322
|
+
status: 'online',
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: 'test2',
|
|
326
|
+
status: 'online',
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
332
|
+
});
|
|
333
|
+
it('should notify subscribers when a user updates their presence', () => {
|
|
334
|
+
const { channel, receiver } = createChannel();
|
|
335
|
+
const presenceListener = jest.fn();
|
|
336
|
+
channel.onUsersChange(presenceListener);
|
|
337
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
338
|
+
// usually the server wouldn't send a presence if the channel is not joined
|
|
339
|
+
// but for testing purposes, we'll just send it anyway
|
|
340
|
+
receiver.next({
|
|
341
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
342
|
+
event: enums_1.PresenceEventTypes.UPDATE,
|
|
343
|
+
channelName: 'test',
|
|
344
|
+
payload: {
|
|
345
|
+
changed: {
|
|
346
|
+
id: 'test',
|
|
347
|
+
status: 'online',
|
|
348
|
+
},
|
|
349
|
+
presence: [
|
|
350
|
+
{
|
|
351
|
+
id: 'test',
|
|
352
|
+
status: 'online',
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
id: 'test2',
|
|
356
|
+
status: 'online',
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
expect(presenceListener).toHaveBeenCalledWith([
|
|
362
|
+
{
|
|
363
|
+
id: 'test',
|
|
364
|
+
status: 'online',
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
id: 'test2',
|
|
368
|
+
status: 'online',
|
|
369
|
+
},
|
|
370
|
+
]);
|
|
371
|
+
expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
|
|
372
|
+
presenceListener.mockClear();
|
|
373
|
+
// if we receive a leave or join event, it should be sent to the listener
|
|
374
|
+
// this is because we are listening for all presence events
|
|
375
|
+
receiver.next({
|
|
376
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
377
|
+
event: enums_1.PresenceEventTypes.JOIN,
|
|
378
|
+
channelName: 'test',
|
|
379
|
+
payload: {
|
|
380
|
+
changed: {
|
|
381
|
+
id: 'test',
|
|
382
|
+
status: 'online',
|
|
383
|
+
},
|
|
384
|
+
presence: [
|
|
385
|
+
{
|
|
386
|
+
id: 'test',
|
|
387
|
+
status: 'online',
|
|
388
|
+
},
|
|
389
|
+
],
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
expect(presenceListener).toHaveBeenCalledWith([
|
|
393
|
+
{
|
|
394
|
+
id: 'test',
|
|
395
|
+
status: 'online',
|
|
396
|
+
},
|
|
397
|
+
]);
|
|
398
|
+
expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
|
|
399
|
+
presenceListener.mockClear();
|
|
400
|
+
// also, if a presence event is received for a different channel, it should not be sent to the listener
|
|
401
|
+
receiver.next({
|
|
402
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
403
|
+
event: enums_1.PresenceEventTypes.UPDATE,
|
|
404
|
+
channelName: 'test2',
|
|
405
|
+
payload: {
|
|
406
|
+
changed: {
|
|
407
|
+
id: 'test',
|
|
408
|
+
status: 'online',
|
|
409
|
+
},
|
|
410
|
+
presence: [
|
|
411
|
+
{
|
|
412
|
+
id: 'test',
|
|
413
|
+
status: 'online',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
id: 'test2',
|
|
417
|
+
status: 'online',
|
|
418
|
+
},
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
423
|
+
// we once again send a presence event for the same channel, but this time it should be sent to the listener
|
|
424
|
+
receiver.next({
|
|
425
|
+
action: channelEngine_1.ServerActions.PRESENCE,
|
|
426
|
+
event: enums_1.PresenceEventTypes.UPDATE,
|
|
427
|
+
channelName: 'test',
|
|
428
|
+
payload: {
|
|
429
|
+
changed: {
|
|
430
|
+
id: 'test',
|
|
431
|
+
status: 'away',
|
|
432
|
+
},
|
|
433
|
+
presence: [
|
|
434
|
+
{
|
|
435
|
+
id: 'test',
|
|
436
|
+
status: 'away',
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
id: 'test2',
|
|
440
|
+
status: 'online',
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
expect(presenceListener).toHaveBeenCalledWith([
|
|
446
|
+
{
|
|
447
|
+
id: 'test',
|
|
448
|
+
status: 'away',
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
id: 'test2',
|
|
452
|
+
status: 'online',
|
|
453
|
+
},
|
|
454
|
+
]);
|
|
455
|
+
expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
|
|
456
|
+
});
|
|
457
|
+
// message events
|
|
458
|
+
it('should notify subscribers when a message is received', () => {
|
|
459
|
+
const { channel, receiver } = createChannel();
|
|
460
|
+
const messageListener = jest.fn();
|
|
461
|
+
channel.onMessage(messageListener);
|
|
462
|
+
expect(messageListener).not.toHaveBeenCalled();
|
|
463
|
+
receiver.next({
|
|
464
|
+
action: channelEngine_1.ServerActions.BROADCAST,
|
|
465
|
+
event: 'message',
|
|
466
|
+
channelName: 'test',
|
|
467
|
+
payload: {
|
|
468
|
+
id: 'test',
|
|
469
|
+
message: 'test',
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
expect(messageListener).toHaveBeenCalledWith('message', {
|
|
473
|
+
id: 'test',
|
|
474
|
+
message: 'test',
|
|
475
|
+
});
|
|
476
|
+
// if a message event is received for a different channel, it should not be sent to the listener
|
|
477
|
+
receiver.next({
|
|
478
|
+
action: channelEngine_1.ServerActions.BROADCAST,
|
|
479
|
+
event: 'message',
|
|
480
|
+
channelName: 'test2',
|
|
481
|
+
payload: {
|
|
482
|
+
id: 'test',
|
|
483
|
+
message: 'test',
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
expect(messageListener).toHaveBeenCalledTimes(1);
|
|
487
|
+
// we can also subscribe to a specific message event
|
|
488
|
+
const specificMessageListener = jest.fn();
|
|
489
|
+
channel.onMessageEvent('test', specificMessageListener);
|
|
490
|
+
expect(specificMessageListener).not.toHaveBeenCalled();
|
|
491
|
+
receiver.next({
|
|
492
|
+
action: channelEngine_1.ServerActions.BROADCAST,
|
|
493
|
+
event: 'test',
|
|
494
|
+
channelName: 'test',
|
|
495
|
+
payload: {
|
|
496
|
+
id: 'test',
|
|
497
|
+
message: 'test',
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
expect(specificMessageListener).toHaveBeenCalledWith({
|
|
501
|
+
id: 'test',
|
|
502
|
+
message: 'test',
|
|
503
|
+
});
|
|
504
|
+
specificMessageListener.mockClear();
|
|
505
|
+
// if a message event is received for the same channel, but a different event, it should not be sent to the listener
|
|
506
|
+
receiver.next({
|
|
507
|
+
action: channelEngine_1.ServerActions.BROADCAST,
|
|
508
|
+
event: 'test2',
|
|
509
|
+
channelName: 'test',
|
|
510
|
+
payload: {
|
|
511
|
+
id: 'test',
|
|
512
|
+
message: 'test',
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
expect(specificMessageListener).not.toHaveBeenCalled();
|
|
516
|
+
// if a message event is received for a different channel, it should not be sent to the listener
|
|
517
|
+
receiver.next({
|
|
518
|
+
action: channelEngine_1.ServerActions.BROADCAST,
|
|
519
|
+
event: 'test',
|
|
520
|
+
channelName: 'test2',
|
|
521
|
+
payload: {
|
|
522
|
+
id: 'test',
|
|
523
|
+
message: 'test',
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
expect(specificMessageListener).not.toHaveBeenCalled();
|
|
527
|
+
});
|
|
528
|
+
});
|
package/client.js
CHANGED
|
@@ -30,14 +30,14 @@ class PondClient {
|
|
|
30
30
|
connect(backoff = 1) {
|
|
31
31
|
const socket = new WebSocket(this._address.toString());
|
|
32
32
|
socket.onopen = () => {
|
|
33
|
-
this._connectionState.
|
|
33
|
+
this._connectionState.next(enums_1.PondState.OPEN);
|
|
34
34
|
};
|
|
35
35
|
socket.onmessage = (message) => {
|
|
36
36
|
const data = JSON.parse(message.data);
|
|
37
|
-
this._broadcaster.
|
|
37
|
+
this._broadcaster.next(data);
|
|
38
38
|
};
|
|
39
39
|
socket.onerror = () => {
|
|
40
|
-
this._connectionState.
|
|
40
|
+
this._connectionState.next(enums_1.PondState.CLOSED);
|
|
41
41
|
setTimeout(() => {
|
|
42
42
|
this.connect(backoff * 2);
|
|
43
43
|
}, backoff * 1000);
|
|
@@ -65,7 +65,8 @@ class PondClient {
|
|
|
65
65
|
* @param params - The params to send to the server.
|
|
66
66
|
*/
|
|
67
67
|
createChannel(name, params) {
|
|
68
|
-
|
|
68
|
+
var _a;
|
|
69
|
+
if (((_a = this._channels[name]) === null || _a === void 0 ? void 0 : _a.channelState) !== enums_1.ChannelState.CLOSED) {
|
|
69
70
|
return this._channels[name];
|
|
70
71
|
}
|
|
71
72
|
const publisher = this._createPublisher();
|