@eleven-am/pondsocket-client 0.0.2
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/browser/client.d.ts +34 -0
- package/browser/client.js +105 -0
- package/browser/client.test.d.ts +1 -0
- package/browser/client.test.js +123 -0
- package/core/channel.d.ts +125 -0
- package/core/channel.js +302 -0
- package/core/channel.test.d.ts +1 -0
- package/core/channel.test.js +762 -0
- package/index.d.ts +3 -0
- package/index.js +8 -0
- package/node/node.d.ts +7 -0
- package/node/node.js +31 -0
- package/package.json +66 -0
- package/types.d.ts +54 -0
- package/types.js +2 -0
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
13
|
+
const channel_1 = require("./channel");
|
|
14
|
+
const createChannel = (params = {}) => {
|
|
15
|
+
const publisher = jest.fn();
|
|
16
|
+
const state = new pondsocket_common_1.SimpleBehaviorSubject(true);
|
|
17
|
+
const receiver = new pondsocket_common_1.SimpleSubject();
|
|
18
|
+
const channel = new channel_1.Channel(publisher, state, 'test', receiver, params);
|
|
19
|
+
return {
|
|
20
|
+
channel,
|
|
21
|
+
publisher,
|
|
22
|
+
state,
|
|
23
|
+
receiver,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
describe('Channel', () => {
|
|
27
|
+
it('should correctly send a join message', () => {
|
|
28
|
+
const { channel, publisher, state } = createChannel();
|
|
29
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
30
|
+
expect(state.value).toBe(true);
|
|
31
|
+
channel.join();
|
|
32
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
33
|
+
action: pondsocket_common_1.ClientActions.JOIN_CHANNEL,
|
|
34
|
+
channelName: 'test',
|
|
35
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_USERS,
|
|
36
|
+
event: pondsocket_common_1.ClientActions.JOIN_CHANNEL,
|
|
37
|
+
payload: {},
|
|
38
|
+
}));
|
|
39
|
+
const { channel: channel2, publisher: publisher2, state: state2 } = createChannel();
|
|
40
|
+
state2.publish(false);
|
|
41
|
+
expect(state2.value).toBe(false);
|
|
42
|
+
expect(publisher2).not.toHaveBeenCalled();
|
|
43
|
+
channel2.join();
|
|
44
|
+
expect(publisher2).not.toHaveBeenCalled();
|
|
45
|
+
state2.publish(true);
|
|
46
|
+
expect(publisher2).toHaveBeenCalledWith(expect.objectContaining({
|
|
47
|
+
action: pondsocket_common_1.ClientActions.JOIN_CHANNEL,
|
|
48
|
+
channelName: 'test',
|
|
49
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_USERS,
|
|
50
|
+
event: pondsocket_common_1.ClientActions.JOIN_CHANNEL,
|
|
51
|
+
payload: {},
|
|
52
|
+
}));
|
|
53
|
+
const { channel: channel3 } = createChannel();
|
|
54
|
+
channel3.leave();
|
|
55
|
+
expect(() => channel3.join())
|
|
56
|
+
.toThrow('This channel has been closed');
|
|
57
|
+
});
|
|
58
|
+
it('should correctly send a leave message', () => {
|
|
59
|
+
const { channel, publisher, receiver } = createChannel();
|
|
60
|
+
expect(channel.channelState).toBe(pondsocket_common_1.ChannelState.IDLE);
|
|
61
|
+
channel.join();
|
|
62
|
+
expect(channel.channelState).toBe(pondsocket_common_1.ChannelState.JOINING);
|
|
63
|
+
receiver.publish({
|
|
64
|
+
requestId: 'test',
|
|
65
|
+
action: pondsocket_common_1.ServerActions.SYSTEM,
|
|
66
|
+
channelName: 'test',
|
|
67
|
+
event: 'SYSTEM',
|
|
68
|
+
payload: {
|
|
69
|
+
event: 'JOIN',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
expect(channel.channelState).toBe(pondsocket_common_1.ChannelState.JOINING);
|
|
73
|
+
// once the server responds with an ack, the channel state should be joined
|
|
74
|
+
receiver.publish({
|
|
75
|
+
requestId: 'test',
|
|
76
|
+
action: pondsocket_common_1.ServerActions.SYSTEM,
|
|
77
|
+
channelName: 'test',
|
|
78
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
79
|
+
payload: {
|
|
80
|
+
event: 'JOIN',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
expect(channel.channelState).toBe(pondsocket_common_1.ChannelState.JOINED);
|
|
84
|
+
channel.leave();
|
|
85
|
+
expect(channel.channelState).toBe(pondsocket_common_1.ChannelState.CLOSED);
|
|
86
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
87
|
+
action: pondsocket_common_1.ClientActions.LEAVE_CHANNEL,
|
|
88
|
+
channelName: 'test',
|
|
89
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_USERS,
|
|
90
|
+
event: pondsocket_common_1.ClientActions.LEAVE_CHANNEL,
|
|
91
|
+
payload: {},
|
|
92
|
+
}));
|
|
93
|
+
});
|
|
94
|
+
it('should notify subscribers when the channel state changes', () => {
|
|
95
|
+
const { channel, receiver } = createChannel();
|
|
96
|
+
const stateListener = jest.fn();
|
|
97
|
+
channel.onChannelStateChange(stateListener);
|
|
98
|
+
expect(stateListener).toHaveBeenCalledWith(pondsocket_common_1.ChannelState.IDLE);
|
|
99
|
+
channel.join();
|
|
100
|
+
receiver.publish({
|
|
101
|
+
requestId: 'test',
|
|
102
|
+
action: 'SYSTEM',
|
|
103
|
+
channelName: 'test',
|
|
104
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
105
|
+
payload: {
|
|
106
|
+
event: 'JOIN',
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
expect(stateListener).toHaveBeenCalledWith(pondsocket_common_1.ChannelState.JOINED);
|
|
110
|
+
channel.leave();
|
|
111
|
+
expect(stateListener).toHaveBeenCalledWith(pondsocket_common_1.ChannelState.CLOSED);
|
|
112
|
+
});
|
|
113
|
+
it('should notify subscribers when state changes BOOLEAN', () => {
|
|
114
|
+
const { channel, receiver, state, publisher } = createChannel();
|
|
115
|
+
const stateListener = jest.fn();
|
|
116
|
+
expect(channel.isClosed()).toBe(false);
|
|
117
|
+
expect(channel.isConnected()).toBe(false);
|
|
118
|
+
expect(channel.isStalled()).toBe(false);
|
|
119
|
+
channel.onConnectionChange(stateListener);
|
|
120
|
+
expect(stateListener).toHaveBeenCalledWith(false);
|
|
121
|
+
expect(channel.isClosed()).toBe(false);
|
|
122
|
+
expect(channel.isConnected()).toBe(false);
|
|
123
|
+
expect(channel.isStalled()).toBe(false);
|
|
124
|
+
channel.join();
|
|
125
|
+
expect(channel.isConnected()).toBe(false);
|
|
126
|
+
expect(channel.isStalled()).toBe(false);
|
|
127
|
+
receiver.publish({
|
|
128
|
+
requestId: 'test',
|
|
129
|
+
action: 'SYSTEM',
|
|
130
|
+
channelName: 'test',
|
|
131
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
132
|
+
payload: {
|
|
133
|
+
event: 'JOIN',
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
expect(stateListener).toHaveBeenCalledWith(true);
|
|
137
|
+
expect(channel.isConnected()).toBe(true);
|
|
138
|
+
expect(channel.isStalled()).toBe(false);
|
|
139
|
+
publisher.mockClear();
|
|
140
|
+
state.publish(false);
|
|
141
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
142
|
+
expect(channel.isStalled()).toBe(true);
|
|
143
|
+
expect(channel.isConnected()).toBe(true);
|
|
144
|
+
state.publish(true);
|
|
145
|
+
expect(publisher).toHaveBeenCalledTimes(1);
|
|
146
|
+
receiver.publish({
|
|
147
|
+
requestId: 'test',
|
|
148
|
+
action: 'SYSTEM',
|
|
149
|
+
channelName: 'test',
|
|
150
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
151
|
+
payload: {
|
|
152
|
+
event: 'JOIN',
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
expect(channel.isStalled()).toBe(false);
|
|
156
|
+
expect(channel.isConnected()).toBe(true);
|
|
157
|
+
channel.leave();
|
|
158
|
+
expect(channel.isClosed()).toBe(true);
|
|
159
|
+
expect(channel.isConnected()).toBe(false);
|
|
160
|
+
expect(channel.isStalled()).toBe(false);
|
|
161
|
+
expect(stateListener).toHaveBeenCalledWith(false);
|
|
162
|
+
});
|
|
163
|
+
it('should correctly send a message', () => {
|
|
164
|
+
const { channel, publisher, state, receiver } = createChannel();
|
|
165
|
+
expect(channel.channelState).toBe(pondsocket_common_1.ChannelState.IDLE);
|
|
166
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
167
|
+
// when the socket is connected but the channel is not joined, the message would not be queued
|
|
168
|
+
channel.broadcast('test', { test: false });
|
|
169
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
170
|
+
channel.join();
|
|
171
|
+
publisher.mockClear();
|
|
172
|
+
receiver.publish({
|
|
173
|
+
requestId: 'test',
|
|
174
|
+
action: 'SYSTEM',
|
|
175
|
+
channelName: 'test',
|
|
176
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
177
|
+
payload: {
|
|
178
|
+
event: 'JOIN',
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
// however once the channel is joined, the message should be sent
|
|
182
|
+
channel.broadcast('test', { test: true });
|
|
183
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
184
|
+
action: pondsocket_common_1.ClientActions.BROADCAST,
|
|
185
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_USERS,
|
|
186
|
+
channelName: 'test',
|
|
187
|
+
event: 'test',
|
|
188
|
+
payload: {
|
|
189
|
+
test: true,
|
|
190
|
+
},
|
|
191
|
+
}));
|
|
192
|
+
// if for some reason the socket is disconnected, the message should be queued
|
|
193
|
+
publisher.mockClear();
|
|
194
|
+
state.publish(false);
|
|
195
|
+
expect(state.value).toBe(false);
|
|
196
|
+
channel.broadcast('test', { test: true });
|
|
197
|
+
expect(publisher).not.toHaveBeenCalled();
|
|
198
|
+
// once the socket is reconnected, a join message should be sent and the queued messages should be sent
|
|
199
|
+
publisher.mockClear();
|
|
200
|
+
state.publish(true);
|
|
201
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
202
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_USERS,
|
|
203
|
+
action: pondsocket_common_1.ClientActions.JOIN_CHANNEL,
|
|
204
|
+
channelName: 'test',
|
|
205
|
+
event: pondsocket_common_1.ClientActions.JOIN_CHANNEL,
|
|
206
|
+
payload: {},
|
|
207
|
+
}));
|
|
208
|
+
// until it receives an ack message the queued messages should not be sent
|
|
209
|
+
expect(publisher).toHaveBeenCalledTimes(1);
|
|
210
|
+
receiver.publish({
|
|
211
|
+
requestId: 'test',
|
|
212
|
+
action: 'SYSTEM',
|
|
213
|
+
channelName: 'test',
|
|
214
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
215
|
+
payload: {
|
|
216
|
+
event: 'JOIN',
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
220
|
+
action: pondsocket_common_1.ClientActions.BROADCAST,
|
|
221
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_USERS,
|
|
222
|
+
channelName: 'test',
|
|
223
|
+
event: 'test',
|
|
224
|
+
payload: {
|
|
225
|
+
test: true,
|
|
226
|
+
},
|
|
227
|
+
}));
|
|
228
|
+
// if the channel is closed, the message should not be sent, and should not be queued
|
|
229
|
+
publisher.mockClear();
|
|
230
|
+
channel.leave();
|
|
231
|
+
// the leave message should be sent
|
|
232
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
233
|
+
action: pondsocket_common_1.ClientActions.LEAVE_CHANNEL,
|
|
234
|
+
channelName: 'test',
|
|
235
|
+
event: pondsocket_common_1.ClientActions.LEAVE_CHANNEL,
|
|
236
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_USERS,
|
|
237
|
+
payload: {},
|
|
238
|
+
}));
|
|
239
|
+
expect(channel.channelState).toBe(pondsocket_common_1.ChannelState.CLOSED);
|
|
240
|
+
channel.broadcast('test', { test: true });
|
|
241
|
+
expect(publisher).toHaveBeenCalledTimes(1);
|
|
242
|
+
});
|
|
243
|
+
// The presence system tests
|
|
244
|
+
it('should notify subscribers when a user joins the channel', () => {
|
|
245
|
+
const { channel, receiver } = createChannel();
|
|
246
|
+
const presenceListener = jest.fn();
|
|
247
|
+
channel.onJoin(presenceListener);
|
|
248
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
249
|
+
// usually the server wouldn't send a presence if the channel is not joined
|
|
250
|
+
// but for testing purposes, we'll just send it anyway
|
|
251
|
+
receiver.publish({
|
|
252
|
+
requestId: 'test',
|
|
253
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
254
|
+
event: pondsocket_common_1.PresenceEventTypes.JOIN,
|
|
255
|
+
channelName: 'test',
|
|
256
|
+
payload: {
|
|
257
|
+
changed: {
|
|
258
|
+
id: 'test',
|
|
259
|
+
status: 'online',
|
|
260
|
+
},
|
|
261
|
+
presence: [
|
|
262
|
+
{
|
|
263
|
+
id: 'test',
|
|
264
|
+
status: 'online',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
id: 'test2',
|
|
268
|
+
status: 'online',
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
expect(presenceListener).toHaveBeenCalledWith({
|
|
274
|
+
id: 'test',
|
|
275
|
+
status: 'online',
|
|
276
|
+
});
|
|
277
|
+
presenceListener.mockClear();
|
|
278
|
+
receiver.publish({
|
|
279
|
+
requestId: 'test',
|
|
280
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
281
|
+
event: pondsocket_common_1.PresenceEventTypes.UPDATE,
|
|
282
|
+
channelName: 'test',
|
|
283
|
+
payload: {
|
|
284
|
+
changed: {
|
|
285
|
+
id: 'test',
|
|
286
|
+
status: 'online',
|
|
287
|
+
},
|
|
288
|
+
presence: [
|
|
289
|
+
{
|
|
290
|
+
id: 'test',
|
|
291
|
+
status: 'online',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: 'test2',
|
|
295
|
+
status: 'online',
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
301
|
+
// also, if a presence event is received for a different channel, it should not be sent to the listener
|
|
302
|
+
receiver.publish({
|
|
303
|
+
requestId: 'test',
|
|
304
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
305
|
+
event: pondsocket_common_1.PresenceEventTypes.JOIN,
|
|
306
|
+
channelName: 'test2',
|
|
307
|
+
payload: {
|
|
308
|
+
changed: {
|
|
309
|
+
id: 'test',
|
|
310
|
+
status: 'online',
|
|
311
|
+
},
|
|
312
|
+
presence: [
|
|
313
|
+
{
|
|
314
|
+
id: 'test',
|
|
315
|
+
status: 'online',
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: 'test2',
|
|
319
|
+
status: 'online',
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
325
|
+
});
|
|
326
|
+
it('should notify subscribers when a user leaves the channel', () => {
|
|
327
|
+
const { channel, receiver } = createChannel();
|
|
328
|
+
const presenceListener = jest.fn();
|
|
329
|
+
channel.onLeave(presenceListener);
|
|
330
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
331
|
+
// usually the server wouldn't send a presence if the channel is not joined
|
|
332
|
+
// but for testing purposes, we'll just send it anyway
|
|
333
|
+
receiver.publish({
|
|
334
|
+
requestId: 'test',
|
|
335
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
336
|
+
event: pondsocket_common_1.PresenceEventTypes.LEAVE,
|
|
337
|
+
channelName: 'test',
|
|
338
|
+
payload: {
|
|
339
|
+
changed: {
|
|
340
|
+
id: 'test',
|
|
341
|
+
status: 'online',
|
|
342
|
+
},
|
|
343
|
+
presence: [
|
|
344
|
+
{
|
|
345
|
+
id: 'test',
|
|
346
|
+
status: 'online',
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: 'test2',
|
|
350
|
+
status: 'online',
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
expect(presenceListener).toHaveBeenCalledWith({
|
|
356
|
+
id: 'test',
|
|
357
|
+
status: 'online',
|
|
358
|
+
});
|
|
359
|
+
presenceListener.mockClear();
|
|
360
|
+
receiver.publish({
|
|
361
|
+
requestId: 'test',
|
|
362
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
363
|
+
event: pondsocket_common_1.PresenceEventTypes.UPDATE,
|
|
364
|
+
channelName: 'test',
|
|
365
|
+
payload: {
|
|
366
|
+
changed: {
|
|
367
|
+
id: 'test',
|
|
368
|
+
status: 'online',
|
|
369
|
+
},
|
|
370
|
+
presence: [
|
|
371
|
+
{
|
|
372
|
+
id: 'test',
|
|
373
|
+
status: 'online',
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
id: 'test2',
|
|
377
|
+
status: 'online',
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
383
|
+
// also, if a presence event is received for a different channel, it should not be sent to the listener
|
|
384
|
+
receiver.publish({
|
|
385
|
+
requestId: 'test',
|
|
386
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
387
|
+
event: pondsocket_common_1.PresenceEventTypes.LEAVE,
|
|
388
|
+
channelName: 'test2',
|
|
389
|
+
payload: {
|
|
390
|
+
changed: {
|
|
391
|
+
id: 'test',
|
|
392
|
+
status: 'online',
|
|
393
|
+
},
|
|
394
|
+
presence: [
|
|
395
|
+
{
|
|
396
|
+
id: 'test',
|
|
397
|
+
status: 'online',
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
id: 'test2',
|
|
401
|
+
status: 'online',
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
407
|
+
});
|
|
408
|
+
it('should notify subscribers when a user updates their presence', () => {
|
|
409
|
+
const { channel, receiver } = createChannel();
|
|
410
|
+
const presenceListener = jest.fn();
|
|
411
|
+
channel.onUsersChange(presenceListener);
|
|
412
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
413
|
+
// usually the server wouldn't send a presence if the channel is not joined
|
|
414
|
+
// but for testing purposes, we'll just send it anyway
|
|
415
|
+
receiver.publish({
|
|
416
|
+
requestId: 'test',
|
|
417
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
418
|
+
event: pondsocket_common_1.PresenceEventTypes.UPDATE,
|
|
419
|
+
channelName: 'test',
|
|
420
|
+
payload: {
|
|
421
|
+
changed: {
|
|
422
|
+
id: 'test',
|
|
423
|
+
status: 'online',
|
|
424
|
+
},
|
|
425
|
+
presence: [
|
|
426
|
+
{
|
|
427
|
+
id: 'test',
|
|
428
|
+
status: 'online',
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
id: 'test2',
|
|
432
|
+
status: 'online',
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
expect(presenceListener).toHaveBeenCalledWith([
|
|
438
|
+
{
|
|
439
|
+
id: 'test',
|
|
440
|
+
status: 'online',
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
id: 'test2',
|
|
444
|
+
status: 'online',
|
|
445
|
+
},
|
|
446
|
+
]);
|
|
447
|
+
expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
|
|
448
|
+
presenceListener.mockClear();
|
|
449
|
+
// if we receive a leave or join event, it should be sent to the listener
|
|
450
|
+
// this is because we are listening for all presence events
|
|
451
|
+
receiver.publish({
|
|
452
|
+
requestId: 'test',
|
|
453
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
454
|
+
event: pondsocket_common_1.PresenceEventTypes.JOIN,
|
|
455
|
+
channelName: 'test',
|
|
456
|
+
payload: {
|
|
457
|
+
changed: {
|
|
458
|
+
id: 'test',
|
|
459
|
+
status: 'online',
|
|
460
|
+
},
|
|
461
|
+
presence: [
|
|
462
|
+
{
|
|
463
|
+
id: 'test',
|
|
464
|
+
status: 'online',
|
|
465
|
+
},
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
expect(presenceListener).toHaveBeenCalledWith([
|
|
470
|
+
{
|
|
471
|
+
id: 'test',
|
|
472
|
+
status: 'online',
|
|
473
|
+
},
|
|
474
|
+
]);
|
|
475
|
+
expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
|
|
476
|
+
presenceListener.mockClear();
|
|
477
|
+
// also, if a presence event is received for a different channel, it should not be sent to the listener
|
|
478
|
+
receiver.publish({
|
|
479
|
+
requestId: 'test',
|
|
480
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
481
|
+
event: pondsocket_common_1.PresenceEventTypes.UPDATE,
|
|
482
|
+
channelName: 'test2',
|
|
483
|
+
payload: {
|
|
484
|
+
changed: {
|
|
485
|
+
id: 'test',
|
|
486
|
+
status: 'online',
|
|
487
|
+
},
|
|
488
|
+
presence: [
|
|
489
|
+
{
|
|
490
|
+
id: 'test',
|
|
491
|
+
status: 'online',
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
id: 'test2',
|
|
495
|
+
status: 'online',
|
|
496
|
+
},
|
|
497
|
+
],
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
501
|
+
// we once again send a presence event for the same channel, but this time it should be sent to the listener
|
|
502
|
+
receiver.publish({
|
|
503
|
+
requestId: 'test',
|
|
504
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
505
|
+
event: pondsocket_common_1.PresenceEventTypes.UPDATE,
|
|
506
|
+
channelName: 'test',
|
|
507
|
+
payload: {
|
|
508
|
+
changed: {
|
|
509
|
+
id: 'test',
|
|
510
|
+
status: 'away',
|
|
511
|
+
},
|
|
512
|
+
presence: [
|
|
513
|
+
{
|
|
514
|
+
id: 'test',
|
|
515
|
+
status: 'away',
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
id: 'test2',
|
|
519
|
+
status: 'online',
|
|
520
|
+
},
|
|
521
|
+
],
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
expect(presenceListener).toHaveBeenCalledWith([
|
|
525
|
+
{
|
|
526
|
+
id: 'test',
|
|
527
|
+
status: 'away',
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
id: 'test2',
|
|
531
|
+
status: 'online',
|
|
532
|
+
},
|
|
533
|
+
]);
|
|
534
|
+
expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
|
|
535
|
+
});
|
|
536
|
+
it('should notify subscribers when any presence event occurs', () => {
|
|
537
|
+
const { channel, receiver } = createChannel();
|
|
538
|
+
const presenceListener = jest.fn();
|
|
539
|
+
channel.onPresenceChange(presenceListener);
|
|
540
|
+
expect(presenceListener).not.toHaveBeenCalled();
|
|
541
|
+
// usually the server wouldn't send a presence if the channel is not joined
|
|
542
|
+
// but for testing purposes, we'll just send it anyway
|
|
543
|
+
receiver.publish({
|
|
544
|
+
requestId: 'test',
|
|
545
|
+
action: pondsocket_common_1.ServerActions.PRESENCE,
|
|
546
|
+
event: pondsocket_common_1.PresenceEventTypes.UPDATE,
|
|
547
|
+
channelName: 'test',
|
|
548
|
+
payload: {
|
|
549
|
+
changed: {
|
|
550
|
+
id: 'test',
|
|
551
|
+
status: 'online',
|
|
552
|
+
},
|
|
553
|
+
presence: [
|
|
554
|
+
{
|
|
555
|
+
id: 'test',
|
|
556
|
+
status: 'online',
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
id: 'test2',
|
|
560
|
+
status: 'online',
|
|
561
|
+
},
|
|
562
|
+
],
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
expect(presenceListener).toHaveBeenCalledWith({
|
|
566
|
+
changed: {
|
|
567
|
+
id: 'test',
|
|
568
|
+
status: 'online',
|
|
569
|
+
},
|
|
570
|
+
presence: [
|
|
571
|
+
{
|
|
572
|
+
id: 'test',
|
|
573
|
+
status: 'online',
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
id: 'test2',
|
|
577
|
+
status: 'online',
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
// message events
|
|
583
|
+
it('should notify subscribers when a message is received', () => {
|
|
584
|
+
const { channel, receiver } = createChannel();
|
|
585
|
+
const messageListener = jest.fn();
|
|
586
|
+
channel.onMessage(messageListener);
|
|
587
|
+
expect(messageListener).not.toHaveBeenCalled();
|
|
588
|
+
receiver.publish({
|
|
589
|
+
requestId: 'test',
|
|
590
|
+
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
591
|
+
event: 'message',
|
|
592
|
+
channelName: 'test',
|
|
593
|
+
payload: {
|
|
594
|
+
id: 'test',
|
|
595
|
+
message: 'test',
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
expect(messageListener).toHaveBeenCalledWith('message', {
|
|
599
|
+
id: 'test',
|
|
600
|
+
message: 'test',
|
|
601
|
+
});
|
|
602
|
+
// if a message event is received for a different channel, it should not be sent to the listener
|
|
603
|
+
receiver.publish({
|
|
604
|
+
requestId: 'test',
|
|
605
|
+
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
606
|
+
event: 'message',
|
|
607
|
+
channelName: 'test2',
|
|
608
|
+
payload: {
|
|
609
|
+
id: 'test',
|
|
610
|
+
message: 'test',
|
|
611
|
+
},
|
|
612
|
+
});
|
|
613
|
+
expect(messageListener).toHaveBeenCalledTimes(1);
|
|
614
|
+
// we can also subscribe to a specific message event
|
|
615
|
+
const specificMessageListener = jest.fn();
|
|
616
|
+
channel.onMessageEvent('test', specificMessageListener);
|
|
617
|
+
expect(specificMessageListener).not.toHaveBeenCalled();
|
|
618
|
+
receiver.publish({
|
|
619
|
+
requestId: 'test',
|
|
620
|
+
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
621
|
+
event: 'test',
|
|
622
|
+
channelName: 'test',
|
|
623
|
+
payload: {
|
|
624
|
+
id: 'test',
|
|
625
|
+
message: 'test',
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
expect(specificMessageListener).toHaveBeenCalledWith({
|
|
629
|
+
id: 'test',
|
|
630
|
+
message: 'test',
|
|
631
|
+
});
|
|
632
|
+
specificMessageListener.mockClear();
|
|
633
|
+
// if a message event is received for the same channel, but a different event, it should not be sent to the listener
|
|
634
|
+
receiver.publish({
|
|
635
|
+
requestId: 'test',
|
|
636
|
+
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
637
|
+
event: 'test2',
|
|
638
|
+
channelName: 'test',
|
|
639
|
+
payload: {
|
|
640
|
+
id: 'test',
|
|
641
|
+
message: 'test',
|
|
642
|
+
},
|
|
643
|
+
});
|
|
644
|
+
expect(specificMessageListener).not.toHaveBeenCalled();
|
|
645
|
+
// if a message event is received for a different channel, it should not be sent to the listener
|
|
646
|
+
receiver.publish({
|
|
647
|
+
requestId: 'test',
|
|
648
|
+
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
649
|
+
event: 'test',
|
|
650
|
+
channelName: 'test2',
|
|
651
|
+
payload: {
|
|
652
|
+
id: 'test',
|
|
653
|
+
message: 'test',
|
|
654
|
+
},
|
|
655
|
+
});
|
|
656
|
+
expect(specificMessageListener).not.toHaveBeenCalled();
|
|
657
|
+
});
|
|
658
|
+
it('should be able to broadcast a message', () => {
|
|
659
|
+
const { channel, receiver, publisher } = createChannel();
|
|
660
|
+
channel.join();
|
|
661
|
+
publisher.mockClear();
|
|
662
|
+
receiver.publish({
|
|
663
|
+
requestId: 'test',
|
|
664
|
+
action: 'SYSTEM',
|
|
665
|
+
channelName: 'test',
|
|
666
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
667
|
+
payload: {
|
|
668
|
+
event: 'JOIN',
|
|
669
|
+
},
|
|
670
|
+
});
|
|
671
|
+
channel.sendMessage('test', {
|
|
672
|
+
test: 'test',
|
|
673
|
+
}, ['test1']);
|
|
674
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
675
|
+
action: pondsocket_common_1.ClientActions.BROADCAST,
|
|
676
|
+
channelName: 'test',
|
|
677
|
+
event: 'test',
|
|
678
|
+
payload: {
|
|
679
|
+
test: 'test',
|
|
680
|
+
},
|
|
681
|
+
addresses: ['test1'],
|
|
682
|
+
}));
|
|
683
|
+
publisher.mockClear();
|
|
684
|
+
channel.broadcastFrom('test', {
|
|
685
|
+
test: 'test',
|
|
686
|
+
});
|
|
687
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
688
|
+
action: pondsocket_common_1.ClientActions.BROADCAST,
|
|
689
|
+
channelName: 'test',
|
|
690
|
+
event: 'test',
|
|
691
|
+
payload: {
|
|
692
|
+
test: 'test',
|
|
693
|
+
},
|
|
694
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER,
|
|
695
|
+
}));
|
|
696
|
+
publisher.mockClear();
|
|
697
|
+
channel.broadcast('test', {
|
|
698
|
+
test: 'test',
|
|
699
|
+
});
|
|
700
|
+
expect(publisher).toHaveBeenCalledWith(expect.objectContaining({
|
|
701
|
+
action: pondsocket_common_1.ClientActions.BROADCAST,
|
|
702
|
+
channelName: 'test',
|
|
703
|
+
event: 'test',
|
|
704
|
+
payload: {
|
|
705
|
+
test: 'test',
|
|
706
|
+
},
|
|
707
|
+
addresses: pondsocket_common_1.ChannelReceiver.ALL_USERS,
|
|
708
|
+
}));
|
|
709
|
+
});
|
|
710
|
+
it('should be able to wait for a message', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
711
|
+
const state = new pondsocket_common_1.SimpleBehaviorSubject(true);
|
|
712
|
+
const receiver = new pondsocket_common_1.SimpleSubject();
|
|
713
|
+
const params = {};
|
|
714
|
+
const publisher = (data) => {
|
|
715
|
+
if (data.event === 'WAITING') {
|
|
716
|
+
// if the server response with another request id, it should not be sent to the listener
|
|
717
|
+
receiver.publish({
|
|
718
|
+
requestId: 'test',
|
|
719
|
+
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
720
|
+
event: 'test',
|
|
721
|
+
channelName: 'test',
|
|
722
|
+
payload: {
|
|
723
|
+
id: 'test',
|
|
724
|
+
response: 'test-response',
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
// if the server response with the same request id, it should be sent to the listener
|
|
728
|
+
receiver.publish({
|
|
729
|
+
requestId: data.requestId,
|
|
730
|
+
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
731
|
+
event: 'test',
|
|
732
|
+
channelName: 'test',
|
|
733
|
+
payload: {
|
|
734
|
+
id: 'test',
|
|
735
|
+
response: 'second-test-response',
|
|
736
|
+
},
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
const channel = new channel_1.Channel(publisher, state, 'test', receiver, params);
|
|
741
|
+
channel.join();
|
|
742
|
+
receiver.publish({
|
|
743
|
+
requestId: 'test',
|
|
744
|
+
action: 'SYSTEM',
|
|
745
|
+
channelName: 'test',
|
|
746
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
747
|
+
payload: {
|
|
748
|
+
event: 'JOIN',
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
752
|
+
// @ts-expect-error - this is a test
|
|
753
|
+
const response = yield channel.sendForResponse('WAITING', {
|
|
754
|
+
id: 'test',
|
|
755
|
+
message: 'test',
|
|
756
|
+
});
|
|
757
|
+
expect(response).toEqual({
|
|
758
|
+
id: 'test',
|
|
759
|
+
response: 'second-test-response',
|
|
760
|
+
});
|
|
761
|
+
}));
|
|
762
|
+
});
|