1 // requires local modules: util, websock, rfb, keyboard, keysym, keysymdef, input, inflator, des, display
2 // requires test modules: fake.websocket, assertions
3 /* jshint expr: true */
4 var assert
= chai
.assert
;
5 var expect
= chai
.expect
;
7 function make_rfb (extra_opts
) {
12 extra_opts
.target
= extra_opts
.target
|| document
.createElement('canvas');
13 return new RFB(extra_opts
);
16 describe('Remote Frame Buffer Protocol Client', function() {
18 before(FakeWebSocket
.replace
);
19 after(FakeWebSocket
.restore
);
22 this.clock
= sinon
.useFakeTimers();
23 // Use a single set of buffers instead of reallocating to
25 var sock
= new Websock();
26 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
27 var rQ
= new Uint8Array(sock
._rQbufferSize
);
29 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
30 Websock
.prototype._allocate_buffers = function () {
38 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
42 describe('Public API Basic Behavior', function () {
44 beforeEach(function () {
48 describe('#connect', function () {
49 beforeEach(function () { client
._updateState
= sinon
.spy(); });
51 it('should set the current state to "connect"', function () {
52 client
.connect('host', 8675);
53 expect(client
._updateState
).to
.have
.been
.calledOnce
;
54 expect(client
._updateState
).to
.have
.been
.calledWith('connect');
57 it('should fail if we are missing a host', function () {
58 sinon
.spy(client
, '_fail');
59 client
.connect(undefined, 8675);
60 expect(client
._fail
).to
.have
.been
.calledOnce
;
63 it('should fail if we are missing a port', function () {
64 sinon
.spy(client
, '_fail');
65 client
.connect('abc');
66 expect(client
._fail
).to
.have
.been
.calledOnce
;
69 it('should not update the state if we are missing a host or port', function () {
70 sinon
.spy(client
, '_fail');
71 client
.connect('abc');
72 expect(client
._fail
).to
.have
.been
.calledOnce
;
73 expect(client
._updateState
).to
.have
.been
.calledOnce
;
74 expect(client
._updateState
).to
.have
.been
.calledWith('failed');
78 describe('#disconnect', function () {
79 beforeEach(function () { client
._updateState
= sinon
.spy(); });
81 it('should set the current state to "disconnect"', function () {
83 expect(client
._updateState
).to
.have
.been
.calledOnce
;
84 expect(client
._updateState
).to
.have
.been
.calledWith('disconnect');
87 it('should unregister error event handler', function () {
88 sinon
.spy(client
._sock
, 'off');
90 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
93 it('should unregister message event handler', function () {
94 sinon
.spy(client
._sock
, 'off');
96 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
99 it('should unregister open event handler', function () {
100 sinon
.spy(client
._sock
, 'off');
102 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
106 describe('#sendPassword', function () {
107 beforeEach(function () { this.clock
= sinon
.useFakeTimers(); });
108 afterEach(function () { this.clock
.restore(); });
110 it('should set the state to "Authentication"', function () {
111 client
._rfb_state
= "blah";
112 client
.sendPassword('pass');
113 expect(client
._rfb_state
).to
.equal('Authentication');
116 it('should call init_msg "soon"', function () {
117 client
._init_msg
= sinon
.spy();
118 client
.sendPassword('pass');
120 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
124 describe('#sendCtrlAlDel', function () {
125 beforeEach(function () {
126 client
._sock
= new Websock();
127 client
._sock
.open('ws://', 'binary');
128 client
._sock
._websocket
._open();
129 sinon
.spy(client
._sock
, 'flush');
130 client
._rfb_state
= "normal";
131 client
._view_only
= false;
134 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
135 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0};
136 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
137 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
138 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
139 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
140 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
141 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
143 client
.sendCtrlAltDel();
144 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
147 it('should not send the keys if we are not in a normal state', function () {
148 client
._rfb_state
= "broken";
149 client
.sendCtrlAltDel();
150 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
153 it('should not send the keys if we are set as view_only', function () {
154 client
._view_only
= true;
155 client
.sendCtrlAltDel();
156 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
160 describe('#sendKey', function () {
161 beforeEach(function () {
162 client
._sock
= new Websock();
163 client
._sock
.open('ws://', 'binary');
164 client
._sock
._websocket
._open();
165 sinon
.spy(client
._sock
, 'flush');
166 client
._rfb_state
= "normal";
167 client
._view_only
= false;
170 it('should send a single key with the given code and state (down = true)', function () {
171 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0};
172 RFB
.messages
.keyEvent(expected
, 123, 1);
173 client
.sendKey(123, true);
174 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
177 it('should send both a down and up event if the state is not specified', function () {
178 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0};
179 RFB
.messages
.keyEvent(expected
, 123, 1);
180 RFB
.messages
.keyEvent(expected
, 123, 0);
182 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
185 it('should not send the key if we are not in a normal state', function () {
186 client
._rfb_state
= "broken";
188 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
191 it('should not send the key if we are set as view_only', function () {
192 client
._view_only
= true;
194 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
198 describe('#clipboardPasteFrom', 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
, 'flush');
204 client
._rfb_state
= "normal";
205 client
._view_only
= false;
208 it('should send the given text in a paste event', function () {
209 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0};
210 RFB
.messages
.clientCutText(expected
, 'abc');
211 client
.clipboardPasteFrom('abc');
212 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
215 it('should not send the text if we are not in a normal state', function () {
216 client
._rfb_state
= "broken";
217 client
.clipboardPasteFrom('abc');
218 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
222 describe("#setDesktopSize", function () {
223 beforeEach(function() {
224 client
._sock
= new Websock();
225 client
._sock
.open('ws://', 'binary');
226 client
._sock
._websocket
._open();
227 sinon
.spy(client
._sock
, 'flush');
228 client
._rfb_state
= "normal";
229 client
._view_only
= false;
230 client
._supportsSetDesktopSize
= true;
233 it('should send the request with the given width and height', function () {
234 var expected
= [251];
235 expected
.push8(0); // padding
236 expected
.push16(1); // width
237 expected
.push16(2); // height
238 expected
.push8(1); // number-of-screens
239 expected
.push8(0); // padding before screen array
240 expected
.push32(0); // id
241 expected
.push16(0); // x-position
242 expected
.push16(0); // y-position
243 expected
.push16(1); // width
244 expected
.push16(2); // height
245 expected
.push32(0); // flags
247 client
.setDesktopSize(1, 2);
248 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
251 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
252 client
._supportsSetDesktopSize
= false;
253 client
.setDesktopSize(1,2);
254 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
257 it('should not send the request if we are not in a normal state', function () {
258 client
._rfb_state
= "broken";
259 client
.setDesktopSize(1,2);
260 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
264 describe("XVP operations", function () {
265 beforeEach(function () {
266 client
._sock
= new Websock();
267 client
._sock
.open('ws://', 'binary');
268 client
._sock
._websocket
._open();
269 sinon
.spy(client
._sock
, 'flush');
270 client
._rfb_state
= "normal";
271 client
._view_only
= false;
272 client
._rfb_xvp_ver
= 1;
275 it('should send the shutdown signal on #xvpShutdown', function () {
276 client
.xvpShutdown();
277 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
280 it('should send the reboot signal on #xvpReboot', function () {
282 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
285 it('should send the reset signal on #xvpReset', function () {
287 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
290 it('should support sending arbitrary XVP operations via #xvpOp', function () {
292 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x07]));
295 it('should not send XVP operations with higher versions than we support', function () {
296 expect(client
.xvpOp(2, 7)).to
.be
.false;
297 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
302 describe('Misc Internals', function () {
303 describe('#_updateState', function () {
305 beforeEach(function () {
306 this.clock
= sinon
.useFakeTimers();
310 afterEach(function () {
311 this.clock
.restore();
314 it('should clear the disconnect timer if the state is not disconnect', function () {
315 var spy
= sinon
.spy();
316 client
._disconnTimer
= setTimeout(spy
, 50);
317 client
._updateState('normal');
319 expect(spy
).to
.not
.have
.been
.called
;
320 expect(client
._disconnTimer
).to
.be
.null;
325 describe('Page States', function () {
326 describe('loaded', function () {
328 beforeEach(function () { client
= make_rfb(); });
330 it('should close any open WebSocket connection', function () {
331 sinon
.spy(client
._sock
, 'close');
332 client
._updateState('loaded');
333 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
337 describe('disconnected', function () {
339 beforeEach(function () { client
= make_rfb(); });
341 it('should close any open WebSocket connection', function () {
342 sinon
.spy(client
._sock
, 'close');
343 client
._updateState('disconnected');
344 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
348 describe('connect', function () {
350 beforeEach(function () { client
= make_rfb(); });
352 it('should reset the variable states', function () {
353 sinon
.spy(client
, '_init_vars');
354 client
._updateState('connect');
355 expect(client
._init_vars
).to
.have
.been
.calledOnce
;
358 it('should actually connect to the websocket', function () {
359 sinon
.spy(client
._sock
, 'open');
360 client
._updateState('connect');
361 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
364 it('should use wss:// to connect if encryption is enabled', function () {
365 sinon
.spy(client
._sock
, 'open');
366 client
.set_encrypt(true);
367 client
._updateState('connect');
368 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
371 it('should use ws:// to connect if encryption is not enabled', function () {
372 sinon
.spy(client
._sock
, 'open');
373 client
.set_encrypt(true);
374 client
._updateState('connect');
375 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
378 it('should use a uri with the host, port, and path specified to connect', function () {
379 sinon
.spy(client
._sock
, 'open');
380 client
.set_encrypt(false);
381 client
._rfb_host
= 'HOST';
382 client
._rfb_port
= 8675;
383 client
._rfb_path
= 'PATH';
384 client
._updateState('connect');
385 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
388 it('should attempt to close the websocket before we open an new one', function () {
389 sinon
.spy(client
._sock
, 'close');
390 client
._updateState('connect');
391 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
395 describe('disconnect', function () {
397 beforeEach(function () {
398 this.clock
= sinon
.useFakeTimers();
400 client
.connect('host', 8675);
403 afterEach(function () {
404 this.clock
.restore();
407 it('should fail if we do not call Websock.onclose within the disconnection timeout', function () {
408 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
409 client
._updateState('disconnect');
410 this.clock
.tick(client
.get_disconnectTimeout() * 1000);
411 expect(client
._rfb_state
).to
.equal('failed');
414 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
415 client
._updateState('disconnect');
416 this.clock
.tick(client
.get_disconnectTimeout() * 500);
417 client
._sock
._websocket
.close();
418 this.clock
.tick(client
.get_disconnectTimeout() * 500 + 1);
419 expect(client
._rfb_state
).to
.equal('disconnected');
422 it('should close the WebSocket connection', function () {
423 sinon
.spy(client
._sock
, 'close');
424 client
._updateState('disconnect');
425 expect(client
._sock
.close
).to
.have
.been
.calledTwice
; // once on loaded, once on disconnect
429 describe('failed', function () {
431 beforeEach(function () {
432 this.clock
= sinon
.useFakeTimers();
434 client
.connect('host', 8675);
437 afterEach(function () {
438 this.clock
.restore();
441 it('should close the WebSocket connection', function () {
442 sinon
.spy(client
._sock
, 'close');
443 client
._updateState('failed');
444 expect(client
._sock
.close
).to
.have
.been
.called
;
447 it('should transition to disconnected but stay in failed state', function () {
448 client
.set_onUpdateState(sinon
.spy());
449 client
._updateState('failed');
451 expect(client
._rfb_state
).to
.equal('failed');
453 var onUpdateState
= client
.get_onUpdateState();
454 expect(onUpdateState
).to
.have
.been
.called
;
455 // it should be specifically the last call
456 expect(onUpdateState
.args
[onUpdateState
.args
.length
- 1][1]).to
.equal('disconnected');
457 expect(onUpdateState
.args
[onUpdateState
.args
.length
- 1][2]).to
.equal('failed');
462 describe('fatal', function () {
464 beforeEach(function () { client
= make_rfb(); });
466 it('should close any open WebSocket connection', function () {
467 sinon
.spy(client
._sock
, 'close');
468 client
._updateState('fatal');
469 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
473 // NB(directxman12): Normal does *nothing* in updateState
476 describe('Protocol Initialization States', function () {
477 describe('ProtocolVersion', function () {
478 beforeEach(function () {
479 this.clock
= sinon
.useFakeTimers();
482 afterEach(function () {
483 this.clock
.restore();
486 function send_ver (ver
, client
) {
487 var arr
= new Uint8Array(12);
488 for (var i
= 0; i
< ver
.length
; i
++) {
489 arr
[i
+4] = ver
.charCodeAt(i
);
491 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
493 client
._sock
._websocket
._receive_data(arr
);
496 describe('version parsing', function () {
498 beforeEach(function () {
500 client
.connect('host', 8675);
501 client
._sock
._websocket
._open();
504 it('should interpret version 000.000 as a repeater', function () {
505 client
._repeaterID
= '\x01\x02\x03\x04\x05';
506 send_ver('000.000', client
);
507 expect(client
._rfb_version
).to
.equal(0);
509 var sent_data
= client
._sock
._websocket
._get_sent_data();
510 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
513 it('should interpret version 003.003 as version 3.3', function () {
514 send_ver('003.003', client
);
515 expect(client
._rfb_version
).to
.equal(3.3);
518 it('should interpret version 003.006 as version 3.3', function () {
519 send_ver('003.006', client
);
520 expect(client
._rfb_version
).to
.equal(3.3);
523 it('should interpret version 003.889 as version 3.3', function () {
524 send_ver('003.889', client
);
525 expect(client
._rfb_version
).to
.equal(3.3);
528 it('should interpret version 003.007 as version 3.7', function () {
529 send_ver('003.007', client
);
530 expect(client
._rfb_version
).to
.equal(3.7);
533 it('should interpret version 003.008 as version 3.8', function () {
534 send_ver('003.008', client
);
535 expect(client
._rfb_version
).to
.equal(3.8);
538 it('should interpret version 004.000 as version 3.8', function () {
539 send_ver('004.000', client
);
540 expect(client
._rfb_version
).to
.equal(3.8);
543 it('should interpret version 004.001 as version 3.8', function () {
544 send_ver('004.001', client
);
545 expect(client
._rfb_version
).to
.equal(3.8);
548 it('should fail on an invalid version', function () {
549 send_ver('002.000', client
);
550 expect(client
._rfb_state
).to
.equal('failed');
555 beforeEach(function () {
557 client
.connect('host', 8675);
558 client
._sock
._websocket
._open();
561 it('should handle two step repeater negotiation', function () {
562 client
._repeaterID
= '\x01\x02\x03\x04\x05';
564 send_ver('000.000', client
);
565 expect(client
._rfb_version
).to
.equal(0);
566 var sent_data
= client
._sock
._websocket
._get_sent_data();
567 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
568 expect(sent_data
).to
.have
.length(250);
570 send_ver('003.008', client
);
571 expect(client
._rfb_version
).to
.equal(3.8);
574 it('should initialize the flush interval', function () {
575 client
._sock
.flush
= sinon
.spy();
576 send_ver('003.008', client
);
577 this.clock
.tick(100);
578 expect(client
._sock
.flush
).to
.have
.been
.calledThrice
;
581 it('should send back the interpreted version', function () {
582 send_ver('004.000', client
);
584 var expected_str
= 'RFB 003.008\n';
586 for (var i
= 0; i
< expected_str
.length
; i
++) {
587 expected
[i
] = expected_str
.charCodeAt(i
);
590 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
593 it('should transition to the Security state on successful negotiation', function () {
594 send_ver('003.008', client
);
595 expect(client
._rfb_state
).to
.equal('Security');
599 describe('Security', function () {
602 beforeEach(function () {
604 client
.connect('host', 8675);
605 client
._sock
._websocket
._open();
606 client
._rfb_state
= 'Security';
609 it('should simply receive the auth scheme when for versions < 3.7', function () {
610 client
._rfb_version
= 3.6;
611 var auth_scheme_raw
= [1, 2, 3, 4];
612 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
613 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
614 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
615 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
618 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
619 client
._rfb_version
= 3.7;
620 var auth_schemes
= [2, 1, 2];
621 client
._sock
._websocket
._receive_data(auth_schemes
);
622 expect(client
._rfb_auth_scheme
).to
.equal(2);
623 expect(client
._sock
).to
.have
.sent(new Uint8Array([2]));
626 it('should fail if there are no supported schemes for versions >= 3.7', function () {
627 client
._rfb_version
= 3.7;
628 var auth_schemes
= [1, 32];
629 client
._sock
._websocket
._receive_data(auth_schemes
);
630 expect(client
._rfb_state
).to
.equal('failed');
633 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
634 client
._rfb_version
= 3.7;
635 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
636 sinon
.spy(client
, '_fail');
637 client
._sock
._websocket
._receive_data(failure_data
);
639 expect(client
._fail
).to
.have
.been
.calledTwice
;
640 expect(client
._fail
).to
.have
.been
.calledWith('Security failure: whoops');
643 it('should transition to the Authentication state and continue on successful negotiation', function () {
644 client
._rfb_version
= 3.7;
645 var auth_schemes
= [1, 1];
646 client
._negotiate_authentication
= sinon
.spy();
647 client
._sock
._websocket
._receive_data(auth_schemes
);
648 expect(client
._rfb_state
).to
.equal('Authentication');
649 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
653 describe('Authentication', function () {
656 beforeEach(function () {
658 client
.connect('host', 8675);
659 client
._sock
._websocket
._open();
660 client
._rfb_state
= 'Security';
663 function send_security(type
, cl
) {
664 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
667 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
668 client
._rfb_version
= 3.6;
669 var err_msg
= "Whoopsies";
670 var data
= [0, 0, 0, 0];
671 var err_len
= err_msg
.length
;
672 data
.push32(err_len
);
673 for (var i
= 0; i
< err_len
; i
++) {
674 data
.push(err_msg
.charCodeAt(i
));
677 sinon
.spy(client
, '_fail');
678 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
679 expect(client
._rfb_state
).to
.equal('failed');
680 expect(client
._fail
).to
.have
.been
.calledWith('Auth failure: Whoopsies');
683 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
684 client
._rfb_version
= 3.8;
685 send_security(1, client
);
686 expect(client
._rfb_state
).to
.equal('SecurityResult');
689 it('should transition straight to ClientInitialisation on "no auth" for versions < 3.8', function () {
690 client
._rfb_version
= 3.7;
691 sinon
.spy(client
, '_updateState');
692 send_security(1, client
);
693 expect(client
._updateState
).to
.have
.been
.calledWith('ClientInitialisation');
694 expect(client
._rfb_state
).to
.equal('ServerInitialisation');
697 it('should fail on an unknown auth scheme', function () {
698 client
._rfb_version
= 3.8;
699 send_security(57, client
);
700 expect(client
._rfb_state
).to
.equal('failed');
703 describe('VNC Authentication (type 2) Handler', function () {
706 beforeEach(function () {
708 client
.connect('host', 8675);
709 client
._sock
._websocket
._open();
710 client
._rfb_state
= 'Security';
711 client
._rfb_version
= 3.8;
714 it('should transition to the "password" state if missing a password', function () {
715 send_security(2, client
);
716 expect(client
._rfb_state
).to
.equal('password');
719 it('should encrypt the password with DES and then send it back', function () {
720 client
._rfb_password
= 'passwd';
721 send_security(2, client
);
722 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
725 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
726 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
728 var des_pass
= RFB
.genDES('passwd', challenge
);
729 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
732 it('should transition to SecurityResult immediately after sending the password', function () {
733 client
._rfb_password
= 'passwd';
734 send_security(2, client
);
737 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
738 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
740 expect(client
._rfb_state
).to
.equal('SecurityResult');
744 describe('XVP Authentication (type 22) Handler', function () {
747 beforeEach(function () {
749 client
.connect('host', 8675);
750 client
._sock
._websocket
._open();
751 client
._rfb_state
= 'Security';
752 client
._rfb_version
= 3.8;
755 it('should fall through to standard VNC authentication upon completion', function () {
756 client
.set_xvp_password_sep('#');
757 client
._rfb_password
= 'user#target#password';
758 client
._negotiate_std_vnc_auth
= sinon
.spy();
759 send_security(22, client
);
760 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
763 it('should transition to the "password" state if the passwords is missing', function() {
764 send_security(22, client
);
765 expect(client
._rfb_state
).to
.equal('password');
768 it('should transition to the "password" state if the passwords is improperly formatted', function() {
769 client
._rfb_password
= 'user@target';
770 send_security(22, client
);
771 expect(client
._rfb_state
).to
.equal('password');
774 it('should split the password, send the first two parts, and pass on the last part', function () {
775 client
.set_xvp_password_sep('#');
776 client
._rfb_password
= 'user#target#password';
777 client
._negotiate_std_vnc_auth
= sinon
.spy();
779 send_security(22, client
);
781 expect(client
._rfb_password
).to
.equal('password');
783 var expected
= [22, 4, 6]; // auth selection, len user, len target
784 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
786 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
790 describe('TightVNC Authentication (type 16) Handler', function () {
793 beforeEach(function () {
795 client
.connect('host', 8675);
796 client
._sock
._websocket
._open();
797 client
._rfb_state
= 'Security';
798 client
._rfb_version
= 3.8;
799 send_security(16, client
);
800 client
._sock
._websocket
._get_sent_data(); // skip the security reply
803 function send_num_str_pairs(pairs
, client
) {
804 var pairs_len
= pairs
.length
;
806 data
.push32(pairs_len
);
808 for (var i
= 0; i
< pairs_len
; i
++) {
809 data
.push32(pairs
[i
][0]);
811 for (j
= 0; j
< 4; j
++) {
812 data
.push(pairs
[i
][1].charCodeAt(j
));
814 for (j
= 0; j
< 8; j
++) {
815 data
.push(pairs
[i
][2].charCodeAt(j
));
819 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
822 it('should skip tunnel negotiation if no tunnels are requested', function () {
823 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
824 expect(client
._rfb_tightvnc
).to
.be
.true;
827 it('should fail if no supported tunnels are listed', function () {
828 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
829 expect(client
._rfb_state
).to
.equal('failed');
832 it('should choose the notunnel tunnel type', function () {
833 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
834 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
837 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
838 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
839 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
840 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
841 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
842 expect(client
._rfb_state
).to
.equal('SecurityResult');
845 /*it('should attempt to use VNC auth over no auth when possible', function () {
846 client._rfb_tightvnc = true;
847 client._negotiate_std_vnc_auth = sinon.spy();
848 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
849 expect(client._sock).to.have.sent([0, 0, 0, 1]);
850 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
851 expect(client._rfb_auth_scheme).to.equal(2);
852 });*/ // while this would make sense, the original code doesn't actually do this
854 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
855 client
._rfb_tightvnc
= true;
856 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
857 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
858 expect(client
._rfb_state
).to
.equal('SecurityResult');
861 it('should accept VNC authentication and transition to that', function () {
862 client
._rfb_tightvnc
= true;
863 client
._negotiate_std_vnc_auth
= sinon
.spy();
864 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
865 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
866 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
867 expect(client
._rfb_auth_scheme
).to
.equal(2);
870 it('should fail if there are no supported auth types', function () {
871 client
._rfb_tightvnc
= true;
872 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
873 expect(client
._rfb_state
).to
.equal('failed');
878 describe('SecurityResult', function () {
881 beforeEach(function () {
883 client
.connect('host', 8675);
884 client
._sock
._websocket
._open();
885 client
._rfb_state
= 'SecurityResult';
888 it('should fall through to ClientInitialisation on a response code of 0', function () {
889 client
._updateState
= sinon
.spy();
890 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
891 expect(client
._updateState
).to
.have
.been
.calledOnce
;
892 expect(client
._updateState
).to
.have
.been
.calledWith('ClientInitialisation');
895 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
896 client
._rfb_version
= 3.8;
897 sinon
.spy(client
, '_fail');
898 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
899 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
900 expect(client
._rfb_state
).to
.equal('failed');
901 expect(client
._fail
).to
.have
.been
.calledWith('whoops');
904 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
905 client
._rfb_version
= 3.7;
906 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
907 expect(client
._rfb_state
).to
.equal('failed');
911 describe('ClientInitialisation', function () {
914 beforeEach(function () {
916 client
.connect('host', 8675);
917 client
._sock
._websocket
._open();
918 client
._rfb_state
= 'SecurityResult';
921 it('should transition to the ServerInitialisation state', function () {
922 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
923 expect(client
._rfb_state
).to
.equal('ServerInitialisation');
926 it('should send 1 if we are in shared mode', function () {
927 client
.set_shared(true);
928 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
929 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
932 it('should send 0 if we are not in shared mode', function () {
933 client
.set_shared(false);
934 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
935 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
939 describe('ServerInitialisation', function () {
942 beforeEach(function () {
944 client
.connect('host', 8675);
945 client
._sock
._websocket
._open();
946 client
._rfb_state
= 'ServerInitialisation';
949 function send_server_init(opts
, client
) {
950 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
951 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
952 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
953 for (var opt
in opts
) {
954 full_opts
[opt
] = opts
[opt
];
958 data
.push16(full_opts
.width
);
959 data
.push16(full_opts
.height
);
961 data
.push(full_opts
.bpp
);
962 data
.push(full_opts
.depth
);
963 data
.push(full_opts
.big_endian
);
964 data
.push(full_opts
.true_color
);
966 data
.push16(full_opts
.red_max
);
967 data
.push16(full_opts
.green_max
);
968 data
.push16(full_opts
.blue_max
);
969 data
.push8(full_opts
.red_shift
);
970 data
.push8(full_opts
.green_shift
);
971 data
.push8(full_opts
.blue_shift
);
978 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
981 name_data
.push32(full_opts
.name
.length
);
982 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
983 name_data
.push(full_opts
.name
.charCodeAt(i
));
985 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
988 it('should set the framebuffer width and height', function () {
989 send_server_init({ width
: 32, height
: 84 }, client
);
990 expect(client
._fb_width
).to
.equal(32);
991 expect(client
._fb_height
).to
.equal(84);
994 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
996 it('should set the framebuffer name and call the callback', function () {
997 client
.set_onDesktopName(sinon
.spy());
998 send_server_init({ name
: 'some name' }, client
);
1000 var spy
= client
.get_onDesktopName();
1001 expect(client
._fb_name
).to
.equal('some name');
1002 expect(spy
).to
.have
.been
.calledOnce
;
1003 expect(spy
.args
[0][1]).to
.equal('some name');
1006 it('should handle the extended init message of the tight encoding', function () {
1007 // NB(sross): we don't actually do anything with it, so just test that we can
1008 // read it w/o throwing an error
1009 client
._rfb_tightvnc
= true;
1010 send_server_init({}, client
);
1012 var tight_data
= [];
1013 tight_data
.push16(1);
1014 tight_data
.push16(2);
1015 tight_data
.push16(3);
1016 tight_data
.push16(0);
1017 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1020 client
._sock
._websocket
._receive_data(tight_data
);
1022 expect(client
._rfb_state
).to
.equal('normal');
1025 it('should set the true color mode on the display to the configuration variable', function () {
1026 client
.set_true_color(false);
1027 sinon
.spy(client
._display
, 'set_true_color');
1028 send_server_init({ true_color
: 1 }, client
);
1029 expect(client
._display
.set_true_color
).to
.have
.been
.calledOnce
;
1030 expect(client
._display
.set_true_color
).to
.have
.been
.calledWith(false);
1033 it('should call the resize callback and resize the display', function () {
1034 client
.set_onFBResize(sinon
.spy());
1035 sinon
.spy(client
._display
, 'resize');
1036 send_server_init({ width
: 27, height
: 32 }, client
);
1038 var spy
= client
.get_onFBResize();
1039 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1040 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1041 expect(spy
).to
.have
.been
.calledOnce
;
1042 expect(spy
.args
[0][1]).to
.equal(27);
1043 expect(spy
.args
[0][2]).to
.equal(32);
1046 it('should grab the mouse and keyboard', function () {
1047 sinon
.spy(client
._keyboard
, 'grab');
1048 sinon
.spy(client
._mouse
, 'grab');
1049 send_server_init({}, client
);
1050 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1051 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1054 it('should set the BPP and depth to 4 and 3 respectively if in true color mode', function () {
1055 client
.set_true_color(true);
1056 send_server_init({}, client
);
1057 expect(client
._fb_Bpp
).to
.equal(4);
1058 expect(client
._fb_depth
).to
.equal(3);
1061 it('should set the BPP and depth to 1 and 1 respectively if not in true color mode', function () {
1062 client
.set_true_color(false);
1063 send_server_init({}, client
);
1064 expect(client
._fb_Bpp
).to
.equal(1);
1065 expect(client
._fb_depth
).to
.equal(1);
1068 // TODO(directxman12): test the various options in this configuration matrix
1069 it('should reply with the pixel format, client encodings, and initial update request', function () {
1070 client
.set_true_color(true);
1071 client
.set_local_cursor(false);
1072 // we skip the cursor encoding
1073 var expected
= {_sQ
: new Uint8Array(34 + 4 * (client
._encodings
.length
- 1)), _sQlen
: 0};
1074 RFB
.messages
.pixelFormat(expected
, 4, 3, true);
1075 RFB
.messages
.clientEncodings(expected
, client
._encodings
, false, true);
1076 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 0, h
: 0 },
1077 dirtyBoxes
: [ { x
: 0, y
: 0, w
: 27, h
: 32 } ] };
1078 RFB
.messages
.fbUpdateRequests(expected
, expected_cdr
, 27, 32);
1080 send_server_init({ width
: 27, height
: 32 }, client
);
1081 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
1084 it('should transition to the "normal" state', function () {
1085 send_server_init({}, client
);
1086 expect(client
._rfb_state
).to
.equal('normal');
1091 describe('Protocol Message Processing After Completing Initialization', function () {
1094 beforeEach(function () {
1095 client
= make_rfb();
1096 client
.connect('host', 8675);
1097 client
._sock
._websocket
._open();
1098 client
._rfb_state
= 'normal';
1099 client
._fb_name
= 'some device';
1100 client
._fb_width
= 640;
1101 client
._fb_height
= 20;
1104 describe('Framebuffer Update Handling', function () {
1107 beforeEach(function () {
1108 client
= make_rfb();
1109 client
.connect('host', 8675);
1110 client
._sock
._websocket
._open();
1111 client
._rfb_state
= 'normal';
1112 client
._fb_name
= 'some device';
1113 client
._fb_width
= 640;
1114 client
._fb_height
= 20;
1117 var target_data_arr
= [
1118 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1119 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1120 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1121 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1125 var target_data_check_arr
= [
1126 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1127 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1128 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1129 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1131 var target_data_check
;
1133 before(function () {
1134 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1135 target_data
= new Uint8Array(target_data_arr
);
1136 target_data_check
= new Uint8Array(target_data_check_arr
);
1139 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1142 if (!rect_cnt
|| rect_cnt
> -1) {
1144 data
.push(0); // msg type
1145 data
.push(0); // padding
1146 data
.push16(rect_cnt
|| rect_data
.length
);
1149 for (var i
= 0; i
< rect_data
.length
; i
++) {
1151 data
.push16(rect_info
[i
].x
);
1152 data
.push16(rect_info
[i
].y
);
1153 data
.push16(rect_info
[i
].width
);
1154 data
.push16(rect_info
[i
].height
);
1155 data
.push32(rect_info
[i
].encoding
);
1157 data
= data
.concat(rect_data
[i
]);
1160 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1163 it('should send an update request if there is sufficient data', function () {
1164 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0};
1165 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 0, h
: 0 },
1166 dirtyBoxes
: [ { x
: 0, y
: 0, w
: 240, h
: 20 } ] };
1167 RFB
.messages
.fbUpdateRequests(expected_msg
, expected_cdr
, 240, 20);
1169 client
._framebufferUpdate = function () { return true; };
1170 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1172 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1175 it('should not send an update request if we need more data', function () {
1176 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1177 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1180 it('should resume receiving an update if we previously did not have enough data', function () {
1181 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0};
1182 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 0, h
: 0 },
1183 dirtyBoxes
: [ { x
: 0, y
: 0, w
: 240, h
: 20 } ] };
1184 RFB
.messages
.fbUpdateRequests(expected_msg
, expected_cdr
, 240, 20);
1186 // just enough to set FBU.rects
1187 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1188 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1190 client
._framebufferUpdate = function () { return true; }; // we magically have enough data
1191 // 247 should *not* be used as the message type here
1192 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1193 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1196 it('should parse out information from a header before any actual data comes in', function () {
1197 client
.set_onFBUReceive(sinon
.spy());
1198 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02, encodingName
: 'RRE' };
1199 send_fbu_msg([rect_info
], [[]], client
);
1201 var spy
= client
.get_onFBUReceive();
1202 expect(spy
).to
.have
.been
.calledOnce
;
1203 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1206 it('should fire onFBUComplete when the update is complete', function () {
1207 client
.set_onFBUComplete(sinon
.spy());
1208 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: -224, encodingName
: 'last_rect' };
1209 send_fbu_msg([rect_info
], [[]], client
); // last_rect
1211 var spy
= client
.get_onFBUComplete();
1212 expect(spy
).to
.have
.been
.calledOnce
;
1213 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1216 it('should not fire onFBUComplete if we have not finished processing the update', function () {
1217 client
.set_onFBUComplete(sinon
.spy());
1218 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x00, encodingName
: 'RAW' };
1219 send_fbu_msg([rect_info
], [[]], client
);
1220 expect(client
.get_onFBUComplete()).to
.not
.have
.been
.called
;
1223 it('should call the appropriate encoding handler', function () {
1224 client
._encHandlers
[0x02] = sinon
.spy();
1225 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02 };
1226 send_fbu_msg([rect_info
], [[]], client
);
1227 expect(client
._encHandlers
[0x02]).to
.have
.been
.calledOnce
;
1230 it('should fail on an unsupported encoding', function () {
1231 client
.set_onFBUReceive(sinon
.spy());
1232 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1233 send_fbu_msg([rect_info
], [[]], client
);
1234 expect(client
._rfb_state
).to
.equal('failed');
1237 it('should be able to pause and resume receiving rects if not enought data', function () {
1238 // seed some initial data to copy
1239 client
._fb_width
= 4;
1240 client
._fb_height
= 4;
1241 client
._display
.resize(4, 4);
1242 var initial_data
= client
._display
._drawCtx
.createImageData(4, 2);
1243 var initial_data_arr
= target_data_check_arr
.slice(0, 32);
1244 for (var i
= 0; i
< 32; i
++) { initial_data
.data
[i
] = initial_data_arr
[i
]; }
1245 client
._display
._drawCtx
.putImageData(initial_data
, 0, 0);
1247 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1248 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1249 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1250 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1251 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1252 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1253 expect(client
._display
).to
.have
.displayed(target_data_check
);
1256 describe('Message Encoding Handlers', function () {
1259 beforeEach(function () {
1260 client
= make_rfb();
1261 client
.connect('host', 8675);
1262 client
._sock
._websocket
._open();
1263 client
._rfb_state
= 'normal';
1264 client
._fb_name
= 'some device';
1265 // a really small frame
1266 client
._fb_width
= 4;
1267 client
._fb_height
= 4;
1268 client
._display
._fb_width
= 4;
1269 client
._display
._fb_height
= 4;
1270 client
._display
._viewportLoc
.w
= 4;
1271 client
._display
._viewportLoc
.h
= 4;
1275 it('should handle the RAW encoding', function () {
1276 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1277 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1278 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1279 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1282 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1283 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1284 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1285 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1286 send_fbu_msg(info
, rects
, client
);
1287 expect(client
._display
).to
.have
.displayed(target_data
);
1290 it('should handle the COPYRECT encoding', function () {
1291 // seed some initial data to copy
1292 var initial_data
= client
._display
._drawCtx
.createImageData(4, 2);
1293 var initial_data_arr
= target_data_check_arr
.slice(0, 32);
1294 for (var i
= 0; i
< 32; i
++) { initial_data
.data
[i
] = initial_data_arr
[i
]; }
1295 client
._display
._drawCtx
.putImageData(initial_data
, 0, 0);
1297 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1298 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1299 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1300 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1301 send_fbu_msg(info
, rects
, client
);
1302 expect(client
._display
).to
.have
.displayed(target_data_check
);
1305 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1306 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1308 it('should handle the RRE encoding', function () {
1309 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1311 rect
.push32(2); // 2 subrects
1312 rect
.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1313 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1317 rect
.push16(0); // x: 0
1318 rect
.push16(0); // y: 0
1319 rect
.push16(2); // width: 2
1320 rect
.push16(2); // height: 2
1321 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1325 rect
.push16(2); // x: 2
1326 rect
.push16(2); // y: 2
1327 rect
.push16(2); // width: 2
1328 rect
.push16(2); // height: 2
1330 send_fbu_msg(info
, [rect
], client
);
1331 expect(client
._display
).to
.have
.displayed(target_data_check
);
1334 describe('the HEXTILE encoding handler', function () {
1336 beforeEach(function () {
1337 client
= make_rfb();
1338 client
.connect('host', 8675);
1339 client
._sock
._websocket
._open();
1340 client
._rfb_state
= 'normal';
1341 client
._fb_name
= 'some device';
1342 // a really small frame
1343 client
._fb_width
= 4;
1344 client
._fb_height
= 4;
1345 client
._display
._fb_width
= 4;
1346 client
._display
._fb_height
= 4;
1347 client
._display
._viewportLoc
.w
= 4;
1348 client
._display
._viewportLoc
.h
= 4;
1352 it('should handle a tile with fg, bg specified, normal subrects', function () {
1353 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1355 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1356 rect
.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1357 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1361 rect
.push(2); // 2 subrects
1362 rect
.push(0); // x: 0, y: 0
1363 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1364 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1365 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1366 send_fbu_msg(info
, [rect
], client
);
1367 expect(client
._display
).to
.have
.displayed(target_data_check
);
1370 it('should handle a raw tile', function () {
1371 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1373 rect
.push(0x01); // raw
1374 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1375 rect
.push(target_data
[i
+ 2]);
1376 rect
.push(target_data
[i
+ 1]);
1377 rect
.push(target_data
[i
]);
1378 rect
.push(target_data
[i
+ 3]);
1380 send_fbu_msg(info
, [rect
], client
);
1381 expect(client
._display
).to
.have
.displayed(target_data
);
1384 it('should handle a tile with only bg specified (solid bg)', function () {
1385 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1388 rect
.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1389 send_fbu_msg(info
, [rect
], client
);
1392 for (var i
= 0; i
< 16; i
++) { expected
.push32(0xff00ff); }
1393 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1396 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1397 // set the width so we can have two tiles
1398 client
._fb_width
= 8;
1399 client
._display
._fb_width
= 8;
1400 client
._display
._viewportLoc
.w
= 8;
1402 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1408 rect
.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1410 // send an empty frame
1413 send_fbu_msg(info
, [rect
], client
);
1417 for (i
= 0; i
< 16; i
++) { expected
.push32(0xff00ff); } // rect 1: solid
1418 for (i
= 0; i
< 16; i
++) { expected
.push32(0xff00ff); } // rect 2: same bkground color
1419 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1422 it('should handle a tile with bg and coloured subrects', function () {
1423 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1425 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1426 rect
.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1427 rect
.push(2); // 2 subrects
1428 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1432 rect
.push(0); // x: 0, y: 0
1433 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1434 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1438 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1439 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1440 send_fbu_msg(info
, [rect
], client
);
1441 expect(client
._display
).to
.have
.displayed(target_data_check
);
1444 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1445 client
._fb_width
= 4;
1446 client
._fb_height
= 17;
1447 client
._display
.resize(4, 17);
1449 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1451 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1452 rect
.push32(0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1453 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1457 rect
.push(8); // 8 subrects
1459 for (i
= 0; i
< 4; i
++) {
1460 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1461 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1462 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1463 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1465 rect
.push(0x08); // anysubrects
1466 rect
.push(1); // 1 subrect
1467 rect
.push(0); // x: 0, y: 0
1468 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1469 send_fbu_msg(info
, [rect
], client
);
1472 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1473 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1474 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1477 it('should fail on an invalid subencoding', function () {
1478 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1479 var rects
= [[45]]; // an invalid subencoding
1480 send_fbu_msg(info
, rects
, client
);
1481 expect(client
._rfb_state
).to
.equal('failed');
1485 it
.skip('should handle the TIGHT encoding', function () {
1486 // TODO(directxman12): test this
1489 it
.skip('should handle the TIGHT_PNG encoding', function () {
1490 // TODO(directxman12): test this
1493 it('should handle the DesktopSize pseduo-encoding', function () {
1494 client
.set_onFBResize(sinon
.spy());
1495 sinon
.spy(client
._display
, 'resize');
1496 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1498 var spy
= client
.get_onFBResize();
1499 expect(spy
).to
.have
.been
.calledOnce
;
1500 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1502 expect(client
._fb_width
).to
.equal(20);
1503 expect(client
._fb_height
).to
.equal(50);
1505 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1506 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1509 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1512 beforeEach(function () {
1513 client
= make_rfb();
1514 client
.connect('host', 8675);
1515 client
._sock
._websocket
._open();
1516 client
._rfb_state
= 'normal';
1517 client
._fb_name
= 'some device';
1518 client
._supportsSetDesktopSize
= false;
1519 // a really small frame
1520 client
._fb_width
= 4;
1521 client
._fb_height
= 4;
1522 client
._display
._fb_width
= 4;
1523 client
._display
._fb_height
= 4;
1524 client
._display
._viewportLoc
.w
= 4;
1525 client
._display
._viewportLoc
.h
= 4;
1527 sinon
.spy(client
._display
, 'resize');
1528 client
.set_onFBResize(sinon
.spy());
1531 function make_screen_data (nr_of_screens
) {
1533 data
.push8(nr_of_screens
); // number-of-screens
1534 data
.push8(0); // padding
1535 data
.push16(0); // padding
1536 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1537 data
.push32(0); // id
1538 data
.push16(0); // x-position
1539 data
.push16(0); // y-position
1540 data
.push16(20); // width
1541 data
.push16(50); // height
1542 data
.push32(0); // flags
1547 it('should handle a resize requested by this client', function () {
1548 var reason_for_change
= 1; // requested by this client
1549 var status_code
= 0; // No error
1551 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1552 width
: 20, height
: 50, encoding
: -308 }],
1553 make_screen_data(1), client
);
1555 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1556 expect(client
._fb_width
).to
.equal(20);
1557 expect(client
._fb_height
).to
.equal(50);
1559 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1560 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1562 var spy
= client
.get_onFBResize();
1563 expect(spy
).to
.have
.been
.calledOnce
;
1564 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1567 it('should handle a resize requested by another client', function () {
1568 var reason_for_change
= 2; // requested by another client
1569 var status_code
= 0; // No error
1571 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1572 width
: 20, height
: 50, encoding
: -308 }],
1573 make_screen_data(1), client
);
1575 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1576 expect(client
._fb_width
).to
.equal(20);
1577 expect(client
._fb_height
).to
.equal(50);
1579 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1580 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1582 var spy
= client
.get_onFBResize();
1583 expect(spy
).to
.have
.been
.calledOnce
;
1584 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1587 it('should be able to recieve requests which contain data for multiple screens', function () {
1588 var reason_for_change
= 2; // requested by another client
1589 var status_code
= 0; // No error
1591 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1592 width
: 60, height
: 50, encoding
: -308 }],
1593 make_screen_data(3), client
);
1595 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1596 expect(client
._fb_width
).to
.equal(60);
1597 expect(client
._fb_height
).to
.equal(50);
1599 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1600 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1602 var spy
= client
.get_onFBResize();
1603 expect(spy
).to
.have
.been
.calledOnce
;
1604 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 60, 50);
1607 it('should not handle a failed request', function () {
1608 var reason_for_change
= 1; // requested by this client
1609 var status_code
= 1; // Resize is administratively prohibited
1611 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1612 width
: 20, height
: 50, encoding
: -308 }],
1613 make_screen_data(1), client
);
1615 expect(client
._fb_width
).to
.equal(4);
1616 expect(client
._fb_height
).to
.equal(4);
1618 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1620 var spy
= client
.get_onFBResize();
1621 expect(spy
).to
.not
.have
.been
.called
;
1625 it
.skip('should handle the Cursor pseudo-encoding', function () {
1626 // TODO(directxman12): test
1629 it('should handle the last_rect pseudo-encoding', function () {
1630 client
.set_onFBUReceive(sinon
.spy());
1631 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1632 expect(client
._FBU
.rects
).to
.equal(0);
1633 expect(client
.get_onFBUReceive()).to
.have
.been
.calledOnce
;
1638 it('should set the colour map on the display on SetColourMapEntries', function () {
1639 var expected_cm
= [];
1640 var data
= [1, 0, 0, 1, 0, 4];
1642 for (i
= 0; i
< 4; i
++) {
1643 expected_cm
[i
+ 1] = [i
* 10, i
* 10 + 1, i
* 10 + 2];
1644 data
.push16(expected_cm
[i
+ 1][2] << 8);
1645 data
.push16(expected_cm
[i
+ 1][1] << 8);
1646 data
.push16(expected_cm
[i
+ 1][0] << 8);
1649 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1650 expect(client
._display
.get_colourMap()).to
.deep
.equal(expected_cm
);
1653 describe('XVP Message Handling', function () {
1654 beforeEach(function () {
1655 client
= make_rfb();
1656 client
.connect('host', 8675);
1657 client
._sock
._websocket
._open();
1658 client
._rfb_state
= 'normal';
1659 client
._fb_name
= 'some device';
1660 client
._fb_width
= 27;
1661 client
._fb_height
= 32;
1664 it('should call updateState with a message on XVP_FAIL, but keep the same state', function () {
1665 client
._updateState
= sinon
.spy();
1666 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 0]));
1667 expect(client
._updateState
).to
.have
.been
.calledOnce
;
1668 expect(client
._updateState
).to
.have
.been
.calledWith('normal', 'Operation Failed');
1671 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1672 client
.set_onXvpInit(sinon
.spy());
1673 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1674 expect(client
._rfb_xvp_ver
).to
.equal(10);
1675 expect(client
.get_onXvpInit()).to
.have
.been
.calledOnce
;
1676 expect(client
.get_onXvpInit()).to
.have
.been
.calledWith(10);
1679 it('should fail on unknown XVP message types', function () {
1680 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1681 expect(client
._rfb_state
).to
.equal('failed');
1685 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1686 var expected_str
= 'cheese!';
1687 var data
= [3, 0, 0, 0];
1688 data
.push32(expected_str
.length
);
1689 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1690 client
.set_onClipboard(sinon
.spy());
1692 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1693 var spy
= client
.get_onClipboard();
1694 expect(spy
).to
.have
.been
.calledOnce
;
1695 expect(spy
.args
[0][1]).to
.equal(expected_str
);
1698 it('should fire the bell callback on Bell', function () {
1699 client
.set_onBell(sinon
.spy());
1700 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1701 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1704 it('should fail on an unknown message type', function () {
1705 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1706 expect(client
._rfb_state
).to
.equal('failed');
1710 describe('Asynchronous Events', function () {
1711 describe('Mouse event handlers', function () {
1713 beforeEach(function () {
1714 client
= make_rfb();
1715 client
._sock
= new Websock();
1716 client
._sock
.open('ws://', 'binary');
1717 client
._sock
._websocket
._open();
1718 sinon
.spy(client
._sock
, 'flush');
1719 client
._rfb_state
= 'normal';
1722 it('should not send button messages in view-only mode', function () {
1723 client
._view_only
= true;
1724 client
._mouse
._onMouseButton(0, 0, 1, 0x001);
1725 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1728 it('should not send movement messages in view-only mode', function () {
1729 client
._view_only
= true;
1730 client
._mouse
._onMouseMove(0, 0);
1731 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1734 it('should send a pointer event on mouse button presses', function () {
1735 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1736 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0};
1737 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1738 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1741 it('should send a mask of 1 on mousedown', function () {
1742 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1743 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0};
1744 RFB
.messages
.pointerEvent(pointer_msg
, 0, 10, 12, 0x001);
1745 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1748 it('should send a mask of 0 on mouseup', function () {
1749 client
._mouse_buttonMask
= 0x001;
1750 client
._mouse
._onMouseButton(10, 12, 0, 0x001);
1751 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0};
1752 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1753 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1756 it('should send a pointer event on mouse movement', function () {
1757 client
._mouse
._onMouseMove(10, 12);
1758 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0};
1759 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1760 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1763 it('should set the button mask so that future mouse movements use it', function () {
1764 client
._mouse
._onMouseButton(10, 12, 1, 0x010);
1765 client
._mouse
._onMouseMove(13, 9);
1766 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0};
1767 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1768 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1769 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1772 // NB(directxman12): we don't need to test not sending messages in
1773 // non-normal modes, since we haven't grabbed input
1774 // yet (grabbing input should be checked in the lifecycle tests).
1776 it('should not send movement messages when viewport dragging', function () {
1777 client
._viewportDragging
= true;
1778 client
._display
.viewportChangePos
= sinon
.spy();
1779 client
._mouse
._onMouseMove(13, 9);
1780 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1783 it('should not send button messages when initiating viewport dragging', function () {
1784 client
._viewportDrag
= true;
1785 client
._mouse
._onMouseButton(13, 9, 0x001);
1786 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1789 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1790 client
._viewportDrag
= true;
1791 client
._mouse
._onMouseButton(13, 9, 0x001);
1792 expect(client
._viewportDragging
).to
.be
.true;
1793 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1796 it('should terminate viewport dragging on a button up event, if enabled', function () {
1797 client
._viewportDrag
= true;
1798 client
._viewportDragging
= true;
1799 client
._mouse
._onMouseButton(13, 9, 0x000);
1800 expect(client
._viewportDragging
).to
.be
.false;
1803 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1804 client
._viewportDrag
= true;
1805 client
._viewportDragging
= true;
1806 client
._viewportHasMoved
= false;
1807 client
._viewportDragPos
= { x
: 23, y
: 9 };
1808 client
._display
.viewportChangePos
= sinon
.spy();
1810 client
._mouse
._onMouseMove(10, 4);
1812 expect(client
._viewportDragging
).to
.be
.true;
1813 expect(client
._viewportHasMoved
.to
.be
.true;
1814 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 10, y
: 4 });
1815 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
1816 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(13, 5);
1820 describe('Keyboard Event Handlers', function () {
1822 beforeEach(function () {
1823 client
= make_rfb();
1824 client
._sock
= new Websock();
1825 client
._sock
.open('ws://', 'binary');
1826 client
._sock
._websocket
._open();
1827 sinon
.spy(client
._sock
, 'flush');
1830 it('should send a key message on a key press', function () {
1831 client
._keyboard
._onKeyPress(1234, 1);
1832 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0};
1833 RFB
.messages
.keyEvent(key_msg
, 1234, 1);
1834 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
1837 it('should not send messages in view-only mode', function () {
1838 client
._view_only
= true;
1839 client
._keyboard
._onKeyPress(1234, 1);
1840 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1844 describe('WebSocket event handlers', function () {
1846 beforeEach(function () {
1847 client
= make_rfb();
1848 this.clock
= sinon
.useFakeTimers();
1851 afterEach(function () { this.clock
.restore(); });
1854 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
1855 client
.connect('host', 8675);
1856 client
._rfb_state
= 'normal';
1857 client
._normal_msg
= sinon
.spy();
1858 client
._sock
._websocket
._receive_data(new Uint8Array([]));
1859 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
1862 it('should handle a message in the normal state as a normal message', function () {
1863 client
.connect('host', 8675);
1864 client
._rfb_state
= 'normal';
1865 client
._normal_msg
= sinon
.spy();
1866 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1867 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
1870 it('should handle a message in any non-disconnected/failed state like an init message', function () {
1871 client
.connect('host', 8675);
1872 client
._rfb_state
= 'ProtocolVersion';
1873 client
._init_msg
= sinon
.spy();
1874 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1875 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
1878 it('should split up the handling of muplitle normal messages across 10ms intervals', function () {
1879 client
.connect('host', 8675);
1880 client
._sock
._websocket
._open();
1881 client
._rfb_state
= 'normal';
1882 client
.set_onBell(sinon
.spy());
1883 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
1884 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1885 this.clock
.tick(20);
1886 expect(client
.get_onBell()).to
.have
.been
.calledTwice
;
1890 it('should update the state to ProtocolVersion on open (if the state is "connect")', function () {
1891 client
.connect('host', 8675);
1892 client
._sock
._websocket
._open();
1893 expect(client
._rfb_state
).to
.equal('ProtocolVersion');
1896 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
1897 client
.connect('host', 8675);
1898 client
._rfb_state
= 'some_other_state';
1899 client
._sock
._websocket
._open();
1900 expect(client
._rfb_state
).to
.equal('failed');
1904 it('should transition to "disconnected" from "disconnect" on a close event', function () {
1905 client
.connect('host', 8675);
1906 client
._rfb_state
= 'disconnect';
1907 client
._sock
._websocket
.close();
1908 expect(client
._rfb_state
).to
.equal('disconnected');
1911 it('should transition to failed if we get a close event from any non-"disconnection" state', function () {
1912 client
.connect('host', 8675);
1913 client
._rfb_state
= 'normal';
1914 client
._sock
._websocket
.close();
1915 expect(client
._rfb_state
).to
.equal('failed');
1918 it('should unregister close event handler', function () {
1919 sinon
.spy(client
._sock
, 'off');
1920 client
.connect('host', 8675);
1921 client
._rfb_state
= 'disconnect';
1922 client
._sock
._websocket
.close();
1923 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
1926 // error events do nothing