]> git.proxmox.com Git - mirror_novnc.git/blob - tests/test.websock.js
feat: add French localization strings
[mirror_novnc.git] / tests / test.websock.js
1 const expect = chai.expect;
2
3 import Websock from '../core/websock.js';
4 import FakeWebSocket from './fake.websocket.js';
5
6 describe('Websock', function () {
7 "use strict";
8
9 describe('Queue methods', function () {
10 let sock;
11 const RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
12
13 beforeEach(function () {
14 sock = new Websock();
15 // skip init
16 sock._allocateBuffers();
17 sock._rQ.set(RQ_TEMPLATE);
18 sock._rQlen = RQ_TEMPLATE.length;
19 });
20 describe('rQlen', function () {
21 it('should return the length of the receive queue', function () {
22 sock.rQi = 0;
23
24 expect(sock.rQlen).to.equal(RQ_TEMPLATE.length);
25 });
26
27 it("should return the proper length if we read some from the receive queue", function () {
28 sock.rQi = 1;
29
30 expect(sock.rQlen).to.equal(RQ_TEMPLATE.length - 1);
31 });
32 });
33
34 describe('rQpeek8', function () {
35 it('should peek at the next byte without poping it off the queue', function () {
36 const befLen = sock.rQlen;
37 const peek = sock.rQpeek8();
38 expect(sock.rQpeek8()).to.equal(peek);
39 expect(sock.rQlen).to.equal(befLen);
40 });
41 });
42
43 describe('rQshift8()', function () {
44 it('should pop a single byte from the receive queue', function () {
45 const peek = sock.rQpeek8();
46 const befLen = sock.rQlen;
47 expect(sock.rQshift8()).to.equal(peek);
48 expect(sock.rQlen).to.equal(befLen - 1);
49 });
50 });
51
52 describe('rQshift16()', function () {
53 it('should pop two bytes from the receive queue and return a single number', function () {
54 const befLen = sock.rQlen;
55 const expected = (RQ_TEMPLATE[0] << 8) + RQ_TEMPLATE[1];
56 expect(sock.rQshift16()).to.equal(expected);
57 expect(sock.rQlen).to.equal(befLen - 2);
58 });
59 });
60
61 describe('rQshift32()', function () {
62 it('should pop four bytes from the receive queue and return a single number', function () {
63 const befLen = sock.rQlen;
64 const expected = (RQ_TEMPLATE[0] << 24) +
65 (RQ_TEMPLATE[1] << 16) +
66 (RQ_TEMPLATE[2] << 8) +
67 RQ_TEMPLATE[3];
68 expect(sock.rQshift32()).to.equal(expected);
69 expect(sock.rQlen).to.equal(befLen - 4);
70 });
71 });
72
73 describe('rQshiftStr', function () {
74 it('should shift the given number of bytes off of the receive queue and return a string', function () {
75 const befLen = sock.rQlen;
76 const befRQi = sock.rQi;
77 const shifted = sock.rQshiftStr(3);
78 expect(shifted).to.be.a('string');
79 expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, befRQi, 3))));
80 expect(sock.rQlen).to.equal(befLen - 3);
81 });
82
83 it('should shift the entire rest of the queue off if no length is given', function () {
84 sock.rQshiftStr();
85 expect(sock.rQlen).to.equal(0);
86 });
87
88 it('should be able to handle very large strings', function () {
89 const BIG_LEN = 500000;
90 const RQ_BIG = new Uint8Array(BIG_LEN);
91 let expected = "";
92 let letterCode = 'a'.charCodeAt(0);
93 for (let i = 0; i < BIG_LEN; i++) {
94 RQ_BIG[i] = letterCode;
95 expected += String.fromCharCode(letterCode);
96
97 if (letterCode < 'z'.charCodeAt(0)) {
98 letterCode++;
99 } else {
100 letterCode = 'a'.charCodeAt(0);
101 }
102 }
103 sock._rQ.set(RQ_BIG);
104 sock._rQlen = RQ_BIG.length;
105
106 const shifted = sock.rQshiftStr();
107
108 expect(shifted).to.be.equal(expected);
109 expect(sock.rQlen).to.equal(0);
110 });
111 });
112
113 describe('rQshiftBytes', function () {
114 it('should shift the given number of bytes of the receive queue and return an array', function () {
115 const befLen = sock.rQlen;
116 const befRQi = sock.rQi;
117 const shifted = sock.rQshiftBytes(3);
118 expect(shifted).to.be.an.instanceof(Uint8Array);
119 expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, befRQi, 3));
120 expect(sock.rQlen).to.equal(befLen - 3);
121 });
122
123 it('should shift the entire rest of the queue off if no length is given', function () {
124 sock.rQshiftBytes();
125 expect(sock.rQlen).to.equal(0);
126 });
127 });
128
129 describe('rQslice', function () {
130 beforeEach(function () {
131 sock.rQi = 0;
132 });
133
134 it('should not modify the receive queue', function () {
135 const befLen = sock.rQlen;
136 sock.rQslice(0, 2);
137 expect(sock.rQlen).to.equal(befLen);
138 });
139
140 it('should return an array containing the given slice of the receive queue', function () {
141 const sl = sock.rQslice(0, 2);
142 expect(sl).to.be.an.instanceof(Uint8Array);
143 expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 0, 2));
144 });
145
146 it('should use the rest of the receive queue if no end is given', function () {
147 const sl = sock.rQslice(1);
148 expect(sl).to.have.length(RQ_TEMPLATE.length - 1);
149 expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1));
150 });
151
152 it('should take the current rQi in to account', function () {
153 sock.rQi = 1;
154 expect(sock.rQslice(0, 2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2));
155 });
156 });
157
158 describe('rQwait', function () {
159 beforeEach(function () {
160 sock.rQi = 0;
161 });
162
163 it('should return true if there are not enough bytes in the receive queue', function () {
164 expect(sock.rQwait('hi', RQ_TEMPLATE.length + 1)).to.be.true;
165 });
166
167 it('should return false if there are enough bytes in the receive queue', function () {
168 expect(sock.rQwait('hi', RQ_TEMPLATE.length)).to.be.false;
169 });
170
171 it('should return true and reduce rQi by "goback" if there are not enough bytes', function () {
172 sock.rQi = 5;
173 expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true;
174 expect(sock.rQi).to.equal(1);
175 });
176
177 it('should raise an error if we try to go back more than possible', function () {
178 sock.rQi = 5;
179 expect(() => sock.rQwait('hi', RQ_TEMPLATE.length, 6)).to.throw(Error);
180 });
181
182 it('should not reduce rQi if there are enough bytes', function () {
183 sock.rQi = 5;
184 sock.rQwait('hi', 1, 6);
185 expect(sock.rQi).to.equal(5);
186 });
187 });
188
189 describe('flush', function () {
190 beforeEach(function () {
191 sock._websocket = {
192 send: sinon.spy()
193 };
194 });
195
196 it('should actually send on the websocket', function () {
197 sock._websocket.bufferedAmount = 8;
198 sock._websocket.readyState = WebSocket.OPEN;
199 sock._sQ = new Uint8Array([1, 2, 3]);
200 sock._sQlen = 3;
201 const encoded = sock._encodeMessage();
202
203 sock.flush();
204 expect(sock._websocket.send).to.have.been.calledOnce;
205 expect(sock._websocket.send).to.have.been.calledWith(encoded);
206 });
207
208 it('should not call send if we do not have anything queued up', function () {
209 sock._sQlen = 0;
210 sock._websocket.bufferedAmount = 8;
211
212 sock.flush();
213
214 expect(sock._websocket.send).not.to.have.been.called;
215 });
216 });
217
218 describe('send', function () {
219 beforeEach(function () {
220 sock.flush = sinon.spy();
221 });
222
223 it('should add to the send queue', function () {
224 sock.send([1, 2, 3]);
225 const sq = sock.sQ;
226 expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
227 });
228
229 it('should call flush', function () {
230 sock.send([1, 2, 3]);
231 expect(sock.flush).to.have.been.calledOnce;
232 });
233 });
234
235 describe('sendString', function () {
236 beforeEach(function () {
237 sock.send = sinon.spy();
238 });
239
240 it('should call send after converting the string to an array', function () {
241 sock.sendString("\x01\x02\x03");
242 expect(sock.send).to.have.been.calledWith([1, 2, 3]);
243 });
244 });
245 });
246
247 describe('lifecycle methods', function () {
248 let oldWS;
249 before(function () {
250 oldWS = WebSocket;
251 });
252
253 let sock;
254 beforeEach(function () {
255 sock = new Websock();
256 // eslint-disable-next-line no-global-assign
257 WebSocket = sinon.spy(FakeWebSocket);
258 });
259
260 describe('opening', function () {
261 it('should pick the correct protocols if none are given', function () {
262
263 });
264
265 it('should open the actual websocket', function () {
266 sock.open('ws://localhost:8675', 'binary');
267 expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary');
268 });
269
270 // it('should initialize the event handlers')?
271 });
272
273 describe('attaching', function () {
274 it('should attach to an existing websocket', function () {
275 let ws = new FakeWebSocket('ws://localhost:8675');
276 sock.attach(ws);
277 expect(WebSocket).to.not.have.been.called;
278 });
279 });
280
281 describe('closing', function () {
282 beforeEach(function () {
283 sock.open('ws://localhost');
284 sock._websocket.close = sinon.spy();
285 });
286
287 it('should close the actual websocket if it is open', function () {
288 sock._websocket.readyState = WebSocket.OPEN;
289 sock.close();
290 expect(sock._websocket.close).to.have.been.calledOnce;
291 });
292
293 it('should close the actual websocket if it is connecting', function () {
294 sock._websocket.readyState = WebSocket.CONNECTING;
295 sock.close();
296 expect(sock._websocket.close).to.have.been.calledOnce;
297 });
298
299 it('should not try to close the actual websocket if closing', function () {
300 sock._websocket.readyState = WebSocket.CLOSING;
301 sock.close();
302 expect(sock._websocket.close).not.to.have.been.called;
303 });
304
305 it('should not try to close the actual websocket if closed', function () {
306 sock._websocket.readyState = WebSocket.CLOSED;
307 sock.close();
308 expect(sock._websocket.close).not.to.have.been.called;
309 });
310
311 it('should reset onmessage to not call _recvMessage', function () {
312 sinon.spy(sock, '_recvMessage');
313 sock.close();
314 sock._websocket.onmessage(null);
315 try {
316 expect(sock._recvMessage).not.to.have.been.called;
317 } finally {
318 sock._recvMessage.restore();
319 }
320 });
321 });
322
323 describe('event handlers', function () {
324 beforeEach(function () {
325 sock._recvMessage = sinon.spy();
326 sock.on('open', sinon.spy());
327 sock.on('close', sinon.spy());
328 sock.on('error', sinon.spy());
329 sock.open('ws://localhost');
330 });
331
332 it('should call _recvMessage on a message', function () {
333 sock._websocket.onmessage(null);
334 expect(sock._recvMessage).to.have.been.calledOnce;
335 });
336
337 it('should call the open event handler on opening', function () {
338 sock._websocket.onopen();
339 expect(sock._eventHandlers.open).to.have.been.calledOnce;
340 });
341
342 it('should call the close event handler on closing', function () {
343 sock._websocket.onclose();
344 expect(sock._eventHandlers.close).to.have.been.calledOnce;
345 });
346
347 it('should call the error event handler on error', function () {
348 sock._websocket.onerror();
349 expect(sock._eventHandlers.error).to.have.been.calledOnce;
350 });
351 });
352
353 describe('ready state', function () {
354 it('should be "unused" after construction', function () {
355 let sock = new Websock();
356 expect(sock.readyState).to.equal('unused');
357 });
358
359 it('should be "connecting" if WebSocket is connecting', function () {
360 let sock = new Websock();
361 let ws = new FakeWebSocket();
362 ws.readyState = WebSocket.CONNECTING;
363 sock.attach(ws);
364 expect(sock.readyState).to.equal('connecting');
365 });
366
367 it('should be "open" if WebSocket is open', function () {
368 let sock = new Websock();
369 let ws = new FakeWebSocket();
370 ws.readyState = WebSocket.OPEN;
371 sock.attach(ws);
372 expect(sock.readyState).to.equal('open');
373 });
374
375 it('should be "closing" if WebSocket is closing', function () {
376 let sock = new Websock();
377 let ws = new FakeWebSocket();
378 ws.readyState = WebSocket.CLOSING;
379 sock.attach(ws);
380 expect(sock.readyState).to.equal('closing');
381 });
382
383 it('should be "closed" if WebSocket is closed', function () {
384 let sock = new Websock();
385 let ws = new FakeWebSocket();
386 ws.readyState = WebSocket.CLOSED;
387 sock.attach(ws);
388 expect(sock.readyState).to.equal('closed');
389 });
390
391 it('should be "unknown" if WebSocket state is unknown', function () {
392 let sock = new Websock();
393 let ws = new FakeWebSocket();
394 ws.readyState = 666;
395 sock.attach(ws);
396 expect(sock.readyState).to.equal('unknown');
397 });
398
399 it('should be "connecting" if RTCDataChannel is connecting', function () {
400 let sock = new Websock();
401 let ws = new FakeWebSocket();
402 ws.readyState = 'connecting';
403 sock.attach(ws);
404 expect(sock.readyState).to.equal('connecting');
405 });
406
407 it('should be "open" if RTCDataChannel is open', function () {
408 let sock = new Websock();
409 let ws = new FakeWebSocket();
410 ws.readyState = 'open';
411 sock.attach(ws);
412 expect(sock.readyState).to.equal('open');
413 });
414
415 it('should be "closing" if RTCDataChannel is closing', function () {
416 let sock = new Websock();
417 let ws = new FakeWebSocket();
418 ws.readyState = 'closing';
419 sock.attach(ws);
420 expect(sock.readyState).to.equal('closing');
421 });
422
423 it('should be "closed" if RTCDataChannel is closed', function () {
424 let sock = new Websock();
425 let ws = new FakeWebSocket();
426 ws.readyState = 'closed';
427 sock.attach(ws);
428 expect(sock.readyState).to.equal('closed');
429 });
430
431 it('should be "unknown" if RTCDataChannel state is unknown', function () {
432 let sock = new Websock();
433 let ws = new FakeWebSocket();
434 ws.readyState = 'foobar';
435 sock.attach(ws);
436 expect(sock.readyState).to.equal('unknown');
437 });
438 });
439
440 after(function () {
441 // eslint-disable-next-line no-global-assign
442 WebSocket = oldWS;
443 });
444 });
445
446 describe('WebSocket Receiving', function () {
447 let sock;
448 beforeEach(function () {
449 sock = new Websock();
450 sock._allocateBuffers();
451 });
452
453 it('should support adding binary Uint8Array data to the receive queue', function () {
454 const msg = { data: new Uint8Array([1, 2, 3]) };
455 sock._mode = 'binary';
456 sock._recvMessage(msg);
457 expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
458 });
459
460 it('should call the message event handler if present', function () {
461 sock._eventHandlers.message = sinon.spy();
462 const msg = { data: new Uint8Array([1, 2, 3]).buffer };
463 sock._mode = 'binary';
464 sock._recvMessage(msg);
465 expect(sock._eventHandlers.message).to.have.been.calledOnce;
466 });
467
468 it('should not call the message event handler if there is nothing in the receive queue', function () {
469 sock._eventHandlers.message = sinon.spy();
470 const msg = { data: new Uint8Array([]).buffer };
471 sock._mode = 'binary';
472 sock._recvMessage(msg);
473 expect(sock._eventHandlers.message).not.to.have.been.called;
474 });
475
476 it('should compact the receive queue when a message handler empties it', function () {
477 sock._eventHandlers.message = () => { sock.rQi = sock._rQlen; };
478 sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
479 sock._rQlen = 6;
480 sock.rQi = 6;
481 const msg = { data: new Uint8Array([1, 2, 3]).buffer };
482 sock._mode = 'binary';
483 sock._recvMessage(msg);
484 expect(sock._rQlen).to.equal(0);
485 expect(sock.rQi).to.equal(0);
486 });
487
488 it('should compact the receive queue when we reach the end of the buffer', function () {
489 sock._rQ = new Uint8Array(20);
490 sock._rQbufferSize = 20;
491 sock._rQlen = 20;
492 sock.rQi = 10;
493 const msg = { data: new Uint8Array([1, 2]).buffer };
494 sock._mode = 'binary';
495 sock._recvMessage(msg);
496 expect(sock._rQlen).to.equal(12);
497 expect(sock.rQi).to.equal(0);
498 });
499
500 it('should automatically resize the receive queue if the incoming message is larger than the buffer', function () {
501 sock._rQ = new Uint8Array(20);
502 sock._rQlen = 0;
503 sock.rQi = 0;
504 sock._rQbufferSize = 20;
505 const msg = { data: new Uint8Array(30).buffer };
506 sock._mode = 'binary';
507 sock._recvMessage(msg);
508 expect(sock._rQlen).to.equal(30);
509 expect(sock.rQi).to.equal(0);
510 expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen
511 });
512
513 it('should automatically resize the receive queue if the incoming message is larger than 1/8th of the buffer and we reach the end of the buffer', function () {
514 sock._rQ = new Uint8Array(20);
515 sock._rQlen = 16;
516 sock.rQi = 16;
517 sock._rQbufferSize = 20;
518 const msg = { data: new Uint8Array(6).buffer };
519 sock._mode = 'binary';
520 sock._recvMessage(msg);
521 expect(sock._rQlen).to.equal(6);
522 expect(sock.rQi).to.equal(0);
523 expect(sock._rQ.length).to.equal(48);
524 });
525 });
526
527 describe('Data encoding', function () {
528 before(function () { FakeWebSocket.replace(); });
529 after(function () { FakeWebSocket.restore(); });
530
531 describe('as binary data', function () {
532 let sock;
533 beforeEach(function () {
534 sock = new Websock();
535 sock.open('ws://', 'binary');
536 sock._websocket._open();
537 });
538
539 it('should only send the send queue up to the send queue length', function () {
540 sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
541 sock._sQlen = 3;
542 const res = sock._encodeMessage();
543 expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
544 });
545
546 it('should properly pass the encoded data off to the actual WebSocket', function () {
547 sock.send([1, 2, 3]);
548 expect(sock._websocket._getSentData()).to.array.equal(new Uint8Array([1, 2, 3]));
549 });
550 });
551 });
552 });