1 // requires local modules: util, websock, rfb, input/util, input/keysym, input/keysymdef, input/devices, 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 var push8 = function (arr
, num
) {
21 var push16 = function (arr
, num
) {
23 arr
.push((num
>> 8) & 0xFF,
27 var push32 = function (arr
, num
) {
29 arr
.push((num
>> 24) & 0xFF,
35 describe('Remote Frame Buffer Protocol Client', function() {
37 before(FakeWebSocket
.replace
);
38 after(FakeWebSocket
.restore
);
41 this.clock
= sinon
.useFakeTimers();
42 // Use a single set of buffers instead of reallocating to
44 var sock
= new Websock();
45 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
46 var rQ
= new Uint8Array(sock
._rQbufferSize
);
48 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
49 Websock
.prototype._allocate_buffers = function () {
57 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
61 describe('Public API Basic Behavior', function () {
63 beforeEach(function () {
67 describe('#connect', function () {
68 beforeEach(function () { client
._updateState
= sinon
.spy(); });
70 it('should set the current state to "connect"', function () {
71 client
.connect('host', 8675);
72 expect(client
._updateState
).to
.have
.been
.calledOnce
;
73 expect(client
._updateState
).to
.have
.been
.calledWith('connect');
76 it('should fail if we are missing a host', function () {
77 sinon
.spy(client
, '_fail');
78 client
.connect(undefined, 8675);
79 expect(client
._fail
).to
.have
.been
.calledOnce
;
82 it('should fail if we are missing a port', function () {
83 sinon
.spy(client
, '_fail');
84 client
.connect('abc');
85 expect(client
._fail
).to
.have
.been
.calledOnce
;
88 it('should not update the state if we are missing a host or port', function () {
89 sinon
.spy(client
, '_fail');
90 client
.connect('abc');
91 expect(client
._fail
).to
.have
.been
.calledOnce
;
92 expect(client
._updateState
).to
.have
.been
.calledOnce
;
93 expect(client
._updateState
).to
.have
.been
.calledWith('failed');
97 describe('#disconnect', function () {
98 beforeEach(function () { client
._updateState
= sinon
.spy(); });
100 it('should set the current state to "disconnect"', function () {
102 expect(client
._updateState
).to
.have
.been
.calledOnce
;
103 expect(client
._updateState
).to
.have
.been
.calledWith('disconnect');
106 it('should unregister error event handler', function () {
107 sinon
.spy(client
._sock
, 'off');
109 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
112 it('should unregister message event handler', function () {
113 sinon
.spy(client
._sock
, 'off');
115 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
118 it('should unregister open event handler', function () {
119 sinon
.spy(client
._sock
, 'off');
121 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
125 describe('#sendPassword', function () {
126 beforeEach(function () { this.clock
= sinon
.useFakeTimers(); });
127 afterEach(function () { this.clock
.restore(); });
129 it('should set the state to "Authentication"', function () {
130 client
._rfb_state
= "blah";
131 client
.sendPassword('pass');
132 expect(client
._rfb_state
).to
.equal('Authentication');
135 it('should call init_msg "soon"', function () {
136 client
._init_msg
= sinon
.spy();
137 client
.sendPassword('pass');
139 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
143 describe('#sendCtrlAlDel', function () {
144 beforeEach(function () {
145 client
._sock
= new Websock();
146 client
._sock
.open('ws://', 'binary');
147 client
._sock
._websocket
._open();
148 sinon
.spy(client
._sock
, 'flush');
149 client
._rfb_state
= "normal";
150 client
._view_only
= false;
153 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
154 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
155 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
156 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
157 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
158 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
159 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
160 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
162 client
.sendCtrlAltDel();
163 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
166 it('should not send the keys if we are not in a normal state', function () {
167 client
._rfb_state
= "broken";
168 client
.sendCtrlAltDel();
169 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
172 it('should not send the keys if we are set as view_only', function () {
173 client
._view_only
= true;
174 client
.sendCtrlAltDel();
175 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
179 describe('#sendKey', function () {
180 beforeEach(function () {
181 client
._sock
= new Websock();
182 client
._sock
.open('ws://', 'binary');
183 client
._sock
._websocket
._open();
184 sinon
.spy(client
._sock
, 'flush');
185 client
._rfb_state
= "normal";
186 client
._view_only
= false;
189 it('should send a single key with the given code and state (down = true)', function () {
190 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
191 RFB
.messages
.keyEvent(expected
, 123, 1);
192 client
.sendKey(123, true);
193 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
196 it('should send both a down and up event if the state is not specified', function () {
197 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
198 RFB
.messages
.keyEvent(expected
, 123, 1);
199 RFB
.messages
.keyEvent(expected
, 123, 0);
201 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
204 it('should not send the key if we are not in a normal state', function () {
205 client
._rfb_state
= "broken";
207 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
210 it('should not send the key if we are set as view_only', function () {
211 client
._view_only
= true;
213 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
217 describe('#clipboardPasteFrom', function () {
218 beforeEach(function () {
219 client
._sock
= new Websock();
220 client
._sock
.open('ws://', 'binary');
221 client
._sock
._websocket
._open();
222 sinon
.spy(client
._sock
, 'flush');
223 client
._rfb_state
= "normal";
224 client
._view_only
= false;
227 it('should send the given text in a paste event', function () {
228 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
229 RFB
.messages
.clientCutText(expected
, 'abc');
230 client
.clipboardPasteFrom('abc');
231 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
234 it('should not send the text if we are not in a normal state', function () {
235 client
._rfb_state
= "broken";
236 client
.clipboardPasteFrom('abc');
237 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
241 describe("#requestDesktopSize", function () {
242 beforeEach(function() {
243 client
._sock
= new Websock();
244 client
._sock
.open('ws://', 'binary');
245 client
._sock
._websocket
._open();
246 sinon
.spy(client
._sock
, 'flush');
247 client
._rfb_state
= "normal";
248 client
._view_only
= false;
249 client
._supportsSetDesktopSize
= true;
252 it('should send the request with the given width and height', function () {
253 var expected
= [251];
254 push8(expected
, 0); // padding
255 push16(expected
, 1); // width
256 push16(expected
, 2); // height
257 push8(expected
, 1); // number-of-screens
258 push8(expected
, 0); // padding before screen array
259 push32(expected
, 0); // id
260 push16(expected
, 0); // x-position
261 push16(expected
, 0); // y-position
262 push16(expected
, 1); // width
263 push16(expected
, 2); // height
264 push32(expected
, 0); // flags
266 client
.requestDesktopSize(1, 2);
267 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
270 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
271 client
._supportsSetDesktopSize
= false;
272 client
.requestDesktopSize(1,2);
273 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
276 it('should not send the request if we are not in a normal state', function () {
277 client
._rfb_state
= "broken";
278 client
.requestDesktopSize(1,2);
279 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
283 describe("XVP operations", function () {
284 beforeEach(function () {
285 client
._sock
= new Websock();
286 client
._sock
.open('ws://', 'binary');
287 client
._sock
._websocket
._open();
288 sinon
.spy(client
._sock
, 'flush');
289 client
._rfb_state
= "normal";
290 client
._view_only
= false;
291 client
._rfb_xvp_ver
= 1;
294 it('should send the shutdown signal on #xvpShutdown', function () {
295 client
.xvpShutdown();
296 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
299 it('should send the reboot signal on #xvpReboot', function () {
301 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
304 it('should send the reset signal on #xvpReset', function () {
306 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
309 it('should support sending arbitrary XVP operations via #xvpOp', function () {
311 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x07]));
314 it('should not send XVP operations with higher versions than we support', function () {
315 expect(client
.xvpOp(2, 7)).to
.be
.false;
316 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
321 describe('Misc Internals', function () {
322 describe('#_updateState', function () {
324 beforeEach(function () {
325 this.clock
= sinon
.useFakeTimers();
329 afterEach(function () {
330 this.clock
.restore();
333 it('should clear the disconnect timer if the state is not disconnect', function () {
334 var spy
= sinon
.spy();
335 client
._disconnTimer
= setTimeout(spy
, 50);
336 client
._updateState('normal');
338 expect(spy
).to
.not
.have
.been
.called
;
339 expect(client
._disconnTimer
).to
.be
.null;
344 describe('Page States', function () {
345 describe('loaded', function () {
347 beforeEach(function () { client
= make_rfb(); });
349 it('should close any open WebSocket connection', function () {
350 sinon
.spy(client
._sock
, 'close');
351 client
._updateState('loaded');
352 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
356 describe('disconnected', function () {
358 beforeEach(function () { client
= make_rfb(); });
360 it('should close any open WebSocket connection', function () {
361 sinon
.spy(client
._sock
, 'close');
362 client
._updateState('disconnected');
363 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
367 describe('connect', function () {
369 beforeEach(function () { client
= make_rfb(); });
371 it('should reset the variable states', function () {
372 sinon
.spy(client
, '_init_vars');
373 client
._updateState('connect');
374 expect(client
._init_vars
).to
.have
.been
.calledOnce
;
377 it('should actually connect to the websocket', function () {
378 sinon
.spy(client
._sock
, 'open');
379 client
._updateState('connect');
380 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
383 it('should use wss:// to connect if encryption is enabled', function () {
384 sinon
.spy(client
._sock
, 'open');
385 client
.set_encrypt(true);
386 client
._updateState('connect');
387 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
390 it('should use ws:// to connect if encryption is not enabled', function () {
391 sinon
.spy(client
._sock
, 'open');
392 client
.set_encrypt(true);
393 client
._updateState('connect');
394 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
397 it('should use a uri with the host, port, and path specified to connect', function () {
398 sinon
.spy(client
._sock
, 'open');
399 client
.set_encrypt(false);
400 client
._rfb_host
= 'HOST';
401 client
._rfb_port
= 8675;
402 client
._rfb_path
= 'PATH';
403 client
._updateState('connect');
404 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
407 it('should attempt to close the websocket before we open an new one', function () {
408 sinon
.spy(client
._sock
, 'close');
409 client
._updateState('connect');
410 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
414 describe('disconnect', function () {
416 beforeEach(function () {
417 this.clock
= sinon
.useFakeTimers();
419 client
.connect('host', 8675);
422 afterEach(function () {
423 this.clock
.restore();
426 it('should fail if we do not call Websock.onclose within the disconnection timeout', function () {
427 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
428 client
._updateState('disconnect');
429 this.clock
.tick(client
.get_disconnectTimeout() * 1000);
430 expect(client
._rfb_state
).to
.equal('failed');
433 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
434 client
._updateState('disconnect');
435 this.clock
.tick(client
.get_disconnectTimeout() * 500);
436 client
._sock
._websocket
.close();
437 this.clock
.tick(client
.get_disconnectTimeout() * 500 + 1);
438 expect(client
._rfb_state
).to
.equal('disconnected');
441 it('should close the WebSocket connection', function () {
442 sinon
.spy(client
._sock
, 'close');
443 client
._updateState('disconnect');
444 expect(client
._sock
.close
).to
.have
.been
.calledTwice
; // once on loaded, once on disconnect
448 describe('failed', function () {
450 beforeEach(function () {
451 this.clock
= sinon
.useFakeTimers();
453 client
.connect('host', 8675);
456 afterEach(function () {
457 this.clock
.restore();
460 it('should close the WebSocket connection', function () {
461 sinon
.spy(client
._sock
, 'close');
462 client
._updateState('failed');
463 expect(client
._sock
.close
).to
.have
.been
.called
;
466 it('should transition to disconnected but stay in failed state', function () {
467 client
.set_onUpdateState(sinon
.spy());
468 client
._updateState('failed');
470 expect(client
._rfb_state
).to
.equal('failed');
472 var onUpdateState
= client
.get_onUpdateState();
473 expect(onUpdateState
).to
.have
.been
.called
;
474 // it should be specifically the last call
475 expect(onUpdateState
.args
[onUpdateState
.args
.length
- 1][1]).to
.equal('disconnected');
476 expect(onUpdateState
.args
[onUpdateState
.args
.length
- 1][2]).to
.equal('failed');
481 describe('fatal', function () {
483 beforeEach(function () { client
= make_rfb(); });
485 it('should close any open WebSocket connection', function () {
486 sinon
.spy(client
._sock
, 'close');
487 client
._updateState('fatal');
488 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
492 // NB(directxman12): Normal does *nothing* in updateState
495 describe('Protocol Initialization States', function () {
496 describe('ProtocolVersion', function () {
497 beforeEach(function () {
498 this.clock
= sinon
.useFakeTimers();
501 afterEach(function () {
502 this.clock
.restore();
505 function send_ver (ver
, client
) {
506 var arr
= new Uint8Array(12);
507 for (var i
= 0; i
< ver
.length
; i
++) {
508 arr
[i
+4] = ver
.charCodeAt(i
);
510 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
512 client
._sock
._websocket
._receive_data(arr
);
515 describe('version parsing', function () {
517 beforeEach(function () {
519 client
.connect('host', 8675);
520 client
._sock
._websocket
._open();
523 it('should interpret version 000.000 as a repeater', function () {
524 client
._repeaterID
= '\x01\x02\x03\x04\x05';
525 send_ver('000.000', client
);
526 expect(client
._rfb_version
).to
.equal(0);
528 var sent_data
= client
._sock
._websocket
._get_sent_data();
529 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
532 it('should interpret version 003.003 as version 3.3', function () {
533 send_ver('003.003', client
);
534 expect(client
._rfb_version
).to
.equal(3.3);
537 it('should interpret version 003.006 as version 3.3', function () {
538 send_ver('003.006', client
);
539 expect(client
._rfb_version
).to
.equal(3.3);
542 it('should interpret version 003.889 as version 3.3', function () {
543 send_ver('003.889', client
);
544 expect(client
._rfb_version
).to
.equal(3.3);
547 it('should interpret version 003.007 as version 3.7', function () {
548 send_ver('003.007', client
);
549 expect(client
._rfb_version
).to
.equal(3.7);
552 it('should interpret version 003.008 as version 3.8', function () {
553 send_ver('003.008', client
);
554 expect(client
._rfb_version
).to
.equal(3.8);
557 it('should interpret version 004.000 as version 3.8', function () {
558 send_ver('004.000', client
);
559 expect(client
._rfb_version
).to
.equal(3.8);
562 it('should interpret version 004.001 as version 3.8', function () {
563 send_ver('004.001', client
);
564 expect(client
._rfb_version
).to
.equal(3.8);
567 it('should fail on an invalid version', function () {
568 send_ver('002.000', client
);
569 expect(client
._rfb_state
).to
.equal('failed');
574 beforeEach(function () {
576 client
.connect('host', 8675);
577 client
._sock
._websocket
._open();
580 it('should handle two step repeater negotiation', function () {
581 client
._repeaterID
= '\x01\x02\x03\x04\x05';
583 send_ver('000.000', client
);
584 expect(client
._rfb_version
).to
.equal(0);
585 var sent_data
= client
._sock
._websocket
._get_sent_data();
586 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
587 expect(sent_data
).to
.have
.length(250);
589 send_ver('003.008', client
);
590 expect(client
._rfb_version
).to
.equal(3.8);
593 it('should send back the interpreted version', function () {
594 send_ver('004.000', client
);
596 var expected_str
= 'RFB 003.008\n';
598 for (var i
= 0; i
< expected_str
.length
; i
++) {
599 expected
[i
] = expected_str
.charCodeAt(i
);
602 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
605 it('should transition to the Security state on successful negotiation', function () {
606 send_ver('003.008', client
);
607 expect(client
._rfb_state
).to
.equal('Security');
611 describe('Security', function () {
614 beforeEach(function () {
616 client
.connect('host', 8675);
617 client
._sock
._websocket
._open();
618 client
._rfb_state
= 'Security';
621 it('should simply receive the auth scheme when for versions < 3.7', function () {
622 client
._rfb_version
= 3.6;
623 var auth_scheme_raw
= [1, 2, 3, 4];
624 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
625 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
626 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
627 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
630 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
631 client
._rfb_version
= 3.7;
632 var auth_schemes
= [2, 1, 2];
633 client
._sock
._websocket
._receive_data(auth_schemes
);
634 expect(client
._rfb_auth_scheme
).to
.equal(2);
635 expect(client
._sock
).to
.have
.sent(new Uint8Array([2]));
638 it('should fail if there are no supported schemes for versions >= 3.7', function () {
639 client
._rfb_version
= 3.7;
640 var auth_schemes
= [1, 32];
641 client
._sock
._websocket
._receive_data(auth_schemes
);
642 expect(client
._rfb_state
).to
.equal('failed');
645 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
646 client
._rfb_version
= 3.7;
647 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
648 sinon
.spy(client
, '_fail');
649 client
._sock
._websocket
._receive_data(failure_data
);
651 expect(client
._fail
).to
.have
.been
.calledTwice
;
652 expect(client
._fail
).to
.have
.been
.calledWith('Security failure: whoops');
655 it('should transition to the Authentication state and continue on successful negotiation', function () {
656 client
._rfb_version
= 3.7;
657 var auth_schemes
= [1, 1];
658 client
._negotiate_authentication
= sinon
.spy();
659 client
._sock
._websocket
._receive_data(auth_schemes
);
660 expect(client
._rfb_state
).to
.equal('Authentication');
661 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
665 describe('Authentication', function () {
668 beforeEach(function () {
670 client
.connect('host', 8675);
671 client
._sock
._websocket
._open();
672 client
._rfb_state
= 'Security';
675 function send_security(type
, cl
) {
676 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
679 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
680 client
._rfb_version
= 3.6;
681 var err_msg
= "Whoopsies";
682 var data
= [0, 0, 0, 0];
683 var err_len
= err_msg
.length
;
684 push32(data
, err_len
);
685 for (var i
= 0; i
< err_len
; i
++) {
686 data
.push(err_msg
.charCodeAt(i
));
689 sinon
.spy(client
, '_fail');
690 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
691 expect(client
._rfb_state
).to
.equal('failed');
692 expect(client
._fail
).to
.have
.been
.calledWith('Auth failure: Whoopsies');
695 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
696 client
._rfb_version
= 3.8;
697 send_security(1, client
);
698 expect(client
._rfb_state
).to
.equal('SecurityResult');
701 it('should transition straight to ClientInitialisation on "no auth" for versions < 3.8', function () {
702 client
._rfb_version
= 3.7;
703 sinon
.spy(client
, '_updateState');
704 send_security(1, client
);
705 expect(client
._updateState
).to
.have
.been
.calledWith('ClientInitialisation');
706 expect(client
._rfb_state
).to
.equal('ServerInitialisation');
709 it('should fail on an unknown auth scheme', function () {
710 client
._rfb_version
= 3.8;
711 send_security(57, client
);
712 expect(client
._rfb_state
).to
.equal('failed');
715 describe('VNC Authentication (type 2) Handler', function () {
718 beforeEach(function () {
720 client
.connect('host', 8675);
721 client
._sock
._websocket
._open();
722 client
._rfb_state
= 'Security';
723 client
._rfb_version
= 3.8;
726 it('should transition to the "password" state if missing a password', function () {
727 send_security(2, client
);
728 expect(client
._rfb_state
).to
.equal('password');
731 it('should encrypt the password with DES and then send it back', function () {
732 client
._rfb_password
= 'passwd';
733 send_security(2, client
);
734 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
737 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
738 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
740 var des_pass
= RFB
.genDES('passwd', challenge
);
741 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
744 it('should transition to SecurityResult immediately after sending the password', function () {
745 client
._rfb_password
= 'passwd';
746 send_security(2, client
);
749 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
750 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
752 expect(client
._rfb_state
).to
.equal('SecurityResult');
756 describe('XVP Authentication (type 22) Handler', function () {
759 beforeEach(function () {
761 client
.connect('host', 8675);
762 client
._sock
._websocket
._open();
763 client
._rfb_state
= 'Security';
764 client
._rfb_version
= 3.8;
767 it('should fall through to standard VNC authentication upon completion', function () {
768 client
.set_xvp_password_sep('#');
769 client
._rfb_password
= 'user#target#password';
770 client
._negotiate_std_vnc_auth
= sinon
.spy();
771 send_security(22, client
);
772 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
775 it('should transition to the "password" state if the passwords is missing', function() {
776 send_security(22, client
);
777 expect(client
._rfb_state
).to
.equal('password');
780 it('should transition to the "password" state if the passwords is improperly formatted', function() {
781 client
._rfb_password
= 'user@target';
782 send_security(22, client
);
783 expect(client
._rfb_state
).to
.equal('password');
786 it('should split the password, send the first two parts, and pass on the last part', function () {
787 client
.set_xvp_password_sep('#');
788 client
._rfb_password
= 'user#target#password';
789 client
._negotiate_std_vnc_auth
= sinon
.spy();
791 send_security(22, client
);
793 expect(client
._rfb_password
).to
.equal('password');
795 var expected
= [22, 4, 6]; // auth selection, len user, len target
796 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
798 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
802 describe('TightVNC Authentication (type 16) Handler', function () {
805 beforeEach(function () {
807 client
.connect('host', 8675);
808 client
._sock
._websocket
._open();
809 client
._rfb_state
= 'Security';
810 client
._rfb_version
= 3.8;
811 send_security(16, client
);
812 client
._sock
._websocket
._get_sent_data(); // skip the security reply
815 function send_num_str_pairs(pairs
, client
) {
816 var pairs_len
= pairs
.length
;
818 push32(data
, pairs_len
);
820 for (var i
= 0; i
< pairs_len
; i
++) {
821 push32(data
, pairs
[i
][0]);
823 for (j
= 0; j
< 4; j
++) {
824 data
.push(pairs
[i
][1].charCodeAt(j
));
826 for (j
= 0; j
< 8; j
++) {
827 data
.push(pairs
[i
][2].charCodeAt(j
));
831 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
834 it('should skip tunnel negotiation if no tunnels are requested', function () {
835 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
836 expect(client
._rfb_tightvnc
).to
.be
.true;
839 it('should fail if no supported tunnels are listed', function () {
840 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
841 expect(client
._rfb_state
).to
.equal('failed');
844 it('should choose the notunnel tunnel type', function () {
845 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
846 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
849 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
850 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
851 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
852 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
853 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
854 expect(client
._rfb_state
).to
.equal('SecurityResult');
857 /*it('should attempt to use VNC auth over no auth when possible', function () {
858 client._rfb_tightvnc = true;
859 client._negotiate_std_vnc_auth = sinon.spy();
860 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
861 expect(client._sock).to.have.sent([0, 0, 0, 1]);
862 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
863 expect(client._rfb_auth_scheme).to.equal(2);
864 });*/ // while this would make sense, the original code doesn't actually do this
866 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
867 client
._rfb_tightvnc
= true;
868 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
869 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
870 expect(client
._rfb_state
).to
.equal('SecurityResult');
873 it('should accept VNC authentication and transition to that', function () {
874 client
._rfb_tightvnc
= true;
875 client
._negotiate_std_vnc_auth
= sinon
.spy();
876 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
877 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
878 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
879 expect(client
._rfb_auth_scheme
).to
.equal(2);
882 it('should fail if there are no supported auth types', function () {
883 client
._rfb_tightvnc
= true;
884 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
885 expect(client
._rfb_state
).to
.equal('failed');
890 describe('SecurityResult', function () {
893 beforeEach(function () {
895 client
.connect('host', 8675);
896 client
._sock
._websocket
._open();
897 client
._rfb_state
= 'SecurityResult';
900 it('should fall through to ClientInitialisation on a response code of 0', function () {
901 client
._updateState
= sinon
.spy();
902 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
903 expect(client
._updateState
).to
.have
.been
.calledOnce
;
904 expect(client
._updateState
).to
.have
.been
.calledWith('ClientInitialisation');
907 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
908 client
._rfb_version
= 3.8;
909 sinon
.spy(client
, '_fail');
910 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
911 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
912 expect(client
._rfb_state
).to
.equal('failed');
913 expect(client
._fail
).to
.have
.been
.calledWith('whoops');
916 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
917 client
._rfb_version
= 3.7;
918 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
919 expect(client
._rfb_state
).to
.equal('failed');
923 describe('ClientInitialisation', function () {
926 beforeEach(function () {
928 client
.connect('host', 8675);
929 client
._sock
._websocket
._open();
930 client
._rfb_state
= 'SecurityResult';
933 it('should transition to the ServerInitialisation state', function () {
934 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
935 expect(client
._rfb_state
).to
.equal('ServerInitialisation');
938 it('should send 1 if we are in shared mode', function () {
939 client
.set_shared(true);
940 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
941 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
944 it('should send 0 if we are not in shared mode', function () {
945 client
.set_shared(false);
946 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
947 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
951 describe('ServerInitialisation', function () {
954 beforeEach(function () {
956 client
.connect('host', 8675);
957 client
._sock
._websocket
._open();
958 client
._rfb_state
= 'ServerInitialisation';
961 function send_server_init(opts
, client
) {
962 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
963 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
964 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
965 for (var opt
in opts
) {
966 full_opts
[opt
] = opts
[opt
];
970 push16(data
, full_opts
.width
);
971 push16(data
, full_opts
.height
);
973 data
.push(full_opts
.bpp
);
974 data
.push(full_opts
.depth
);
975 data
.push(full_opts
.big_endian
);
976 data
.push(full_opts
.true_color
);
978 push16(data
, full_opts
.red_max
);
979 push16(data
, full_opts
.green_max
);
980 push16(data
, full_opts
.blue_max
);
981 push8(data
, full_opts
.red_shift
);
982 push8(data
, full_opts
.green_shift
);
983 push8(data
, full_opts
.blue_shift
);
990 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
993 push32(name_data
, full_opts
.name
.length
);
994 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
995 name_data
.push(full_opts
.name
.charCodeAt(i
));
997 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1000 it('should set the framebuffer width and height', function () {
1001 send_server_init({ width
: 32, height
: 84 }, client
);
1002 expect(client
._fb_width
).to
.equal(32);
1003 expect(client
._fb_height
).to
.equal(84);
1006 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1008 it('should set the framebuffer name and call the callback', function () {
1009 client
.set_onDesktopName(sinon
.spy());
1010 send_server_init({ name
: 'some name' }, client
);
1012 var spy
= client
.get_onDesktopName();
1013 expect(client
._fb_name
).to
.equal('some name');
1014 expect(spy
).to
.have
.been
.calledOnce
;
1015 expect(spy
.args
[0][1]).to
.equal('some name');
1018 it('should handle the extended init message of the tight encoding', function () {
1019 // NB(sross): we don't actually do anything with it, so just test that we can
1020 // read it w/o throwing an error
1021 client
._rfb_tightvnc
= true;
1022 send_server_init({}, client
);
1024 var tight_data
= [];
1025 push16(tight_data
, 1);
1026 push16(tight_data
, 2);
1027 push16(tight_data
, 3);
1028 push16(tight_data
, 0);
1029 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1032 client
._sock
._websocket
._receive_data(tight_data
);
1034 expect(client
._rfb_state
).to
.equal('normal');
1037 it('should set the true color mode on the display to the configuration variable', function () {
1038 client
.set_true_color(false);
1039 sinon
.spy(client
._display
, 'set_true_color');
1040 send_server_init({ true_color
: 1 }, client
);
1041 expect(client
._display
.set_true_color
).to
.have
.been
.calledOnce
;
1042 expect(client
._display
.set_true_color
).to
.have
.been
.calledWith(false);
1045 it('should call the resize callback and resize the display', function () {
1046 client
.set_onFBResize(sinon
.spy());
1047 sinon
.spy(client
._display
, 'resize');
1048 send_server_init({ width
: 27, height
: 32 }, client
);
1050 var spy
= client
.get_onFBResize();
1051 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1052 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1053 expect(spy
).to
.have
.been
.calledOnce
;
1054 expect(spy
.args
[0][1]).to
.equal(27);
1055 expect(spy
.args
[0][2]).to
.equal(32);
1058 it('should grab the mouse and keyboard', function () {
1059 sinon
.spy(client
._keyboard
, 'grab');
1060 sinon
.spy(client
._mouse
, 'grab');
1061 send_server_init({}, client
);
1062 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1063 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1066 it('should set the BPP and depth to 4 and 3 respectively if in true color mode', function () {
1067 client
.set_true_color(true);
1068 send_server_init({}, client
);
1069 expect(client
._fb_Bpp
).to
.equal(4);
1070 expect(client
._fb_depth
).to
.equal(3);
1073 it('should set the BPP and depth to 1 and 1 respectively if not in true color mode', function () {
1074 client
.set_true_color(false);
1075 send_server_init({}, client
);
1076 expect(client
._fb_Bpp
).to
.equal(1);
1077 expect(client
._fb_depth
).to
.equal(1);
1080 // TODO(directxman12): test the various options in this configuration matrix
1081 it('should reply with the pixel format, client encodings, and initial update request', function () {
1082 client
.set_true_color(true);
1083 client
.set_local_cursor(false);
1084 // we skip the cursor encoding
1085 var expected
= {_sQ
: new Uint8Array(34 + 4 * (client
._encodings
.length
- 1)),
1087 flush: function () {}};
1088 RFB
.messages
.pixelFormat(expected
, 4, 3, true);
1089 RFB
.messages
.clientEncodings(expected
, client
._encodings
, false, true);
1090 RFB
.messages
.fbUpdateRequest(expected
, false, 0, 0, 27, 32);
1092 send_server_init({ width
: 27, height
: 32 }, client
);
1093 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
1096 it('should transition to the "normal" state', function () {
1097 send_server_init({}, client
);
1098 expect(client
._rfb_state
).to
.equal('normal');
1103 describe('Protocol Message Processing After Completing Initialization', function () {
1106 beforeEach(function () {
1107 client
= make_rfb();
1108 client
.connect('host', 8675);
1109 client
._sock
._websocket
._open();
1110 client
._rfb_state
= 'normal';
1111 client
._fb_name
= 'some device';
1112 client
._fb_width
= 640;
1113 client
._fb_height
= 20;
1116 describe('Framebuffer Update Handling', function () {
1119 beforeEach(function () {
1120 client
= make_rfb();
1121 client
.connect('host', 8675);
1122 client
._sock
._websocket
._open();
1123 client
._rfb_state
= 'normal';
1124 client
._fb_name
= 'some device';
1125 client
._fb_width
= 640;
1126 client
._fb_height
= 20;
1129 var target_data_arr
= [
1130 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1131 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1132 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1133 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1137 var target_data_check_arr
= [
1138 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1139 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1140 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1141 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1143 var target_data_check
;
1145 before(function () {
1146 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1147 target_data
= new Uint8Array(target_data_arr
);
1148 target_data_check
= new Uint8Array(target_data_check_arr
);
1151 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1154 if (!rect_cnt
|| rect_cnt
> -1) {
1156 data
.push(0); // msg type
1157 data
.push(0); // padding
1158 push16(data
, rect_cnt
|| rect_data
.length
);
1161 for (var i
= 0; i
< rect_data
.length
; i
++) {
1163 push16(data
, rect_info
[i
].x
);
1164 push16(data
, rect_info
[i
].y
);
1165 push16(data
, rect_info
[i
].width
);
1166 push16(data
, rect_info
[i
].height
);
1167 push32(data
, rect_info
[i
].encoding
);
1169 data
= data
.concat(rect_data
[i
]);
1172 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1175 it('should send an update request if there is sufficient data', function () {
1176 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1177 RFB
.messages
.fbUpdateRequest(expected_msg
, false, 0, 0, 240, 20);
1179 client
._framebufferUpdate = function () { return true; };
1180 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1182 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1185 it('should not send an update request if we need more data', function () {
1186 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1187 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1190 it('should resume receiving an update if we previously did not have enough data', function () {
1191 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1192 RFB
.messages
.fbUpdateRequest(expected_msg
, false, 0, 0, 240, 20);
1194 // just enough to set FBU.rects
1195 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1196 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1198 client
._framebufferUpdate = function () { return true; }; // we magically have enough data
1199 // 247 should *not* be used as the message type here
1200 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1201 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1204 it('should send a request for both clean and dirty areas', function () {
1205 var expected_msg
= {_sQ
: new Uint8Array(20), _sQlen
: 0, flush: function() {}};
1206 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 120, h
: 20 },
1207 dirtyBoxes
: [ { x
: 120, y
: 0, w
: 120, h
: 20 } ] };
1209 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 120, 20);
1210 RFB
.messages
.fbUpdateRequest(expected_msg
, false, 120, 0, 120, 20);
1212 client
._framebufferUpdate = function () { return true; };
1213 client
._display
.getCleanDirtyReset = function () { return expected_cdr
; };
1214 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1216 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1219 it('should only request non-incremental rects in continuous updates mode', function () {
1220 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1221 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 120, h
: 20 },
1222 dirtyBoxes
: [ { x
: 120, y
: 0, w
: 120, h
: 20 } ] };
1224 RFB
.messages
.fbUpdateRequest(expected_msg
, false, 120, 0, 120, 20);
1226 client
._enabledContinuousUpdates
= true;
1227 client
._framebufferUpdate = function () { return true; };
1228 client
._display
.getCleanDirtyReset = function () { return expected_cdr
; };
1229 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1231 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1234 it('should not send a request in continuous updates mode when clean', function () {
1235 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 240, h
: 20 },
1238 client
._enabledContinuousUpdates
= true;
1239 client
._framebufferUpdate = function () { return true; };
1240 client
._display
.getCleanDirtyReset = function () { return expected_cdr
; };
1241 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1243 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1246 it('should parse out information from a header before any actual data comes in', function () {
1247 client
.set_onFBUReceive(sinon
.spy());
1248 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02, encodingName
: 'RRE' };
1249 send_fbu_msg([rect_info
], [[]], client
);
1251 var spy
= client
.get_onFBUReceive();
1252 expect(spy
).to
.have
.been
.calledOnce
;
1253 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1256 it('should fire onFBUComplete when the update is complete', function () {
1257 client
.set_onFBUComplete(sinon
.spy());
1258 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: -224, encodingName
: 'last_rect' };
1259 send_fbu_msg([rect_info
], [[]], client
); // last_rect
1261 var spy
= client
.get_onFBUComplete();
1262 expect(spy
).to
.have
.been
.calledOnce
;
1263 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1266 it('should not fire onFBUComplete if we have not finished processing the update', function () {
1267 client
.set_onFBUComplete(sinon
.spy());
1268 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x00, encodingName
: 'RAW' };
1269 send_fbu_msg([rect_info
], [[]], client
);
1270 expect(client
.get_onFBUComplete()).to
.not
.have
.been
.called
;
1273 it('should call the appropriate encoding handler', function () {
1274 client
._encHandlers
[0x02] = sinon
.spy();
1275 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02 };
1276 send_fbu_msg([rect_info
], [[]], client
);
1277 expect(client
._encHandlers
[0x02]).to
.have
.been
.calledOnce
;
1280 it('should fail on an unsupported encoding', function () {
1281 client
.set_onFBUReceive(sinon
.spy());
1282 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1283 send_fbu_msg([rect_info
], [[]], client
);
1284 expect(client
._rfb_state
).to
.equal('failed');
1287 it('should be able to pause and resume receiving rects if not enought data', function () {
1288 // seed some initial data to copy
1289 client
._fb_width
= 4;
1290 client
._fb_height
= 4;
1291 client
._display
.resize(4, 4);
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
[0]], [rects
[0]], client
, 2);
1302 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1303 expect(client
._display
).to
.have
.displayed(target_data_check
);
1306 describe('Message Encoding Handlers', function () {
1309 beforeEach(function () {
1310 client
= make_rfb();
1311 client
.connect('host', 8675);
1312 client
._sock
._websocket
._open();
1313 client
._rfb_state
= 'normal';
1314 client
._fb_name
= 'some device';
1315 // a really small frame
1316 client
._fb_width
= 4;
1317 client
._fb_height
= 4;
1318 client
._display
._fb_width
= 4;
1319 client
._display
._fb_height
= 4;
1320 client
._display
._viewportLoc
.w
= 4;
1321 client
._display
._viewportLoc
.h
= 4;
1325 it('should handle the RAW encoding', function () {
1326 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1327 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1328 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1329 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1332 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1333 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1334 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1335 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1336 send_fbu_msg(info
, rects
, client
);
1337 expect(client
._display
).to
.have
.displayed(target_data
);
1340 it('should handle the COPYRECT encoding', function () {
1341 // seed some initial data to copy
1342 var initial_data
= client
._display
._drawCtx
.createImageData(4, 2);
1343 var initial_data_arr
= target_data_check_arr
.slice(0, 32);
1344 for (var i
= 0; i
< 32; i
++) { initial_data
.data
[i
] = initial_data_arr
[i
]; }
1345 client
._display
._drawCtx
.putImageData(initial_data
, 0, 0);
1347 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1348 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1349 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1350 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1351 send_fbu_msg(info
, rects
, client
);
1352 expect(client
._display
).to
.have
.displayed(target_data_check
);
1355 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1356 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1358 it('should handle the RRE encoding', function () {
1359 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1361 push32(rect
, 2); // 2 subrects
1362 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1363 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1367 push16(rect
, 0); // x: 0
1368 push16(rect
, 0); // y: 0
1369 push16(rect
, 2); // width: 2
1370 push16(rect
, 2); // height: 2
1371 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1375 push16(rect
, 2); // x: 2
1376 push16(rect
, 2); // y: 2
1377 push16(rect
, 2); // width: 2
1378 push16(rect
, 2); // height: 2
1380 send_fbu_msg(info
, [rect
], client
);
1381 expect(client
._display
).to
.have
.displayed(target_data_check
);
1384 describe('the HEXTILE encoding handler', function () {
1386 beforeEach(function () {
1387 client
= make_rfb();
1388 client
.connect('host', 8675);
1389 client
._sock
._websocket
._open();
1390 client
._rfb_state
= 'normal';
1391 client
._fb_name
= 'some device';
1392 // a really small frame
1393 client
._fb_width
= 4;
1394 client
._fb_height
= 4;
1395 client
._display
._fb_width
= 4;
1396 client
._display
._fb_height
= 4;
1397 client
._display
._viewportLoc
.w
= 4;
1398 client
._display
._viewportLoc
.h
= 4;
1402 it('should handle a tile with fg, bg specified, normal subrects', function () {
1403 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1405 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1406 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1407 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1411 rect
.push(2); // 2 subrects
1412 rect
.push(0); // x: 0, y: 0
1413 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1414 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1415 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1416 send_fbu_msg(info
, [rect
], client
);
1417 expect(client
._display
).to
.have
.displayed(target_data_check
);
1420 it('should handle a raw tile', function () {
1421 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1423 rect
.push(0x01); // raw
1424 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1425 rect
.push(target_data
[i
+ 2]);
1426 rect
.push(target_data
[i
+ 1]);
1427 rect
.push(target_data
[i
]);
1428 rect
.push(target_data
[i
+ 3]);
1430 send_fbu_msg(info
, [rect
], client
);
1431 expect(client
._display
).to
.have
.displayed(target_data
);
1434 it('should handle a tile with only bg specified (solid bg)', function () {
1435 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1438 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1439 send_fbu_msg(info
, [rect
], client
);
1442 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1443 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1446 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1447 // set the width so we can have two tiles
1448 client
._fb_width
= 8;
1449 client
._display
._fb_width
= 8;
1450 client
._display
._viewportLoc
.w
= 8;
1452 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1458 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1460 // send an empty frame
1463 send_fbu_msg(info
, [rect
], client
);
1467 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1468 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1469 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1472 it('should handle a tile with bg and coloured subrects', function () {
1473 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1475 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1476 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1477 rect
.push(2); // 2 subrects
1478 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1482 rect
.push(0); // x: 0, y: 0
1483 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1484 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1488 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1489 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1490 send_fbu_msg(info
, [rect
], client
);
1491 expect(client
._display
).to
.have
.displayed(target_data_check
);
1494 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1495 client
._fb_width
= 4;
1496 client
._fb_height
= 17;
1497 client
._display
.resize(4, 17);
1499 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1501 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1502 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1503 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1507 rect
.push(8); // 8 subrects
1509 for (i
= 0; i
< 4; i
++) {
1510 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1511 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1512 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1513 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1515 rect
.push(0x08); // anysubrects
1516 rect
.push(1); // 1 subrect
1517 rect
.push(0); // x: 0, y: 0
1518 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1519 send_fbu_msg(info
, [rect
], client
);
1522 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1523 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1524 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1527 it('should fail on an invalid subencoding', function () {
1528 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1529 var rects
= [[45]]; // an invalid subencoding
1530 send_fbu_msg(info
, rects
, client
);
1531 expect(client
._rfb_state
).to
.equal('failed');
1535 it
.skip('should handle the TIGHT encoding', function () {
1536 // TODO(directxman12): test this
1539 it
.skip('should handle the TIGHT_PNG encoding', function () {
1540 // TODO(directxman12): test this
1543 it('should handle the DesktopSize pseduo-encoding', function () {
1544 client
.set_onFBResize(sinon
.spy());
1545 sinon
.spy(client
._display
, 'resize');
1546 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1548 var spy
= client
.get_onFBResize();
1549 expect(spy
).to
.have
.been
.calledOnce
;
1550 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1552 expect(client
._fb_width
).to
.equal(20);
1553 expect(client
._fb_height
).to
.equal(50);
1555 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1556 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1559 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1562 beforeEach(function () {
1563 client
= make_rfb();
1564 client
.connect('host', 8675);
1565 client
._sock
._websocket
._open();
1566 client
._rfb_state
= 'normal';
1567 client
._fb_name
= 'some device';
1568 client
._supportsSetDesktopSize
= false;
1569 // a really small frame
1570 client
._fb_width
= 4;
1571 client
._fb_height
= 4;
1572 client
._display
._fb_width
= 4;
1573 client
._display
._fb_height
= 4;
1574 client
._display
._viewportLoc
.w
= 4;
1575 client
._display
._viewportLoc
.h
= 4;
1577 sinon
.spy(client
._display
, 'resize');
1578 client
.set_onFBResize(sinon
.spy());
1581 function make_screen_data (nr_of_screens
) {
1583 push8(data
, nr_of_screens
); // number-of-screens
1584 push8(data
, 0); // padding
1585 push16(data
, 0); // padding
1586 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1587 push32(data
, 0); // id
1588 push16(data
, 0); // x-position
1589 push16(data
, 0); // y-position
1590 push16(data
, 20); // width
1591 push16(data
, 50); // height
1592 push32(data
, 0); // flags
1597 it('should handle a resize requested by this client', function () {
1598 var reason_for_change
= 1; // requested by this client
1599 var status_code
= 0; // No error
1601 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1602 width
: 20, height
: 50, encoding
: -308 }],
1603 make_screen_data(1), client
);
1605 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1606 expect(client
._fb_width
).to
.equal(20);
1607 expect(client
._fb_height
).to
.equal(50);
1609 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1610 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1612 var spy
= client
.get_onFBResize();
1613 expect(spy
).to
.have
.been
.calledOnce
;
1614 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1617 it('should handle a resize requested by another client', function () {
1618 var reason_for_change
= 2; // requested by another client
1619 var status_code
= 0; // No error
1621 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1622 width
: 20, height
: 50, encoding
: -308 }],
1623 make_screen_data(1), client
);
1625 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1626 expect(client
._fb_width
).to
.equal(20);
1627 expect(client
._fb_height
).to
.equal(50);
1629 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1630 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1632 var spy
= client
.get_onFBResize();
1633 expect(spy
).to
.have
.been
.calledOnce
;
1634 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1637 it('should be able to recieve requests which contain data for multiple screens', function () {
1638 var reason_for_change
= 2; // requested by another client
1639 var status_code
= 0; // No error
1641 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1642 width
: 60, height
: 50, encoding
: -308 }],
1643 make_screen_data(3), client
);
1645 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1646 expect(client
._fb_width
).to
.equal(60);
1647 expect(client
._fb_height
).to
.equal(50);
1649 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1650 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1652 var spy
= client
.get_onFBResize();
1653 expect(spy
).to
.have
.been
.calledOnce
;
1654 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 60, 50);
1657 it('should not handle a failed request', function () {
1658 var reason_for_change
= 1; // requested by this client
1659 var status_code
= 1; // Resize is administratively prohibited
1661 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1662 width
: 20, height
: 50, encoding
: -308 }],
1663 make_screen_data(1), client
);
1665 expect(client
._fb_width
).to
.equal(4);
1666 expect(client
._fb_height
).to
.equal(4);
1668 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1670 var spy
= client
.get_onFBResize();
1671 expect(spy
).to
.not
.have
.been
.called
;
1675 it
.skip('should handle the Cursor pseudo-encoding', function () {
1676 // TODO(directxman12): test
1679 it('should handle the last_rect pseudo-encoding', function () {
1680 client
.set_onFBUReceive(sinon
.spy());
1681 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1682 expect(client
._FBU
.rects
).to
.equal(0);
1683 expect(client
.get_onFBUReceive()).to
.have
.been
.calledOnce
;
1688 it('should set the colour map on the display on SetColourMapEntries', function () {
1689 var expected_cm
= [];
1690 var data
= [1, 0, 0, 1, 0, 4];
1692 for (i
= 0; i
< 4; i
++) {
1693 expected_cm
[i
+ 1] = [i
* 10, i
* 10 + 1, i
* 10 + 2];
1694 push16(data
, expected_cm
[i
+ 1][2] << 8);
1695 push16(data
, expected_cm
[i
+ 1][1] << 8);
1696 push16(data
, expected_cm
[i
+ 1][0] << 8);
1699 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1700 expect(client
._display
.get_colourMap()).to
.deep
.equal(expected_cm
);
1703 describe('XVP Message Handling', function () {
1704 beforeEach(function () {
1705 client
= make_rfb();
1706 client
.connect('host', 8675);
1707 client
._sock
._websocket
._open();
1708 client
._rfb_state
= 'normal';
1709 client
._fb_name
= 'some device';
1710 client
._fb_width
= 27;
1711 client
._fb_height
= 32;
1714 it('should call updateState with a message on XVP_FAIL, but keep the same state', function () {
1715 client
._updateState
= sinon
.spy();
1716 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 0]));
1717 expect(client
._updateState
).to
.have
.been
.calledOnce
;
1718 expect(client
._updateState
).to
.have
.been
.calledWith('normal', 'Operation Failed');
1721 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1722 client
.set_onXvpInit(sinon
.spy());
1723 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1724 expect(client
._rfb_xvp_ver
).to
.equal(10);
1725 expect(client
.get_onXvpInit()).to
.have
.been
.calledOnce
;
1726 expect(client
.get_onXvpInit()).to
.have
.been
.calledWith(10);
1729 it('should fail on unknown XVP message types', function () {
1730 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1731 expect(client
._rfb_state
).to
.equal('failed');
1735 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1736 var expected_str
= 'cheese!';
1737 var data
= [3, 0, 0, 0];
1738 push32(data
, expected_str
.length
);
1739 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1740 client
.set_onClipboard(sinon
.spy());
1742 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1743 var spy
= client
.get_onClipboard();
1744 expect(spy
).to
.have
.been
.calledOnce
;
1745 expect(spy
.args
[0][1]).to
.equal(expected_str
);
1748 it('should fire the bell callback on Bell', function () {
1749 client
.set_onBell(sinon
.spy());
1750 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1751 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1754 it('should respond correctly to ServerFence', function () {
1755 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1756 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1758 var payload
= "foo\x00ab9";
1760 // ClientFence and ServerFence are identical in structure
1761 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1762 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1764 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1766 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1768 expected_msg
._sQlen
= 0;
1769 incoming_msg
._sQlen
= 0;
1771 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1772 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1774 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1776 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1779 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1780 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1782 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1784 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1786 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1788 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1789 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1792 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1793 client
._enabledContinuousUpdates
= true;
1794 client
._supportsContinuousUpdates
= true;
1796 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1798 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1801 it('should update continuous updates on resize', function () {
1802 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1803 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1805 client
._FBU
.width
= 450;
1806 client
._FBU
.height
= 160;
1808 client
._encHandlers
.handle_FB_resize();
1810 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1812 client
._enabledContinuousUpdates
= true;
1814 client
._FBU
.width
= 90;
1815 client
._FBU
.height
= 700;
1817 client
._encHandlers
.handle_FB_resize();
1819 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1822 it('should fail on an unknown message type', function () {
1823 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1824 expect(client
._rfb_state
).to
.equal('failed');
1828 describe('Asynchronous Events', function () {
1829 describe('Mouse event handlers', function () {
1831 beforeEach(function () {
1832 client
= make_rfb();
1833 client
._sock
= new Websock();
1834 client
._sock
.open('ws://', 'binary');
1835 client
._sock
._websocket
._open();
1836 sinon
.spy(client
._sock
, 'flush');
1837 client
._rfb_state
= 'normal';
1840 it('should not send button messages in view-only mode', function () {
1841 client
._view_only
= true;
1842 client
._mouse
._onMouseButton(0, 0, 1, 0x001);
1843 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1846 it('should not send movement messages in view-only mode', function () {
1847 client
._view_only
= true;
1848 client
._mouse
._onMouseMove(0, 0);
1849 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1852 it('should send a pointer event on mouse button presses', function () {
1853 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1854 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1855 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1856 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1859 it('should send a mask of 1 on mousedown', function () {
1860 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1861 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1862 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1863 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1866 it('should send a mask of 0 on mouseup', function () {
1867 client
._mouse_buttonMask
= 0x001;
1868 client
._mouse
._onMouseButton(10, 12, 0, 0x001);
1869 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1870 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1871 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1874 it('should send a pointer event on mouse movement', function () {
1875 client
._mouse
._onMouseMove(10, 12);
1876 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1877 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1878 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1881 it('should set the button mask so that future mouse movements use it', function () {
1882 client
._mouse
._onMouseButton(10, 12, 1, 0x010);
1883 client
._mouse
._onMouseMove(13, 9);
1884 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1885 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1886 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1887 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1890 // NB(directxman12): we don't need to test not sending messages in
1891 // non-normal modes, since we haven't grabbed input
1892 // yet (grabbing input should be checked in the lifecycle tests).
1894 it('should not send movement messages when viewport dragging', function () {
1895 client
._viewportDragging
= true;
1896 client
._display
.viewportChangePos
= sinon
.spy();
1897 client
._mouse
._onMouseMove(13, 9);
1898 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1901 it('should not send button messages when initiating viewport dragging', function () {
1902 client
._viewportDrag
= true;
1903 client
._mouse
._onMouseButton(13, 9, 0x001);
1904 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1907 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1908 client
._viewportDrag
= true;
1909 client
._mouse
._onMouseButton(13, 9, 0x001);
1910 expect(client
._viewportDragging
).to
.be
.true;
1911 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1914 it('should terminate viewport dragging on a button up event, if enabled', function () {
1915 client
._viewportDrag
= true;
1916 client
._viewportDragging
= true;
1917 client
._mouse
._onMouseButton(13, 9, 0x000);
1918 expect(client
._viewportDragging
).to
.be
.false;
1921 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1922 client
._viewportDrag
= true;
1923 client
._viewportDragging
= true;
1924 client
._viewportHasMoved
= false;
1925 client
._viewportDragPos
= { x
: 23, y
: 9 };
1926 client
._display
.viewportChangePos
= sinon
.spy();
1928 client
._mouse
._onMouseMove(10, 4);
1930 expect(client
._viewportDragging
).to
.be
.true;
1931 expect(client
._viewportHasMoved
).to
.be
.true;
1932 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 10, y
: 4 });
1933 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
1934 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(13, 5);
1938 describe('Keyboard Event Handlers', function () {
1940 beforeEach(function () {
1941 client
= make_rfb();
1942 client
._sock
= new Websock();
1943 client
._sock
.open('ws://', 'binary');
1944 client
._sock
._websocket
._open();
1945 sinon
.spy(client
._sock
, 'flush');
1948 it('should send a key message on a key press', function () {
1950 keyevent
.type
= 'keydown';
1951 keyevent
.keysym
= {};
1952 keyevent
.keysym
.keysym
= 1234;
1953 client
._keyboard
._onKeyPress(keyevent
);
1954 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
1955 RFB
.messages
.keyEvent(key_msg
, 1234, 1);
1956 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
1959 it('should not send messages in view-only mode', function () {
1960 client
._view_only
= true;
1961 client
._keyboard
._onKeyPress(1234, 1);
1962 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1966 describe('WebSocket event handlers', function () {
1968 beforeEach(function () {
1969 client
= make_rfb();
1970 this.clock
= sinon
.useFakeTimers();
1973 afterEach(function () { this.clock
.restore(); });
1976 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
1977 client
.connect('host', 8675);
1978 client
._rfb_state
= 'normal';
1979 client
._normal_msg
= sinon
.spy();
1980 client
._sock
._websocket
._receive_data(new Uint8Array([]));
1981 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
1984 it('should handle a message in the normal state as a normal message', function () {
1985 client
.connect('host', 8675);
1986 client
._rfb_state
= 'normal';
1987 client
._normal_msg
= sinon
.spy();
1988 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1989 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
1992 it('should handle a message in any non-disconnected/failed state like an init message', function () {
1993 client
.connect('host', 8675);
1994 client
._rfb_state
= 'ProtocolVersion';
1995 client
._init_msg
= sinon
.spy();
1996 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1997 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
2000 it('should split up the handling of muplitle normal messages across 10ms intervals', function () {
2001 client
.connect('host', 8675);
2002 client
._sock
._websocket
._open();
2003 client
._rfb_state
= 'normal';
2004 client
.set_onBell(sinon
.spy());
2005 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2006 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
2007 this.clock
.tick(20);
2008 expect(client
.get_onBell()).to
.have
.been
.calledTwice
;
2012 it('should update the state to ProtocolVersion on open (if the state is "connect")', function () {
2013 client
.connect('host', 8675);
2014 client
._sock
._websocket
._open();
2015 expect(client
._rfb_state
).to
.equal('ProtocolVersion');
2018 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2019 client
.connect('host', 8675);
2020 client
._rfb_state
= 'some_other_state';
2021 client
._sock
._websocket
._open();
2022 expect(client
._rfb_state
).to
.equal('failed');
2026 it('should transition to "disconnected" from "disconnect" on a close event', function () {
2027 client
.connect('host', 8675);
2028 client
._rfb_state
= 'disconnect';
2029 client
._sock
._websocket
.close();
2030 expect(client
._rfb_state
).to
.equal('disconnected');
2033 it('should transition to failed if we get a close event from any non-"disconnection" state', function () {
2034 client
.connect('host', 8675);
2035 client
._rfb_state
= 'normal';
2036 client
._sock
._websocket
.close();
2037 expect(client
._rfb_state
).to
.equal('failed');
2040 it('should unregister close event handler', function () {
2041 sinon
.spy(client
._sock
, 'off');
2042 client
.connect('host', 8675);
2043 client
._rfb_state
= 'disconnect';
2044 client
._sock
._websocket
.close();
2045 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2048 // error events do nothing