]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.rfb.js
Remove unecessarily nested loop
[mirror_novnc.git] / tests / test.rfb.js
CommitLineData
6940936f 1// requires local modules: util, base64, websock, rfb, keyboard, keysym, keysymdef, input, inflator, des, display
2c9623b5 2// requires test modules: fake.websocket, assertions
b1dee947
SR
3/* jshint expr: true */
4var assert = chai.assert;
5var expect = chai.expect;
6
7function make_rfb (extra_opts) {
8 if (!extra_opts) {
9 extra_opts = {};
10 }
11
12 extra_opts.target = extra_opts.target || document.createElement('canvas');
13 return new RFB(extra_opts);
14}
15
b1dee947
SR
16describe('Remote Frame Buffer Protocol Client', function() {
17 "use strict";
18 before(FakeWebSocket.replace);
19 after(FakeWebSocket.restore);
20
21 describe('Public API Basic Behavior', function () {
22 var client;
23 beforeEach(function () {
24 client = make_rfb();
25 });
26
27 describe('#connect', function () {
28 beforeEach(function () { client._updateState = sinon.spy(); });
29
30 it('should set the current state to "connect"', function () {
31 client.connect('host', 8675);
32 expect(client._updateState).to.have.been.calledOnce;
33 expect(client._updateState).to.have.been.calledWith('connect');
34 });
35
36 it('should fail if we are missing a host', function () {
37 sinon.spy(client, '_fail');
38 client.connect(undefined, 8675);
39 expect(client._fail).to.have.been.calledOnce;
40 });
41
42 it('should fail if we are missing a port', function () {
43 sinon.spy(client, '_fail');
44 client.connect('abc');
45 expect(client._fail).to.have.been.calledOnce;
46 });
47
48 it('should not update the state if we are missing a host or port', function () {
49 sinon.spy(client, '_fail');
50 client.connect('abc');
51 expect(client._fail).to.have.been.calledOnce;
52 expect(client._updateState).to.have.been.calledOnce;
53 expect(client._updateState).to.have.been.calledWith('failed');
54 });
55 });
56
57 describe('#disconnect', function () {
58 beforeEach(function () { client._updateState = sinon.spy(); });
59
60 it('should set the current state to "disconnect"', function () {
61 client.disconnect();
62 expect(client._updateState).to.have.been.calledOnce;
63 expect(client._updateState).to.have.been.calledWith('disconnect');
64 });
155d78b3
JS
65
66 it('should unregister error event handler', function () {
67 sinon.spy(client._sock, 'off');
68 client.disconnect();
69 expect(client._sock.off).to.have.been.calledWith('error');
70 });
71
72 it('should unregister message event handler', function () {
73 sinon.spy(client._sock, 'off');
74 client.disconnect();
75 expect(client._sock.off).to.have.been.calledWith('message');
76 });
77
78 it('should unregister open event handler', function () {
79 sinon.spy(client._sock, 'off');
80 client.disconnect();
81 expect(client._sock.off).to.have.been.calledWith('open');
82 });
b1dee947
SR
83 });
84
85 describe('#sendPassword', function () {
86 beforeEach(function () { this.clock = sinon.useFakeTimers(); });
87 afterEach(function () { this.clock.restore(); });
88
89 it('should set the state to "Authentication"', function () {
90 client._rfb_state = "blah";
91 client.sendPassword('pass');
92 expect(client._rfb_state).to.equal('Authentication');
93 });
94
95 it('should call init_msg "soon"', function () {
96 client._init_msg = sinon.spy();
97 client.sendPassword('pass');
98 this.clock.tick(5);
99 expect(client._init_msg).to.have.been.calledOnce;
100 });
101 });
102
103 describe('#sendCtrlAlDel', function () {
104 beforeEach(function () {
105 client._sock = new Websock();
106 client._sock.open('ws://', 'binary');
107 client._sock._websocket._open();
108 sinon.spy(client._sock, 'send');
109 client._rfb_state = "normal";
110 client._view_only = false;
111 });
112
113 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
114 var expected = [];
115 expected = expected.concat(RFB.messages.keyEvent(0xFFE3, 1));
116 expected = expected.concat(RFB.messages.keyEvent(0xFFE9, 1));
117 expected = expected.concat(RFB.messages.keyEvent(0xFFFF, 1));
118 expected = expected.concat(RFB.messages.keyEvent(0xFFFF, 0));
119 expected = expected.concat(RFB.messages.keyEvent(0xFFE9, 0));
120 expected = expected.concat(RFB.messages.keyEvent(0xFFE3, 0));
121
122 client.sendCtrlAltDel();
123 expect(client._sock).to.have.sent(expected);
124 });
125
126 it('should not send the keys if we are not in a normal state', function () {
127 client._rfb_state = "broken";
128 client.sendCtrlAltDel();
129 expect(client._sock.send).to.not.have.been.called;
130 });
131
132 it('should not send the keys if we are set as view_only', function () {
133 client._view_only = true;
134 client.sendCtrlAltDel();
135 expect(client._sock.send).to.not.have.been.called;
136 });
137 });
138
139 describe('#sendKey', function () {
140 beforeEach(function () {
141 client._sock = new Websock();
142 client._sock.open('ws://', 'binary');
143 client._sock._websocket._open();
144 sinon.spy(client._sock, 'send');
145 client._rfb_state = "normal";
146 client._view_only = false;
147 });
148
149 it('should send a single key with the given code and state (down = true)', function () {
150 var expected = RFB.messages.keyEvent(123, 1);
151 client.sendKey(123, true);
152 expect(client._sock).to.have.sent(expected);
153 });
154
155 it('should send both a down and up event if the state is not specified', function () {
156 var expected = RFB.messages.keyEvent(123, 1);
157 expected = expected.concat(RFB.messages.keyEvent(123, 0));
158 client.sendKey(123);
159 expect(client._sock).to.have.sent(expected);
160 });
161
162 it('should not send the key if we are not in a normal state', function () {
163 client._rfb_state = "broken";
164 client.sendKey(123);
165 expect(client._sock.send).to.not.have.been.called;
166 });
167
168 it('should not send the key if we are set as view_only', function () {
169 client._view_only = true;
170 client.sendKey(123);
171 expect(client._sock.send).to.not.have.been.called;
172 });
173 });
174
175 describe('#clipboardPasteFrom', function () {
176 beforeEach(function () {
177 client._sock = new Websock();
178 client._sock.open('ws://', 'binary');
179 client._sock._websocket._open();
180 sinon.spy(client._sock, 'send');
181 client._rfb_state = "normal";
182 client._view_only = false;
183 });
184
185 it('should send the given text in a paste event', function () {
186 var expected = RFB.messages.clientCutText('abc');
187 client.clipboardPasteFrom('abc');
188 expect(client._sock).to.have.sent(expected);
189 });
190
191 it('should not send the text if we are not in a normal state', function () {
192 client._rfb_state = "broken";
193 client.clipboardPasteFrom('abc');
194 expect(client._sock.send).to.not.have.been.called;
195 });
196 });
197
4dec490a 198 describe("#setDesktopSize", function () {
199 beforeEach(function() {
200 client._sock = new Websock();
201 client._sock.open('ws://', 'binary');
202 client._sock._websocket._open();
203 sinon.spy(client._sock, 'send');
204 client._rfb_state = "normal";
205 client._view_only = false;
206 client._supportsSetDesktopSize = true;
207 });
208
209 it('should send the request with the given width and height', function () {
210 var expected = [251];
211 expected.push8(0); // padding
212 expected.push16(1); // width
213 expected.push16(2); // height
214 expected.push8(1); // number-of-screens
215 expected.push8(0); // padding before screen array
216 expected.push32(0); // id
217 expected.push16(0); // x-position
218 expected.push16(0); // y-position
219 expected.push16(1); // width
220 expected.push16(2); // height
221 expected.push32(0); // flags
222
223 client.setDesktopSize(1, 2);
224 expect(client._sock).to.have.sent(expected);
225 });
226
227 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
228 client._supportsSetDesktopSize = false;
229 client.setDesktopSize(1,2);
230 expect(client._sock.send).to.not.have.been.called;
231 });
232
233 it('should not send the request if we are not in a normal state', function () {
234 client._rfb_state = "broken";
235 client.setDesktopSize(1,2);
236 expect(client._sock.send).to.not.have.been.called;
237 });
238 });
239
b1dee947
SR
240 describe("XVP operations", function () {
241 beforeEach(function () {
242 client._sock = new Websock();
243 client._sock.open('ws://', 'binary');
244 client._sock._websocket._open();
245 sinon.spy(client._sock, 'send');
246 client._rfb_state = "normal";
247 client._view_only = false;
248 client._rfb_xvp_ver = 1;
249 });
250
251 it('should send the shutdown signal on #xvpShutdown', function () {
252 client.xvpShutdown();
253 expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x02]);
254 });
255
256 it('should send the reboot signal on #xvpReboot', function () {
257 client.xvpReboot();
258 expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x03]);
259 });
260
261 it('should send the reset signal on #xvpReset', function () {
262 client.xvpReset();
263 expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x04]);
264 });
265
266 it('should support sending arbitrary XVP operations via #xvpOp', function () {
267 client.xvpOp(1, 7);
268 expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x07]);
269 });
270
271 it('should not send XVP operations with higher versions than we support', function () {
272 expect(client.xvpOp(2, 7)).to.be.false;
273 expect(client._sock.send).to.not.have.been.called;
274 });
275 });
276 });
277
278 describe('Misc Internals', function () {
279 describe('#_updateState', function () {
280 var client;
281 beforeEach(function () {
282 this.clock = sinon.useFakeTimers();
283 client = make_rfb();
284 });
285
286 afterEach(function () {
287 this.clock.restore();
288 });
289
290 it('should clear the disconnect timer if the state is not disconnect', function () {
291 var spy = sinon.spy();
292 client._disconnTimer = setTimeout(spy, 50);
293 client._updateState('normal');
294 this.clock.tick(51);
295 expect(spy).to.not.have.been.called;
296 expect(client._disconnTimer).to.be.null;
297 });
298 });
299 });
300
301 describe('Page States', function () {
302 describe('loaded', function () {
303 var client;
304 beforeEach(function () { client = make_rfb(); });
305
306 it('should close any open WebSocket connection', function () {
307 sinon.spy(client._sock, 'close');
308 client._updateState('loaded');
309 expect(client._sock.close).to.have.been.calledOnce;
310 });
311 });
312
313 describe('disconnected', function () {
314 var client;
315 beforeEach(function () { client = make_rfb(); });
316
317 it('should close any open WebSocket connection', function () {
318 sinon.spy(client._sock, 'close');
319 client._updateState('disconnected');
320 expect(client._sock.close).to.have.been.calledOnce;
321 });
322 });
323
324 describe('connect', function () {
325 var client;
326 beforeEach(function () { client = make_rfb(); });
327
328 it('should reset the variable states', function () {
329 sinon.spy(client, '_init_vars');
330 client._updateState('connect');
331 expect(client._init_vars).to.have.been.calledOnce;
332 });
333
334 it('should actually connect to the websocket', function () {
335 sinon.spy(client._sock, 'open');
336 client._updateState('connect');
337 expect(client._sock.open).to.have.been.calledOnce;
338 });
339
340 it('should use wss:// to connect if encryption is enabled', function () {
341 sinon.spy(client._sock, 'open');
342 client.set_encrypt(true);
343 client._updateState('connect');
344 expect(client._sock.open.args[0][0]).to.contain('wss://');
345 });
346
347 it('should use ws:// to connect if encryption is not enabled', function () {
348 sinon.spy(client._sock, 'open');
349 client.set_encrypt(true);
350 client._updateState('connect');
351 expect(client._sock.open.args[0][0]).to.contain('wss://');
352 });
353
354 it('should use a uri with the host, port, and path specified to connect', function () {
355 sinon.spy(client._sock, 'open');
356 client.set_encrypt(false);
357 client._rfb_host = 'HOST';
358 client._rfb_port = 8675;
359 client._rfb_path = 'PATH';
360 client._updateState('connect');
361 expect(client._sock.open).to.have.been.calledWith('ws://HOST:8675/PATH');
362 });
363
364 it('should attempt to close the websocket before we open an new one', function () {
365 sinon.spy(client._sock, 'close');
366 client._updateState('connect');
367 expect(client._sock.close).to.have.been.calledOnce;
368 });
369 });
370
371 describe('disconnect', function () {
372 var client;
373 beforeEach(function () {
374 this.clock = sinon.useFakeTimers();
375 client = make_rfb();
376 client.connect('host', 8675);
377 });
378
379 afterEach(function () {
380 this.clock.restore();
381 });
382
383 it('should fail if we do not call Websock.onclose within the disconnection timeout', function () {
384 client._sock._websocket.close = function () {}; // explicitly don't call onclose
385 client._updateState('disconnect');
386 this.clock.tick(client.get_disconnectTimeout() * 1000);
387 expect(client._rfb_state).to.equal('failed');
388 });
389
390 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
391 client._updateState('disconnect');
392 this.clock.tick(client.get_disconnectTimeout() * 500);
393 client._sock._websocket.close();
394 this.clock.tick(client.get_disconnectTimeout() * 500 + 1);
395 expect(client._rfb_state).to.equal('disconnected');
396 });
397
398 it('should close the WebSocket connection', function () {
399 sinon.spy(client._sock, 'close');
400 client._updateState('disconnect');
401 expect(client._sock.close).to.have.been.calledTwice; // once on loaded, once on disconnect
402 });
403 });
404
405 describe('failed', function () {
406 var client;
407 beforeEach(function () {
408 this.clock = sinon.useFakeTimers();
409 client = make_rfb();
410 client.connect('host', 8675);
411 });
412
413 afterEach(function () {
414 this.clock.restore();
415 });
416
417 it('should close the WebSocket connection', function () {
418 sinon.spy(client._sock, 'close');
419 client._updateState('failed');
420 expect(client._sock.close).to.have.been.called;
421 });
422
423 it('should transition to disconnected but stay in failed state', function () {
424 client.set_onUpdateState(sinon.spy());
425 client._updateState('failed');
426 this.clock.tick(50);
427 expect(client._rfb_state).to.equal('failed');
428
429 var onUpdateState = client.get_onUpdateState();
430 expect(onUpdateState).to.have.been.called;
431 // it should be specifically the last call
432 expect(onUpdateState.args[onUpdateState.args.length - 1][1]).to.equal('disconnected');
433 expect(onUpdateState.args[onUpdateState.args.length - 1][2]).to.equal('failed');
434 });
435
436 });
437
438 describe('fatal', function () {
439 var client;
440 beforeEach(function () { client = make_rfb(); });
441
442 it('should close any open WebSocket connection', function () {
443 sinon.spy(client._sock, 'close');
444 client._updateState('fatal');
445 expect(client._sock.close).to.have.been.calledOnce;
446 });
447 });
448
449 // NB(directxman12): Normal does *nothing* in updateState
450 });
451
452 describe('Protocol Initialization States', function () {
453 describe('ProtocolVersion', function () {
454 beforeEach(function () {
455 this.clock = sinon.useFakeTimers();
456 });
457
458 afterEach(function () {
459 this.clock.restore();
460 });
461
462 function send_ver (ver, client) {
463 var arr = new Uint8Array(12);
464 for (var i = 0; i < ver.length; i++) {
465 arr[i+4] = ver.charCodeAt(i);
466 }
467 arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' ';
468 arr[11] = '\n';
469 client._sock._websocket._receive_data(arr);
470 }
471
472 describe('version parsing', function () {
473 var client;
474 beforeEach(function () {
475 client = make_rfb();
476 client.connect('host', 8675);
477 client._sock._websocket._open();
478 });
479
480 it('should interpret version 000.000 as a repeater', function () {
481 client._repeaterID = '\x01\x02\x03\x04\x05';
482 send_ver('000.000', client);
483 expect(client._rfb_version).to.equal(0);
484
485 var sent_data = client._sock._websocket._get_sent_data();
486 expect(sent_data.slice(0, 5)).to.deep.equal([1, 2, 3, 4, 5]);
487 });
488
489 it('should interpret version 003.003 as version 3.3', function () {
490 send_ver('003.003', client);
491 expect(client._rfb_version).to.equal(3.3);
492 });
493
494 it('should interpret version 003.006 as version 3.3', function () {
495 send_ver('003.006', client);
496 expect(client._rfb_version).to.equal(3.3);
497 });
498
499 it('should interpret version 003.889 as version 3.3', function () {
500 send_ver('003.889', client);
501 expect(client._rfb_version).to.equal(3.3);
502 });
503
504 it('should interpret version 003.007 as version 3.7', function () {
505 send_ver('003.007', client);
506 expect(client._rfb_version).to.equal(3.7);
507 });
508
509 it('should interpret version 003.008 as version 3.8', function () {
510 send_ver('003.008', client);
511 expect(client._rfb_version).to.equal(3.8);
512 });
513
514 it('should interpret version 004.000 as version 3.8', function () {
515 send_ver('004.000', client);
516 expect(client._rfb_version).to.equal(3.8);
517 });
518
519 it('should interpret version 004.001 as version 3.8', function () {
520 send_ver('004.001', client);
521 expect(client._rfb_version).to.equal(3.8);
522 });
523
524 it('should fail on an invalid version', function () {
525 send_ver('002.000', client);
526 expect(client._rfb_state).to.equal('failed');
527 });
528 });
529
530 var client;
531 beforeEach(function () {
532 client = make_rfb();
533 client.connect('host', 8675);
534 client._sock._websocket._open();
535 });
536
537 it('should handle two step repeater negotiation', function () {
538 client._repeaterID = '\x01\x02\x03\x04\x05';
539
540 send_ver('000.000', client);
541 expect(client._rfb_version).to.equal(0);
542 var sent_data = client._sock._websocket._get_sent_data();
543 expect(sent_data.slice(0, 5)).to.deep.equal([1, 2, 3, 4, 5]);
544 expect(sent_data).to.have.length(250);
545
546 send_ver('003.008', client);
547 expect(client._rfb_version).to.equal(3.8);
548 });
549
550 it('should initialize the flush interval', function () {
551 client._sock.flush = sinon.spy();
552 send_ver('003.008', client);
553 this.clock.tick(100);
554 expect(client._sock.flush).to.have.been.calledThrice;
555 });
556
557 it('should send back the interpreted version', function () {
558 send_ver('004.000', client);
559
560 var expected_str = 'RFB 003.008\n';
561 var expected = [];
562 for (var i = 0; i < expected_str.length; i++) {
563 expected[i] = expected_str.charCodeAt(i);
564 }
565
566 expect(client._sock).to.have.sent(expected);
567 });
568
569 it('should transition to the Security state on successful negotiation', function () {
570 send_ver('003.008', client);
571 expect(client._rfb_state).to.equal('Security');
572 });
573 });
574
575 describe('Security', function () {
576 var client;
577
578 beforeEach(function () {
579 client = make_rfb();
580 client.connect('host', 8675);
581 client._sock._websocket._open();
582 client._rfb_state = 'Security';
583 });
584
585 it('should simply receive the auth scheme when for versions < 3.7', function () {
586 client._rfb_version = 3.6;
587 var auth_scheme_raw = [1, 2, 3, 4];
588 var auth_scheme = (auth_scheme_raw[0] << 24) + (auth_scheme_raw[1] << 16) +
589 (auth_scheme_raw[2] << 8) + auth_scheme_raw[3];
590 client._sock._websocket._receive_data(auth_scheme_raw);
591 expect(client._rfb_auth_scheme).to.equal(auth_scheme);
592 });
593
594 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
595 client._rfb_version = 3.7;
596 var auth_schemes = [2, 1, 2];
597 client._sock._websocket._receive_data(auth_schemes);
598 expect(client._rfb_auth_scheme).to.equal(2);
599 expect(client._sock).to.have.sent([2]);
600 });
601
602 it('should fail if there are no supported schemes for versions >= 3.7', function () {
603 client._rfb_version = 3.7;
604 var auth_schemes = [1, 32];
605 client._sock._websocket._receive_data(auth_schemes);
606 expect(client._rfb_state).to.equal('failed');
607 });
608
609 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
610 client._rfb_version = 3.7;
611 var failure_data = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
612 sinon.spy(client, '_fail');
613 client._sock._websocket._receive_data(failure_data);
614
615 expect(client._fail).to.have.been.calledTwice;
616 expect(client._fail).to.have.been.calledWith('Security failure: whoops');
617 });
618
619 it('should transition to the Authentication state and continue on successful negotiation', function () {
620 client._rfb_version = 3.7;
621 var auth_schemes = [1, 1];
622 client._negotiate_authentication = sinon.spy();
623 client._sock._websocket._receive_data(auth_schemes);
624 expect(client._rfb_state).to.equal('Authentication');
625 expect(client._negotiate_authentication).to.have.been.calledOnce;
626 });
627 });
628
629 describe('Authentication', function () {
630 var client;
631
632 beforeEach(function () {
633 client = make_rfb();
634 client.connect('host', 8675);
635 client._sock._websocket._open();
636 client._rfb_state = 'Security';
637 });
638
639 function send_security(type, cl) {
640 cl._sock._websocket._receive_data(new Uint8Array([1, type]));
641 }
642
643 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
644 client._rfb_version = 3.6;
645 var err_msg = "Whoopsies";
646 var data = [0, 0, 0, 0];
647 var err_len = err_msg.length;
648 data.push32(err_len);
649 for (var i = 0; i < err_len; i++) {
650 data.push(err_msg.charCodeAt(i));
651 }
652
653 sinon.spy(client, '_fail');
654 client._sock._websocket._receive_data(new Uint8Array(data));
655 expect(client._rfb_state).to.equal('failed');
656 expect(client._fail).to.have.been.calledWith('Auth failure: Whoopsies');
657 });
658
659 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
660 client._rfb_version = 3.8;
661 send_security(1, client);
662 expect(client._rfb_state).to.equal('SecurityResult');
663 });
664
665 it('should transition straight to ClientInitialisation on "no auth" for versions < 3.8', function () {
666 client._rfb_version = 3.7;
667 sinon.spy(client, '_updateState');
668 send_security(1, client);
669 expect(client._updateState).to.have.been.calledWith('ClientInitialisation');
670 expect(client._rfb_state).to.equal('ServerInitialisation');
671 });
672
673 it('should fail on an unknown auth scheme', function () {
674 client._rfb_version = 3.8;
675 send_security(57, client);
676 expect(client._rfb_state).to.equal('failed');
677 });
678
679 describe('VNC Authentication (type 2) Handler', function () {
680 var client;
681
682 beforeEach(function () {
683 client = make_rfb();
684 client.connect('host', 8675);
685 client._sock._websocket._open();
686 client._rfb_state = 'Security';
687 client._rfb_version = 3.8;
688 });
689
690 it('should transition to the "password" state if missing a password', function () {
691 send_security(2, client);
692 expect(client._rfb_state).to.equal('password');
693 });
694
695 it('should encrypt the password with DES and then send it back', function () {
696 client._rfb_password = 'passwd';
697 send_security(2, client);
698 client._sock._websocket._get_sent_data(); // skip the choice of auth reply
699
700 var challenge = [];
701 for (var i = 0; i < 16; i++) { challenge[i] = i; }
702 client._sock._websocket._receive_data(new Uint8Array(challenge));
703
704 var des_pass = RFB.genDES('passwd', challenge);
705 expect(client._sock).to.have.sent(des_pass);
706 });
707
708 it('should transition to SecurityResult immediately after sending the password', function () {
709 client._rfb_password = 'passwd';
710 send_security(2, client);
711
712 var challenge = [];
713 for (var i = 0; i < 16; i++) { challenge[i] = i; }
714 client._sock._websocket._receive_data(new Uint8Array(challenge));
715
716 expect(client._rfb_state).to.equal('SecurityResult');
717 });
718 });
719
720 describe('XVP Authentication (type 22) Handler', function () {
721 var client;
722
723 beforeEach(function () {
724 client = make_rfb();
725 client.connect('host', 8675);
726 client._sock._websocket._open();
727 client._rfb_state = 'Security';
728 client._rfb_version = 3.8;
729 });
730
731 it('should fall through to standard VNC authentication upon completion', function () {
732 client.set_xvp_password_sep('#');
733 client._rfb_password = 'user#target#password';
734 client._negotiate_std_vnc_auth = sinon.spy();
735 send_security(22, client);
736 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
737 });
738
739 it('should transition to the "password" state if the passwords is missing', function() {
740 send_security(22, client);
741 expect(client._rfb_state).to.equal('password');
742 });
743
744 it('should transition to the "password" state if the passwords is improperly formatted', function() {
745 client._rfb_password = 'user@target';
746 send_security(22, client);
747 expect(client._rfb_state).to.equal('password');
748 });
749
750 it('should split the password, send the first two parts, and pass on the last part', function () {
751 client.set_xvp_password_sep('#');
752 client._rfb_password = 'user#target#password';
753 client._negotiate_std_vnc_auth = sinon.spy();
754
755 send_security(22, client);
756
757 expect(client._rfb_password).to.equal('password');
758
759 var expected = [22, 4, 6]; // auth selection, len user, len target
760 for (var i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); }
761
762 expect(client._sock).to.have.sent(expected);
763 });
764 });
765
766 describe('TightVNC Authentication (type 16) Handler', function () {
767 var client;
768
769 beforeEach(function () {
770 client = make_rfb();
771 client.connect('host', 8675);
772 client._sock._websocket._open();
773 client._rfb_state = 'Security';
774 client._rfb_version = 3.8;
775 send_security(16, client);
776 client._sock._websocket._get_sent_data(); // skip the security reply
777 });
778
779 function send_num_str_pairs(pairs, client) {
780 var pairs_len = pairs.length;
781 var data = [];
782 data.push32(pairs_len);
783
784 for (var i = 0; i < pairs_len; i++) {
785 data.push32(pairs[i][0]);
786 var j;
787 for (j = 0; j < 4; j++) {
788 data.push(pairs[i][1].charCodeAt(j));
789 }
790 for (j = 0; j < 8; j++) {
791 data.push(pairs[i][2].charCodeAt(j));
792 }
793 }
794
795 client._sock._websocket._receive_data(new Uint8Array(data));
796 }
797
798 it('should skip tunnel negotiation if no tunnels are requested', function () {
799 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
800 expect(client._rfb_tightvnc).to.be.true;
801 });
802
803 it('should fail if no supported tunnels are listed', function () {
804 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client);
805 expect(client._rfb_state).to.equal('failed');
806 });
807
808 it('should choose the notunnel tunnel type', function () {
809 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client);
810 expect(client._sock).to.have.sent([0, 0, 0, 0]);
811 });
812
813 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
814 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client);
815 client._sock._websocket._get_sent_data(); // skip the tunnel choice here
816 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
817 expect(client._sock).to.have.sent([0, 0, 0, 1]);
818 expect(client._rfb_state).to.equal('SecurityResult');
819 });
820
821 /*it('should attempt to use VNC auth over no auth when possible', function () {
822 client._rfb_tightvnc = true;
823 client._negotiate_std_vnc_auth = sinon.spy();
824 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
825 expect(client._sock).to.have.sent([0, 0, 0, 1]);
826 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
827 expect(client._rfb_auth_scheme).to.equal(2);
828 });*/ // while this would make sense, the original code doesn't actually do this
829
830 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
831 client._rfb_tightvnc = true;
832 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
833 expect(client._sock).to.have.sent([0, 0, 0, 1]);
834 expect(client._rfb_state).to.equal('SecurityResult');
835 });
836
837 it('should accept VNC authentication and transition to that', function () {
838 client._rfb_tightvnc = true;
839 client._negotiate_std_vnc_auth = sinon.spy();
840 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client);
841 expect(client._sock).to.have.sent([0, 0, 0, 2]);
842 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
843 expect(client._rfb_auth_scheme).to.equal(2);
844 });
845
846 it('should fail if there are no supported auth types', function () {
847 client._rfb_tightvnc = true;
848 send_num_str_pairs([[23, 'stdv', 'badval__']], client);
849 expect(client._rfb_state).to.equal('failed');
850 });
851 });
852 });
853
854 describe('SecurityResult', function () {
855 var client;
856
857 beforeEach(function () {
858 client = make_rfb();
859 client.connect('host', 8675);
860 client._sock._websocket._open();
861 client._rfb_state = 'SecurityResult';
862 });
863
864 it('should fall through to ClientInitialisation on a response code of 0', function () {
865 client._updateState = sinon.spy();
866 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
867 expect(client._updateState).to.have.been.calledOnce;
868 expect(client._updateState).to.have.been.calledWith('ClientInitialisation');
869 });
870
871 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
872 client._rfb_version = 3.8;
873 sinon.spy(client, '_fail');
874 var failure_data = [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
875 client._sock._websocket._receive_data(new Uint8Array(failure_data));
876 expect(client._rfb_state).to.equal('failed');
877 expect(client._fail).to.have.been.calledWith('whoops');
878 });
879
880 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
881 client._rfb_version = 3.7;
882 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 1]));
883 expect(client._rfb_state).to.equal('failed');
884 });
885 });
886
887 describe('ClientInitialisation', function () {
888 var client;
889
890 beforeEach(function () {
891 client = make_rfb();
892 client.connect('host', 8675);
893 client._sock._websocket._open();
894 client._rfb_state = 'SecurityResult';
895 });
896
897 it('should transition to the ServerInitialisation state', function () {
898 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
899 expect(client._rfb_state).to.equal('ServerInitialisation');
900 });
901
902 it('should send 1 if we are in shared mode', function () {
903 client.set_shared(true);
904 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
905 expect(client._sock).to.have.sent([1]);
906 });
907
908 it('should send 0 if we are not in shared mode', function () {
909 client.set_shared(false);
910 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
911 expect(client._sock).to.have.sent([0]);
912 });
913 });
914
915 describe('ServerInitialisation', function () {
916 var client;
917
918 beforeEach(function () {
919 client = make_rfb();
920 client.connect('host', 8675);
921 client._sock._websocket._open();
922 client._rfb_state = 'ServerInitialisation';
923 });
924
925 function send_server_init(opts, client) {
926 var full_opts = { width: 10, height: 12, bpp: 24, depth: 24, big_endian: 0,
927 true_color: 1, red_max: 255, green_max: 255, blue_max: 255,
928 red_shift: 16, green_shift: 8, blue_shift: 0, name: 'a name' };
929 for (var opt in opts) {
930 full_opts[opt] = opts[opt];
931 }
932 var data = [];
933
934 data.push16(full_opts.width);
935 data.push16(full_opts.height);
936
937 data.push(full_opts.bpp);
938 data.push(full_opts.depth);
939 data.push(full_opts.big_endian);
940 data.push(full_opts.true_color);
941
942 data.push16(full_opts.red_max);
943 data.push16(full_opts.green_max);
944 data.push16(full_opts.blue_max);
945 data.push8(full_opts.red_shift);
946 data.push8(full_opts.green_shift);
947 data.push8(full_opts.blue_shift);
948
949 // padding
950 data.push8(0);
951 data.push8(0);
952 data.push8(0);
953
954 client._sock._websocket._receive_data(new Uint8Array(data));
955
956 var name_data = [];
957 name_data.push32(full_opts.name.length);
958 for (var i = 0; i < full_opts.name.length; i++) {
959 name_data.push(full_opts.name.charCodeAt(i));
960 }
961 client._sock._websocket._receive_data(new Uint8Array(name_data));
962 }
963
964 it('should set the framebuffer width and height', function () {
965 send_server_init({ width: 32, height: 84 }, client);
966 expect(client._fb_width).to.equal(32);
967 expect(client._fb_height).to.equal(84);
968 });
969
970 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
971
972 it('should set the framebuffer name and call the callback', function () {
973 client.set_onDesktopName(sinon.spy());
974 send_server_init({ name: 'some name' }, client);
975
976 var spy = client.get_onDesktopName();
977 expect(client._fb_name).to.equal('some name');
978 expect(spy).to.have.been.calledOnce;
979 expect(spy.args[0][1]).to.equal('some name');
980 });
981
982 it('should handle the extended init message of the tight encoding', function () {
983 // NB(sross): we don't actually do anything with it, so just test that we can
984 // read it w/o throwing an error
985 client._rfb_tightvnc = true;
986 send_server_init({}, client);
987
988 var tight_data = [];
989 tight_data.push16(1);
990 tight_data.push16(2);
991 tight_data.push16(3);
992 tight_data.push16(0);
993 for (var i = 0; i < 16 + 32 + 48; i++) {
994 tight_data.push(i);
995 }
996 client._sock._websocket._receive_data(tight_data);
997
998 expect(client._rfb_state).to.equal('normal');
999 });
1000
1001 it('should set the true color mode on the display to the configuration variable', function () {
1002 client.set_true_color(false);
1003 sinon.spy(client._display, 'set_true_color');
1004 send_server_init({ true_color: 1 }, client);
1005 expect(client._display.set_true_color).to.have.been.calledOnce;
1006 expect(client._display.set_true_color).to.have.been.calledWith(false);
1007 });
1008
1009 it('should call the resize callback and resize the display', function () {
1010 client.set_onFBResize(sinon.spy());
1011 sinon.spy(client._display, 'resize');
1012 send_server_init({ width: 27, height: 32 }, client);
1013
1014 var spy = client.get_onFBResize();
1015 expect(client._display.resize).to.have.been.calledOnce;
1016 expect(client._display.resize).to.have.been.calledWith(27, 32);
1017 expect(spy).to.have.been.calledOnce;
1018 expect(spy.args[0][1]).to.equal(27);
1019 expect(spy.args[0][2]).to.equal(32);
1020 });
1021
1022 it('should grab the mouse and keyboard', function () {
1023 sinon.spy(client._keyboard, 'grab');
1024 sinon.spy(client._mouse, 'grab');
1025 send_server_init({}, client);
1026 expect(client._keyboard.grab).to.have.been.calledOnce;
1027 expect(client._mouse.grab).to.have.been.calledOnce;
1028 });
1029
1030 it('should set the BPP and depth to 4 and 3 respectively if in true color mode', function () {
1031 client.set_true_color(true);
1032 send_server_init({}, client);
1033 expect(client._fb_Bpp).to.equal(4);
1034 expect(client._fb_depth).to.equal(3);
1035 });
1036
1037 it('should set the BPP and depth to 1 and 1 respectively if not in true color mode', function () {
1038 client.set_true_color(false);
1039 send_server_init({}, client);
1040 expect(client._fb_Bpp).to.equal(1);
1041 expect(client._fb_depth).to.equal(1);
1042 });
1043
1044 // TODO(directxman12): test the various options in this configuration matrix
1045 it('should reply with the pixel format, client encodings, and initial update request', function () {
1046 client.set_true_color(true);
1047 client.set_local_cursor(false);
1048 var expected = RFB.messages.pixelFormat(4, 3, true);
1049 expected = expected.concat(RFB.messages.clientEncodings(client._encodings, false, true));
1050 var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
1051 dirtyBoxes: [ { x: 0, y: 0, w: 27, h: 32 } ] };
1052 expected = expected.concat(RFB.messages.fbUpdateRequests(expected_cdr, 27, 32));
1053
1054 send_server_init({ width: 27, height: 32 }, client);
1055 expect(client._sock).to.have.sent(expected);
1056 });
1057
1058 it('should check for sending mouse events', function () {
1059 // be lazy with our checking so we don't have to check through the whole sent buffer
1060 sinon.spy(client, '_checkEvents');
1061 send_server_init({}, client);
1062 expect(client._checkEvents).to.have.been.calledOnce;
1063 });
1064
1065 it('should transition to the "normal" state', function () {
1066 send_server_init({}, client);
1067 expect(client._rfb_state).to.equal('normal');
1068 });
1069 });
1070 });
1071
1072 describe('Protocol Message Processing After Completing Initialization', function () {
1073 var client;
1074
1075 beforeEach(function () {
1076 client = make_rfb();
1077 client.connect('host', 8675);
1078 client._sock._websocket._open();
1079 client._rfb_state = 'normal';
1080 client._fb_name = 'some device';
1081 client._fb_width = 640;
1082 client._fb_height = 20;
1083 });
1084
1085 describe('Framebuffer Update Handling', function () {
1086 var client;
1087
1088 beforeEach(function () {
1089 client = make_rfb();
1090 client.connect('host', 8675);
1091 client._sock._websocket._open();
1092 client._rfb_state = 'normal';
1093 client._fb_name = 'some device';
1094 client._fb_width = 640;
1095 client._fb_height = 20;
1096 });
1097
1098 var target_data_arr = [
1099 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1100 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1101 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1102 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1103 ];
1104 var target_data;
1105
1106 var target_data_check_arr = [
1107 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1108 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1109 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1110 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1111 ];
1112 var target_data_check;
1113
1114 before(function () {
1115 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1116 target_data = new Uint8Array(target_data_arr);
1117 target_data_check = new Uint8Array(target_data_check_arr);
1118 });
1119
1120 function send_fbu_msg (rect_info, rect_data, client, rect_cnt) {
1121 var data = [];
1122
1123 if (!rect_cnt || rect_cnt > -1) {
1124 // header
1125 data.push(0); // msg type
1126 data.push(0); // padding
1127 data.push16(rect_cnt || rect_data.length);
1128 }
1129
1130 for (var i = 0; i < rect_data.length; i++) {
1131 if (rect_info[i]) {
1132 data.push16(rect_info[i].x);
1133 data.push16(rect_info[i].y);
1134 data.push16(rect_info[i].width);
1135 data.push16(rect_info[i].height);
1136 data.push32(rect_info[i].encoding);
1137 }
1138 data = data.concat(rect_data[i]);
1139 }
1140
1141 client._sock._websocket._receive_data(new Uint8Array(data));
1142 }
1143
1144 it('should send an update request if there is sufficient data', function () {
1145 var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
fda40d89
SR
1146 dirtyBoxes: [ { x: 0, y: 0, w: 240, h: 20 } ] };
1147 var expected_msg = RFB.messages.fbUpdateRequests(expected_cdr, 240, 20);
b1dee947
SR
1148
1149 client._framebufferUpdate = function () { return true; };
1150 client._sock._websocket._receive_data(new Uint8Array([0]));
1151
1152 expect(client._sock).to.have.sent(expected_msg);
1153 });
1154
1155 it('should not send an update request if we need more data', function () {
1156 client._sock._websocket._receive_data(new Uint8Array([0]));
1157 expect(client._sock._websocket._get_sent_data()).to.have.length(0);
1158 });
1159
1160 it('should resume receiving an update if we previously did not have enough data', function () {
1161 var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
fda40d89
SR
1162 dirtyBoxes: [ { x: 0, y: 0, w: 240, h: 20 } ] };
1163 var expected_msg = RFB.messages.fbUpdateRequests(expected_cdr, 240, 20);
b1dee947
SR
1164
1165 // just enough to set FBU.rects
1166 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 3]));
1167 expect(client._sock._websocket._get_sent_data()).to.have.length(0);
1168
1169 client._framebufferUpdate = function () { return true; }; // we magically have enough data
1170 // 247 should *not* be used as the message type here
1171 client._sock._websocket._receive_data(new Uint8Array([247]));
1172 expect(client._sock).to.have.sent(expected_msg);
1173 });
1174
1175 it('should parse out information from a header before any actual data comes in', function () {
1176 client.set_onFBUReceive(sinon.spy());
1177 var rect_info = { x: 8, y: 11, width: 27, height: 32, encoding: 0x02, encodingName: 'RRE' };
1178 send_fbu_msg([rect_info], [[]], client);
1179
1180 var spy = client.get_onFBUReceive();
1181 expect(spy).to.have.been.calledOnce;
1182 expect(spy).to.have.been.calledWith(sinon.match.any, rect_info);
1183 });
1184
1185 it('should fire onFBUComplete when the update is complete', function () {
1186 client.set_onFBUComplete(sinon.spy());
1187 var rect_info = { x: 8, y: 11, width: 27, height: 32, encoding: -224, encodingName: 'last_rect' };
1188 send_fbu_msg([rect_info], [[]], client); // last_rect
1189
1190 var spy = client.get_onFBUComplete();
1191 expect(spy).to.have.been.calledOnce;
1192 expect(spy).to.have.been.calledWith(sinon.match.any, rect_info);
1193 });
1194
1195 it('should not fire onFBUComplete if we have not finished processing the update', function () {
1196 client.set_onFBUComplete(sinon.spy());
1197 var rect_info = { x: 8, y: 11, width: 27, height: 32, encoding: 0x00, encodingName: 'RAW' };
1198 send_fbu_msg([rect_info], [[]], client);
1199 expect(client.get_onFBUComplete()).to.not.have.been.called;
1200 });
1201
1202 it('should call the appropriate encoding handler', function () {
1203 client._encHandlers[0x02] = sinon.spy();
1204 var rect_info = { x: 8, y: 11, width: 27, height: 32, encoding: 0x02 };
1205 send_fbu_msg([rect_info], [[]], client);
1206 expect(client._encHandlers[0x02]).to.have.been.calledOnce;
1207 });
1208
1209 it('should fail on an unsupported encoding', function () {
1210 client.set_onFBUReceive(sinon.spy());
1211 var rect_info = { x: 8, y: 11, width: 27, height: 32, encoding: 234 };
1212 send_fbu_msg([rect_info], [[]], client);
1213 expect(client._rfb_state).to.equal('failed');
1214 });
1215
1216 it('should be able to pause and resume receiving rects if not enought data', function () {
1217 // seed some initial data to copy
1218 client._fb_width = 4;
1219 client._fb_height = 4;
1220 client._display.resize(4, 4);
1221 var initial_data = client._display._drawCtx.createImageData(4, 2);
1222 var initial_data_arr = target_data_check_arr.slice(0, 32);
1223 for (var i = 0; i < 32; i++) { initial_data.data[i] = initial_data_arr[i]; }
1224 client._display._drawCtx.putImageData(initial_data, 0, 0);
1225
1226 var info = [{ x: 0, y: 2, width: 2, height: 2, encoding: 0x01},
1227 { x: 2, y: 2, width: 2, height: 2, encoding: 0x01}];
1228 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1229 var rects = [[0, 2, 0, 0], [0, 0, 0, 0]];
1230 send_fbu_msg([info[0]], [rects[0]], client, 2);
1231 send_fbu_msg([info[1]], [rects[1]], client, -1);
1232 expect(client._display).to.have.displayed(target_data_check);
1233 });
1234
1235 describe('Message Encoding Handlers', function () {
1236 var client;
1237
1238 beforeEach(function () {
1239 client = make_rfb();
1240 client.connect('host', 8675);
1241 client._sock._websocket._open();
1242 client._rfb_state = 'normal';
1243 client._fb_name = 'some device';
1244 // a really small frame
1245 client._fb_width = 4;
1246 client._fb_height = 4;
1247 client._display._fb_width = 4;
1248 client._display._fb_height = 4;
2c9623b5
SR
1249 client._display._viewportLoc.w = 4;
1250 client._display._viewportLoc.h = 4;
b1dee947
SR
1251 client._fb_Bpp = 4;
1252 });
1253
1254 it('should handle the RAW encoding', function () {
1255 var info = [{ x: 0, y: 0, width: 2, height: 2, encoding: 0x00 },
1256 { x: 2, y: 0, width: 2, height: 2, encoding: 0x00 },
1257 { x: 0, y: 2, width: 4, height: 1, encoding: 0x00 },
1258 { x: 0, y: 3, width: 4, height: 1, encoding: 0x00 }];
1259 // data is in bgrx
1260 var rects = [
1261 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1262 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1263 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1264 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1265 send_fbu_msg(info, rects, client);
1266 expect(client._display).to.have.displayed(target_data);
1267 });
1268
1269 it('should handle the COPYRECT encoding', function () {
1270 // seed some initial data to copy
1271 var initial_data = client._display._drawCtx.createImageData(4, 2);
1272 var initial_data_arr = target_data_check_arr.slice(0, 32);
1273 for (var i = 0; i < 32; i++) { initial_data.data[i] = initial_data_arr[i]; }
1274 client._display._drawCtx.putImageData(initial_data, 0, 0);
1275
1276 var info = [{ x: 0, y: 2, width: 2, height: 2, encoding: 0x01},
1277 { x: 2, y: 2, width: 2, height: 2, encoding: 0x01}];
1278 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1279 var rects = [[0, 2, 0, 0], [0, 0, 0, 0]];
1280 send_fbu_msg(info, rects, client);
1281 expect(client._display).to.have.displayed(target_data_check);
1282 });
1283
1284 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1285 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1286
1287 it('should handle the RRE encoding', function () {
1288 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x02 }];
1289 var rect = [];
1290 rect.push32(2); // 2 subrects
1291 rect.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1292 rect.push(0xff); // becomes ff0000ff --> #0000FF color
1293 rect.push(0x00);
1294 rect.push(0x00);
1295 rect.push(0xff);
1296 rect.push16(0); // x: 0
1297 rect.push16(0); // y: 0
1298 rect.push16(2); // width: 2
1299 rect.push16(2); // height: 2
1300 rect.push(0xff); // becomes ff0000ff --> #0000FF color
1301 rect.push(0x00);
1302 rect.push(0x00);
1303 rect.push(0xff);
1304 rect.push16(2); // x: 2
1305 rect.push16(2); // y: 2
1306 rect.push16(2); // width: 2
1307 rect.push16(2); // height: 2
1308
1309 send_fbu_msg(info, [rect], client);
1310 expect(client._display).to.have.displayed(target_data_check);
1311 });
1312
1313 describe('the HEXTILE encoding handler', function () {
1314 var client;
1315 beforeEach(function () {
1316 client = make_rfb();
1317 client.connect('host', 8675);
1318 client._sock._websocket._open();
1319 client._rfb_state = 'normal';
1320 client._fb_name = 'some device';
1321 // a really small frame
1322 client._fb_width = 4;
1323 client._fb_height = 4;
1324 client._display._fb_width = 4;
1325 client._display._fb_height = 4;
2c9623b5
SR
1326 client._display._viewportLoc.w = 4;
1327 client._display._viewportLoc.h = 4;
b1dee947
SR
1328 client._fb_Bpp = 4;
1329 });
1330
1331 it('should handle a tile with fg, bg specified, normal subrects', function () {
1332 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1333 var rect = [];
1334 rect.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1335 rect.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1336 rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
1337 rect.push(0x00);
1338 rect.push(0x00);
1339 rect.push(0xff);
1340 rect.push(2); // 2 subrects
1341 rect.push(0); // x: 0, y: 0
1342 rect.push(1 | (1 << 4)); // width: 2, height: 2
1343 rect.push(2 | (2 << 4)); // x: 2, y: 2
1344 rect.push(1 | (1 << 4)); // width: 2, height: 2
1345 send_fbu_msg(info, [rect], client);
1346 expect(client._display).to.have.displayed(target_data_check);
1347 });
1348
1349 it('should handle a raw tile', function () {
1350 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1351 var rect = [];
1352 rect.push(0x01); // raw
1353 for (var i = 0; i < target_data.length; i += 4) {
1354 rect.push(target_data[i + 2]);
1355 rect.push(target_data[i + 1]);
1356 rect.push(target_data[i]);
1357 rect.push(target_data[i + 3]);
1358 }
1359 send_fbu_msg(info, [rect], client);
1360 expect(client._display).to.have.displayed(target_data);
1361 });
1362
1363 it('should handle a tile with only bg specified (solid bg)', function () {
1364 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1365 var rect = [];
1366 rect.push(0x02);
1367 rect.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1368 send_fbu_msg(info, [rect], client);
1369
1370 var expected = [];
1371 for (var i = 0; i < 16; i++) { expected.push32(0xff00ff); }
1372 expect(client._display).to.have.displayed(new Uint8Array(expected));
1373 });
1374
40ac6f0a
RK
1375 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1376 // set the width so we can have two tiles
1377 client._fb_width = 8;
1378 client._display._fb_width = 8;
1379 client._display._viewportLoc.w = 8;
1380
4865278d 1381 var info = [{ x: 0, y: 0, width: 32, height: 4, encoding: 0x05 }];
40ac6f0a
RK
1382
1383 var rect = [];
1384
1385 // send a bg frame
1386 rect.push(0x02);
1387 rect.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1388
1389 // send an empty frame
1390 rect.push(0x00);
1391
1392 send_fbu_msg(info, [rect], client);
1393
1394 var expected = [];
1395 var i;
1396 for (i = 0; i < 16; i++) { expected.push32(0xff00ff); } // rect 1: solid
1397 for (i = 0; i < 16; i++) { expected.push32(0xff00ff); } // rect 2: same bkground color
1398 expect(client._display).to.have.displayed(new Uint8Array(expected));
1399 });
1400
b1dee947
SR
1401 it('should handle a tile with bg and coloured subrects', function () {
1402 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1403 var rect = [];
1404 rect.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1405 rect.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1406 rect.push(2); // 2 subrects
1407 rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
1408 rect.push(0x00);
1409 rect.push(0x00);
1410 rect.push(0xff);
1411 rect.push(0); // x: 0, y: 0
1412 rect.push(1 | (1 << 4)); // width: 2, height: 2
1413 rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
1414 rect.push(0x00);
1415 rect.push(0x00);
1416 rect.push(0xff);
1417 rect.push(2 | (2 << 4)); // x: 2, y: 2
1418 rect.push(1 | (1 << 4)); // width: 2, height: 2
1419 send_fbu_msg(info, [rect], client);
1420 expect(client._display).to.have.displayed(target_data_check);
1421 });
1422
1423 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1424 client._fb_width = 4;
1425 client._fb_height = 17;
1426 client._display.resize(4, 17);
1427
1428 var info = [{ x: 0, y: 0, width: 4, height: 17, encoding: 0x05}];
1429 var rect = [];
1430 rect.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1431 rect.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1432 rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
1433 rect.push(0x00);
1434 rect.push(0x00);
1435 rect.push(0xff);
1436 rect.push(8); // 8 subrects
1437 var i;
1438 for (i = 0; i < 4; i++) {
1439 rect.push((0 << 4) | (i * 4)); // x: 0, y: i*4
1440 rect.push(1 | (1 << 4)); // width: 2, height: 2
1441 rect.push((2 << 4) | (i * 4 + 2)); // x: 2, y: i * 4 + 2
1442 rect.push(1 | (1 << 4)); // width: 2, height: 2
1443 }
1444 rect.push(0x08); // anysubrects
1445 rect.push(1); // 1 subrect
1446 rect.push(0); // x: 0, y: 0
1447 rect.push(1 | (1 << 4)); // width: 2, height: 2
1448 send_fbu_msg(info, [rect], client);
1449
1450 var expected = [];
1451 for (i = 0; i < 4; i++) { expected = expected.concat(target_data_check_arr); }
1452 expected = expected.concat(target_data_check_arr.slice(0, 16));
1453 expect(client._display).to.have.displayed(new Uint8Array(expected));
1454 });
1455
1456 it('should fail on an invalid subencoding', function () {
1457 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1458 var rects = [[45]]; // an invalid subencoding
1459 send_fbu_msg(info, rects, client);
1460 expect(client._rfb_state).to.equal('failed');
1461 });
1462 });
1463
1464 it.skip('should handle the TIGHT encoding', function () {
1465 // TODO(directxman12): test this
1466 });
1467
1468 it.skip('should handle the TIGHT_PNG encoding', function () {
1469 // TODO(directxman12): test this
1470 });
1471
1472 it('should handle the DesktopSize pseduo-encoding', function () {
1473 client.set_onFBResize(sinon.spy());
1474 sinon.spy(client._display, 'resize');
1475 send_fbu_msg([{ x: 0, y: 0, width: 20, height: 50, encoding: -223 }], [[]], client);
1476
1477 var spy = client.get_onFBResize();
1478 expect(spy).to.have.been.calledOnce;
1479 expect(spy).to.have.been.calledWith(sinon.match.any, 20, 50);
1480
1481 expect(client._fb_width).to.equal(20);
1482 expect(client._fb_height).to.equal(50);
1483
1484 expect(client._display.resize).to.have.been.calledOnce;
1485 expect(client._display.resize).to.have.been.calledWith(20, 50);
1486 });
1487
4dec490a 1488 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1489 var client;
1490
1491 beforeEach(function () {
1492 client = make_rfb();
1493 client.connect('host', 8675);
1494 client._sock._websocket._open();
1495 client._rfb_state = 'normal';
1496 client._fb_name = 'some device';
1497 client._supportsSetDesktopSize = false;
1498 // a really small frame
1499 client._fb_width = 4;
1500 client._fb_height = 4;
1501 client._display._fb_width = 4;
1502 client._display._fb_height = 4;
1503 client._display._viewportLoc.w = 4;
1504 client._display._viewportLoc.h = 4;
1505 client._fb_Bpp = 4;
1506 sinon.spy(client._display, 'resize');
1507 client.set_onFBResize(sinon.spy());
1508 });
1509
1510 function make_screen_data (nr_of_screens) {
1511 var data = [];
1512 data.push8(nr_of_screens); // number-of-screens
1513 data.push8(0); // padding
1514 data.push16(0); // padding
1515 for (var i=0; i<nr_of_screens; i += 1) {
1516 data.push32(0); // id
1517 data.push16(0); // x-position
1518 data.push16(0); // y-position
1519 data.push16(20); // width
1520 data.push16(50); // height
1521 data.push32(0); // flags
1522 }
1523 return data;
1524 }
1525
1526 it('should handle a resize requested by this client', function () {
1527 var reason_for_change = 1; // requested by this client
1528 var status_code = 0; // No error
1529
1530 send_fbu_msg([{ x: reason_for_change, y: status_code,
1531 width: 20, height: 50, encoding: -308 }],
1532 make_screen_data(1), client);
1533
1534 expect(client._supportsSetDesktopSize).to.be.true;
1535 expect(client._fb_width).to.equal(20);
1536 expect(client._fb_height).to.equal(50);
1537
1538 expect(client._display.resize).to.have.been.calledOnce;
1539 expect(client._display.resize).to.have.been.calledWith(20, 50);
1540
1541 var spy = client.get_onFBResize();
1542 expect(spy).to.have.been.calledOnce;
1543 expect(spy).to.have.been.calledWith(sinon.match.any, 20, 50);
1544 });
1545
1546 it('should handle a resize requested by another client', function () {
1547 var reason_for_change = 2; // requested by another client
1548 var status_code = 0; // No error
1549
1550 send_fbu_msg([{ x: reason_for_change, y: status_code,
1551 width: 20, height: 50, encoding: -308 }],
1552 make_screen_data(1), client);
1553
1554 expect(client._supportsSetDesktopSize).to.be.true;
1555 expect(client._fb_width).to.equal(20);
1556 expect(client._fb_height).to.equal(50);
1557
1558 expect(client._display.resize).to.have.been.calledOnce;
1559 expect(client._display.resize).to.have.been.calledWith(20, 50);
1560
1561 var spy = client.get_onFBResize();
1562 expect(spy).to.have.been.calledOnce;
1563 expect(spy).to.have.been.calledWith(sinon.match.any, 20, 50);
1564 });
1565
1566 it('should be able to recieve requests which contain data for multiple screens', function () {
1567 var reason_for_change = 2; // requested by another client
1568 var status_code = 0; // No error
1569
1570 send_fbu_msg([{ x: reason_for_change, y: status_code,
1571 width: 60, height: 50, encoding: -308 }],
1572 make_screen_data(3), client);
1573
1574 expect(client._supportsSetDesktopSize).to.be.true;
1575 expect(client._fb_width).to.equal(60);
1576 expect(client._fb_height).to.equal(50);
1577
1578 expect(client._display.resize).to.have.been.calledOnce;
1579 expect(client._display.resize).to.have.been.calledWith(60, 50);
1580
1581 var spy = client.get_onFBResize();
1582 expect(spy).to.have.been.calledOnce;
1583 expect(spy).to.have.been.calledWith(sinon.match.any, 60, 50);
1584 });
1585
1586 it('should not handle a failed request', function () {
798340b9 1587 var reason_for_change = 1; // requested by this client
4dec490a 1588 var status_code = 1; // Resize is administratively prohibited
1589
1590 send_fbu_msg([{ x: reason_for_change, y: status_code,
1591 width: 20, height: 50, encoding: -308 }],
1592 make_screen_data(1), client);
1593
1594 expect(client._fb_width).to.equal(4);
1595 expect(client._fb_height).to.equal(4);
1596
1597 expect(client._display.resize).to.not.have.been.called;
1598
1599 var spy = client.get_onFBResize();
1600 expect(spy).to.not.have.been.called;
1601 });
1602 });
1603
b1dee947
SR
1604 it.skip('should handle the Cursor pseudo-encoding', function () {
1605 // TODO(directxman12): test
1606 });
1607
1608 it('should handle the last_rect pseudo-encoding', function () {
1609 client.set_onFBUReceive(sinon.spy());
1610 send_fbu_msg([{ x: 0, y: 0, width: 0, height: 0, encoding: -224}], [[]], client, 100);
1611 expect(client._FBU.rects).to.equal(0);
1612 expect(client.get_onFBUReceive()).to.have.been.calledOnce;
1613 });
1614 });
1615 });
1616
1617 it('should set the colour map on the display on SetColourMapEntries', function () {
1618 var expected_cm = [];
1619 var data = [1, 0, 0, 1, 0, 4];
1620 var i;
1621 for (i = 0; i < 4; i++) {
1622 expected_cm[i + 1] = [i * 10, i * 10 + 1, i * 10 + 2];
1623 data.push16(expected_cm[i + 1][2] << 8);
1624 data.push16(expected_cm[i + 1][1] << 8);
1625 data.push16(expected_cm[i + 1][0] << 8);
1626 }
1627
1628 client._sock._websocket._receive_data(new Uint8Array(data));
1629 expect(client._display.get_colourMap()).to.deep.equal(expected_cm);
1630 });
1631
1632 describe('XVP Message Handling', function () {
1633 beforeEach(function () {
1634 client = make_rfb();
1635 client.connect('host', 8675);
1636 client._sock._websocket._open();
1637 client._rfb_state = 'normal';
1638 client._fb_name = 'some device';
1639 client._fb_width = 27;
1640 client._fb_height = 32;
1641 });
1642
1643 it('should call updateState with a message on XVP_FAIL, but keep the same state', function () {
1644 client._updateState = sinon.spy();
1645 client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 0]));
1646 expect(client._updateState).to.have.been.calledOnce;
1647 expect(client._updateState).to.have.been.calledWith('normal', 'Operation Failed');
1648 });
1649
1650 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1651 client.set_onXvpInit(sinon.spy());
1652 client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 1]));
1653 expect(client._rfb_xvp_ver).to.equal(10);
1654 expect(client.get_onXvpInit()).to.have.been.calledOnce;
1655 expect(client.get_onXvpInit()).to.have.been.calledWith(10);
1656 });
1657
1658 it('should fail on unknown XVP message types', function () {
1659 client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 237]));
1660 expect(client._rfb_state).to.equal('failed');
1661 });
1662 });
1663
1664 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1665 var expected_str = 'cheese!';
1666 var data = [3, 0, 0, 0];
1667 data.push32(expected_str.length);
1668 for (var i = 0; i < expected_str.length; i++) { data.push(expected_str.charCodeAt(i)); }
1669 client.set_onClipboard(sinon.spy());
1670
1671 client._sock._websocket._receive_data(new Uint8Array(data));
1672 var spy = client.get_onClipboard();
1673 expect(spy).to.have.been.calledOnce;
1674 expect(spy.args[0][1]).to.equal(expected_str);
1675 });
1676
1677 it('should fire the bell callback on Bell', function () {
1678 client.set_onBell(sinon.spy());
1679 client._sock._websocket._receive_data(new Uint8Array([2]));
1680 expect(client.get_onBell()).to.have.been.calledOnce;
1681 });
1682
1683 it('should fail on an unknown message type', function () {
1684 client._sock._websocket._receive_data(new Uint8Array([87]));
1685 expect(client._rfb_state).to.equal('failed');
1686 });
1687 });
1688
1689 describe('Asynchronous Events', function () {
1690 describe('Mouse event handlers', function () {
1691 var client;
1692 beforeEach(function () {
1693 client = make_rfb();
1694 client._sock.send = sinon.spy();
1695 client._rfb_state = 'normal';
1696 });
1697
1698 it('should not send button messages in view-only mode', function () {
1699 client._view_only = true;
1700 client._mouse._onMouseButton(0, 0, 1, 0x001);
1701 expect(client._sock.send).to.not.have.been.called;
1702 });
1703
1704 it('should not send movement messages in view-only mode', function () {
1705 client._view_only = true;
1706 client._mouse._onMouseMove(0, 0);
1707 expect(client._sock.send).to.not.have.been.called;
1708 });
1709
1710 it('should send a pointer event on mouse button presses', function () {
1711 client._mouse._onMouseButton(10, 12, 1, 0x001);
1712 expect(client._sock.send).to.have.been.calledOnce;
1713 var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x001);
1714 expect(client._sock.send).to.have.been.calledWith(pointer_msg);
1715 });
1716
d02a99f0
SR
1717 it('should send a mask of 1 on mousedown', function () {
1718 client._mouse._onMouseButton(10, 12, 1, 0x001);
1719 expect(client._sock.send).to.have.been.calledOnce;
1720 var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x001);
1721 expect(client._sock.send).to.have.been.calledWith(pointer_msg);
1722 });
1723
1724 it('should send a mask of 0 on mouseup', function () {
3b4fd003 1725 client._mouse_buttonMask = 0x001;
d02a99f0
SR
1726 client._mouse._onMouseButton(10, 12, 0, 0x001);
1727 expect(client._sock.send).to.have.been.calledOnce;
1728 var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x000);
1729 expect(client._sock.send).to.have.been.calledWith(pointer_msg);
1730 });
1731
b1dee947
SR
1732 it('should send a pointer event on mouse movement', function () {
1733 client._mouse._onMouseMove(10, 12);
1734 expect(client._sock.send).to.have.been.calledOnce;
1735 var pointer_msg = RFB.messages.pointerEvent(10, 12, 0);
1736 expect(client._sock.send).to.have.been.calledWith(pointer_msg);
1737 });
1738
1739 it('should set the button mask so that future mouse movements use it', function () {
1740 client._mouse._onMouseButton(10, 12, 1, 0x010);
1741 client._sock.send = sinon.spy();
1742 client._mouse._onMouseMove(13, 9);
1743 expect(client._sock.send).to.have.been.calledOnce;
1744 var pointer_msg = RFB.messages.pointerEvent(13, 9, 0x010);
1745 expect(client._sock.send).to.have.been.calledWith(pointer_msg);
1746 });
1747
1748 // NB(directxman12): we don't need to test not sending messages in
1749 // non-normal modes, since we haven't grabbed input
1750 // yet (grabbing input should be checked in the lifecycle tests).
1751
1752 it('should not send movement messages when viewport dragging', function () {
1753 client._viewportDragging = true;
636be753 1754 client._display.viewportChangePos = sinon.spy();
b1dee947
SR
1755 client._mouse._onMouseMove(13, 9);
1756 expect(client._sock.send).to.not.have.been.called;
1757 });
1758
1759 it('should not send button messages when initiating viewport dragging', function () {
1760 client._viewportDrag = true;
1761 client._mouse._onMouseButton(13, 9, 0x001);
1762 expect(client._sock.send).to.not.have.been.called;
1763 });
1764
1765 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1766 client._viewportDrag = true;
1767 client._mouse._onMouseButton(13, 9, 0x001);
1768 expect(client._viewportDragging).to.be.true;
1769 expect(client._viewportDragPos).to.deep.equal({ x: 13, y: 9 });
1770 });
1771
1772 it('should terminate viewport dragging on a button up event, if enabled', function () {
1773 client._viewportDrag = true;
1774 client._viewportDragging = true;
1775 client._mouse._onMouseButton(13, 9, 0x000);
1776 expect(client._viewportDragging).to.be.false;
1777 });
1778
1779 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1780 client._viewportDrag = true;
1781 client._viewportDragging = true;
1782 client._viewportDragPos = { x: 13, y: 9 };
636be753 1783 client._display.viewportChangePos = sinon.spy();
b1dee947
SR
1784
1785 client._mouse._onMouseMove(10, 4);
1786
1787 expect(client._viewportDragging).to.be.true;
1788 expect(client._viewportDragPos).to.deep.equal({ x: 10, y: 4 });
636be753 1789 expect(client._display.viewportChangePos).to.have.been.calledOnce;
1790 expect(client._display.viewportChangePos).to.have.been.calledWith(3, 5);
b1dee947
SR
1791 });
1792 });
1793
1794 describe('Keyboard Event Handlers', function () {
1795 var client;
1796 beforeEach(function () {
1797 client = make_rfb();
1798 client._sock.send = sinon.spy();
1799 });
1800
1801 it('should send a key message on a key press', function () {
1802 client._keyboard._onKeyPress(1234, 1);
1803 expect(client._sock.send).to.have.been.calledOnce;
1804 var key_msg = RFB.messages.keyEvent(1234, 1);
1805 expect(client._sock.send).to.have.been.calledWith(key_msg);
1806 });
1807
1808 it('should not send messages in view-only mode', function () {
1809 client._view_only = true;
1810 client._keyboard._onKeyPress(1234, 1);
1811 expect(client._sock.send).to.not.have.been.called;
1812 });
1813 });
1814
1815 describe('WebSocket event handlers', function () {
1816 var client;
1817 beforeEach(function () {
1818 client = make_rfb();
1819 this.clock = sinon.useFakeTimers();
1820 });
1821
1822 afterEach(function () { this.clock.restore(); });
1823
1824 // message events
1825 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
1826 client.connect('host', 8675);
1827 client._rfb_state = 'normal';
1828 client._normal_msg = sinon.spy();
1829 client._sock._websocket._receive_data(Base64.encode([]));
1830 expect(client._normal_msg).to.not.have.been.called;
1831 });
1832
1833 it('should handle a message in the normal state as a normal message', function () {
1834 client.connect('host', 8675);
1835 client._rfb_state = 'normal';
1836 client._normal_msg = sinon.spy();
1837 client._sock._websocket._receive_data(Base64.encode([1, 2, 3]));
1838 expect(client._normal_msg).to.have.been.calledOnce;
1839 });
1840
1841 it('should handle a message in any non-disconnected/failed state like an init message', function () {
1842 client.connect('host', 8675);
1843 client._rfb_state = 'ProtocolVersion';
1844 client._init_msg = sinon.spy();
1845 client._sock._websocket._receive_data(Base64.encode([1, 2, 3]));
1846 expect(client._init_msg).to.have.been.calledOnce;
1847 });
1848
1849 it('should split up the handling of muplitle normal messages across 10ms intervals', function () {
1850 client.connect('host', 8675);
1851 client._sock._websocket._open();
1852 client._rfb_state = 'normal';
1853 client.set_onBell(sinon.spy());
1854 client._sock._websocket._receive_data(new Uint8Array([0x02, 0x02]));
1855 expect(client.get_onBell()).to.have.been.calledOnce;
1856 this.clock.tick(20);
1857 expect(client.get_onBell()).to.have.been.calledTwice;
1858 });
1859
1860 // open events
1861 it('should update the state to ProtocolVersion on open (if the state is "connect")', function () {
1862 client.connect('host', 8675);
1863 client._sock._websocket._open();
1864 expect(client._rfb_state).to.equal('ProtocolVersion');
1865 });
1866
1867 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
1868 client.connect('host', 8675);
1869 client._rfb_state = 'some_other_state';
1870 client._sock._websocket._open();
1871 expect(client._rfb_state).to.equal('failed');
1872 });
1873
1874 // close events
1875 it('should transition to "disconnected" from "disconnect" on a close event', function () {
1876 client.connect('host', 8675);
1877 client._rfb_state = 'disconnect';
1878 client._sock._websocket.close();
1879 expect(client._rfb_state).to.equal('disconnected');
1880 });
1881
1882 it('should transition to failed if we get a close event from any non-"disconnection" state', function () {
1883 client.connect('host', 8675);
1884 client._rfb_state = 'normal';
1885 client._sock._websocket.close();
1886 expect(client._rfb_state).to.equal('failed');
1887 });
1888
155d78b3
JS
1889 it('should unregister close event handler', function () {
1890 sinon.spy(client._sock, 'off');
1891 client.connect('host', 8675);
1892 client._rfb_state = 'disconnect';
1893 client._sock._websocket.close();
1894 expect(client._sock.off).to.have.been.calledWith('close');
1895 });
1896
b1dee947
SR
1897 // error events do nothing
1898 });
1899 });
1900});