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
._updateConnectionState
= sinon
.spy(); });
70 it('should set the current state to "connecting"', function () {
71 client
.connect('host', 8675);
72 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
73 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('connecting');
76 it('should not try to connect if we are missing a host', function () {
77 client
._fail
= sinon
.spy();
78 client
._rfb_connection_state
= '';
79 client
.connect(undefined, 8675);
80 expect(client
._fail
).to
.have
.been
.calledOnce
;
81 expect(client
._updateConnectionState
).to
.not
.have
.been
.called
;
82 expect(client
._rfb_connection_state
).to
.equal('');
85 it('should not try to connect if we are missing a port', function () {
86 client
._fail
= sinon
.spy();
87 client
._rfb_connection_state
= '';
88 client
.connect('abc');
89 expect(client
._fail
).to
.have
.been
.calledOnce
;
90 expect(client
._updateConnectionState
).to
.not
.have
.been
.called
;
91 expect(client
._rfb_connection_state
).to
.equal('');
95 describe('#disconnect', function () {
96 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
98 it('should set the current state to "disconnecting"', function () {
100 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
101 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('disconnecting');
104 it('should unregister error event handler', function () {
105 sinon
.spy(client
._sock
, 'off');
107 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
110 it('should unregister message event handler', function () {
111 sinon
.spy(client
._sock
, 'off');
113 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
116 it('should unregister open event handler', function () {
117 sinon
.spy(client
._sock
, 'off');
119 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
123 describe('#sendPassword', function () {
124 beforeEach(function () { this.clock
= sinon
.useFakeTimers(); });
125 afterEach(function () { this.clock
.restore(); });
127 it('should set the rfb password properly"', function () {
128 client
.sendPassword('pass');
129 expect(client
._rfb_password
).to
.equal('pass');
132 it('should call init_msg "soon"', function () {
133 client
._init_msg
= sinon
.spy();
134 client
.sendPassword('pass');
136 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
140 describe('#sendCtrlAlDel', function () {
141 beforeEach(function () {
142 client
._sock
= new Websock();
143 client
._sock
.open('ws://', 'binary');
144 client
._sock
._websocket
._open();
145 sinon
.spy(client
._sock
, 'flush');
146 client
._rfb_connection_state
= 'connected';
147 client
._view_only
= false;
150 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
151 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
152 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
153 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
154 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
155 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
156 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
157 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
159 client
.sendCtrlAltDel();
160 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
163 it('should not send the keys if we are not in a normal state', function () {
164 client
._rfb_connection_state
= "broken";
165 client
.sendCtrlAltDel();
166 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
169 it('should not send the keys if we are set as view_only', function () {
170 client
._view_only
= true;
171 client
.sendCtrlAltDel();
172 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
176 describe('#sendKey', function () {
177 beforeEach(function () {
178 client
._sock
= new Websock();
179 client
._sock
.open('ws://', 'binary');
180 client
._sock
._websocket
._open();
181 sinon
.spy(client
._sock
, 'flush');
182 client
._rfb_connection_state
= 'connected';
183 client
._view_only
= false;
186 it('should send a single key with the given code and state (down = true)', function () {
187 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
188 RFB
.messages
.keyEvent(expected
, 123, 1);
189 client
.sendKey(123, true);
190 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
193 it('should send both a down and up event if the state is not specified', function () {
194 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
195 RFB
.messages
.keyEvent(expected
, 123, 1);
196 RFB
.messages
.keyEvent(expected
, 123, 0);
198 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
201 it('should not send the key if we are not in a normal state', function () {
202 client
._rfb_connection_state
= "broken";
204 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
207 it('should not send the key if we are set as view_only', function () {
208 client
._view_only
= true;
210 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
214 describe('#clipboardPasteFrom', function () {
215 beforeEach(function () {
216 client
._sock
= new Websock();
217 client
._sock
.open('ws://', 'binary');
218 client
._sock
._websocket
._open();
219 sinon
.spy(client
._sock
, 'flush');
220 client
._rfb_connection_state
= 'connected';
221 client
._view_only
= false;
224 it('should send the given text in a paste event', function () {
225 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
226 RFB
.messages
.clientCutText(expected
, 'abc');
227 client
.clipboardPasteFrom('abc');
228 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
231 it('should not send the text if we are not in a normal state', function () {
232 client
._rfb_connection_state
= "broken";
233 client
.clipboardPasteFrom('abc');
234 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
238 describe("#requestDesktopSize", function () {
239 beforeEach(function() {
240 client
._sock
= new Websock();
241 client
._sock
.open('ws://', 'binary');
242 client
._sock
._websocket
._open();
243 sinon
.spy(client
._sock
, 'flush');
244 client
._rfb_connection_state
= 'connected';
245 client
._view_only
= false;
246 client
._supportsSetDesktopSize
= true;
249 it('should send the request with the given width and height', function () {
250 var expected
= [251];
251 push8(expected
, 0); // padding
252 push16(expected
, 1); // width
253 push16(expected
, 2); // height
254 push8(expected
, 1); // number-of-screens
255 push8(expected
, 0); // padding before screen array
256 push32(expected
, 0); // id
257 push16(expected
, 0); // x-position
258 push16(expected
, 0); // y-position
259 push16(expected
, 1); // width
260 push16(expected
, 2); // height
261 push32(expected
, 0); // flags
263 client
.requestDesktopSize(1, 2);
264 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
267 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
268 client
._supportsSetDesktopSize
= false;
269 client
.requestDesktopSize(1,2);
270 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
273 it('should not send the request if we are not in a normal state', function () {
274 client
._rfb_connection_state
= "broken";
275 client
.requestDesktopSize(1,2);
276 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
280 describe("XVP operations", function () {
281 beforeEach(function () {
282 client
._sock
= new Websock();
283 client
._sock
.open('ws://', 'binary');
284 client
._sock
._websocket
._open();
285 sinon
.spy(client
._sock
, 'flush');
286 client
._rfb_connection_state
= 'connected';
287 client
._view_only
= false;
288 client
._rfb_xvp_ver
= 1;
291 it('should send the shutdown signal on #xvpShutdown', function () {
292 client
.xvpShutdown();
293 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
296 it('should send the reboot signal on #xvpReboot', function () {
298 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
301 it('should send the reset signal on #xvpReset', function () {
303 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
306 it('should support sending arbitrary XVP operations via #xvpOp', function () {
308 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x07]));
311 it('should not send XVP operations with higher versions than we support', function () {
312 expect(client
.xvpOp(2, 7)).to
.be
.false;
313 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
318 describe('Misc Internals', function () {
319 describe('#_updateConnectionState', function () {
321 beforeEach(function () {
322 this.clock
= sinon
.useFakeTimers();
326 afterEach(function () {
327 this.clock
.restore();
330 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
331 var spy
= sinon
.spy();
332 client
._disconnTimer
= setTimeout(spy
, 50);
333 client
._updateConnectionState('connecting');
335 expect(spy
).to
.not
.have
.been
.called
;
336 expect(client
._disconnTimer
).to
.be
.null;
339 it('should call the updateState callback', function () {
340 client
.set_onUpdateState(sinon
.spy());
341 client
._updateConnectionState('connecting');
342 var spy
= client
.get_onUpdateState();
343 expect(spy
).to
.have
.been
.calledOnce
;
344 expect(spy
.args
[0][1]).to
.equal('connecting');
347 it('should set the rfb_connection_state', function () {
348 client
._rfb_connection_state
= 'disconnecting';
349 client
._updateConnectionState('disconnected');
350 expect(client
._rfb_connection_state
).to
.equal('disconnected');
353 it('should not change the state when we are disconnected', function () {
354 client
._rfb_connection_state
= 'disconnected';
355 client
._updateConnectionState('connecting');
356 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
359 it('should ignore state changes to the same state', function () {
360 client
.set_onUpdateState(sinon
.spy());
361 client
._rfb_connection_state
= 'connecting';
362 client
._updateConnectionState('connecting');
363 var spy
= client
.get_onUpdateState();
364 expect(spy
).to
.not
.have
.been
.called
;
367 it('should ignore illegal state changes', function () {
368 client
.set_onUpdateState(sinon
.spy());
369 client
._rfb_connection_state
= 'connected';
370 client
._updateConnectionState('disconnected');
371 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
372 var spy
= client
.get_onUpdateState();
373 expect(spy
).to
.not
.have
.been
.called
;
377 describe('#_fail', function () {
379 beforeEach(function () {
380 this.clock
= sinon
.useFakeTimers();
382 client
.connect('host', 8675);
385 afterEach(function () {
386 this.clock
.restore();
389 it('should close the WebSocket connection', function () {
390 sinon
.spy(client
._sock
, 'close');
392 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
395 it('should transition to disconnected', function () {
396 sinon
.spy(client
, '_updateConnectionState');
398 this.clock
.tick(2000);
399 expect(client
._updateConnectionState
).to
.have
.been
.called
;
400 expect(client
._rfb_connection_state
).to
.equal('disconnected');
403 it('should set disconnect_reason', function () {
404 client
._rfb_connection_state
= 'connected';
405 client
._fail('a reason');
406 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
409 it('should not include details in disconnect_reason', function () {
410 client
._rfb_connection_state
= 'connected';
411 client
._fail('a reason', 'details');
412 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
415 it('should result in disconnect callback with message when reason given', function () {
416 client
._rfb_connection_state
= 'connected';
417 client
.set_onDisconnected(sinon
.spy());
418 client
._fail('a reason');
419 var spy
= client
.get_onDisconnected();
420 this.clock
.tick(2000);
421 expect(spy
).to
.have
.been
.calledOnce
;
422 expect(spy
.args
[0].length
).to
.equal(2);
423 expect(spy
.args
[0][1]).to
.equal('a reason');
428 describe('#_notification', function () {
430 beforeEach(function () { client
= make_rfb(); });
432 it('should call the notification callback', function () {
433 client
.set_onNotification(sinon
.spy());
434 client
._notification('notify!', 'warn');
435 var spy
= client
.get_onNotification();
436 expect(spy
).to
.have
.been
.calledOnce
;
437 expect(spy
.args
[0][1]).to
.equal('notify!');
438 expect(spy
.args
[0][2]).to
.equal('warn');
441 it('should not call the notification callback when level is invalid', function () {
442 client
.set_onNotification(sinon
.spy());
443 client
._notification('notify!', 'invalid');
444 var spy
= client
.get_onNotification();
445 expect(spy
).to
.not
.have
.been
.called
;
450 describe('Connection States', function () {
451 describe('connecting', function () {
453 beforeEach(function () { client
= make_rfb(); });
455 it('should reset the variable states', function () {
456 sinon
.spy(client
, '_init_vars');
457 client
._updateConnectionState('connecting');
458 expect(client
._init_vars
).to
.have
.been
.calledOnce
;
461 it('should actually connect to the websocket', function () {
462 sinon
.spy(client
._sock
, 'open');
463 client
._updateConnectionState('connecting');
464 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
467 it('should use wss:// to connect if encryption is enabled', function () {
468 sinon
.spy(client
._sock
, 'open');
469 client
.set_encrypt(true);
470 client
._updateConnectionState('connecting');
471 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
474 it('should use ws:// to connect if encryption is not enabled', function () {
475 sinon
.spy(client
._sock
, 'open');
476 client
.set_encrypt(true);
477 client
._updateConnectionState('connecting');
478 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
481 it('should use a uri with the host, port, and path specified to connect', function () {
482 sinon
.spy(client
._sock
, 'open');
483 client
.set_encrypt(false);
484 client
._rfb_host
= 'HOST';
485 client
._rfb_port
= 8675;
486 client
._rfb_path
= 'PATH';
487 client
._updateConnectionState('connecting');
488 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
492 describe('disconnecting', function () {
494 beforeEach(function () {
495 this.clock
= sinon
.useFakeTimers();
497 client
.connect('host', 8675);
500 afterEach(function () {
501 this.clock
.restore();
504 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
505 sinon
.spy(client
, '_updateConnectionState');
506 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
507 client
._updateConnectionState('disconnecting');
508 this.clock
.tick(client
.get_disconnectTimeout() * 1000);
509 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
510 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
511 expect(client
._rfb_connection_state
).to
.equal("disconnected");
514 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
515 client
._updateConnectionState('disconnecting');
516 this.clock
.tick(client
.get_disconnectTimeout() * 500);
517 client
._sock
._websocket
.close();
518 this.clock
.tick(client
.get_disconnectTimeout() * 500 + 1);
519 expect(client
._rfb_connection_state
).to
.equal('disconnected');
522 it('should close the WebSocket connection', function () {
523 sinon
.spy(client
._sock
, 'close');
524 client
._updateConnectionState('disconnecting');
525 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
529 describe('disconnected', function () {
531 beforeEach(function () { client
= make_rfb(); });
533 it('should call the disconnect callback if the state is "disconnected"', function () {
534 client
.set_onDisconnected(sinon
.spy());
535 client
._rfb_connection_state
= 'disconnecting';
536 client
._rfb_disconnect_reason
= "error";
537 client
._updateConnectionState('disconnected');
538 var spy
= client
.get_onDisconnected();
539 expect(spy
).to
.have
.been
.calledOnce
;
540 expect(spy
.args
[0][1]).to
.equal("error");
543 it('should not call the disconnect callback if the state is not "disconnected"', function () {
544 client
.set_onDisconnected(sinon
.spy());
545 client
._updateConnectionState('disconnecting');
546 var spy
= client
.get_onDisconnected();
547 expect(spy
).to
.not
.have
.been
.called
;
550 it('should call the disconnect callback without msg when no reason given', function () {
551 client
.set_onDisconnected(sinon
.spy());
552 client
._rfb_connection_state
= 'disconnecting';
553 client
._rfb_disconnect_reason
= "";
554 client
._updateConnectionState('disconnected');
555 var spy
= client
.get_onDisconnected();
556 expect(spy
).to
.have
.been
.calledOnce
;
557 expect(spy
.args
[0].length
).to
.equal(1);
560 it('should call the updateState callback before the disconnect callback', function () {
561 client
.set_onDisconnected(sinon
.spy());
562 client
.set_onUpdateState(sinon
.spy());
563 client
._rfb_connection_state
= 'disconnecting';
564 client
._updateConnectionState('disconnected');
565 var updateStateSpy
= client
.get_onUpdateState();
566 var disconnectSpy
= client
.get_onDisconnected();
567 expect(updateStateSpy
.calledBefore(disconnectSpy
)).to
.be
.true;
571 // NB(directxman12): Connected does *nothing* in updateConnectionState
574 describe('Protocol Initialization States', function () {
575 describe('ProtocolVersion', function () {
576 beforeEach(function () {
577 this.clock
= sinon
.useFakeTimers();
580 afterEach(function () {
581 this.clock
.restore();
584 function send_ver (ver
, client
) {
585 var arr
= new Uint8Array(12);
586 for (var i
= 0; i
< ver
.length
; i
++) {
587 arr
[i
+4] = ver
.charCodeAt(i
);
589 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
591 client
._sock
._websocket
._receive_data(arr
);
594 describe('version parsing', function () {
596 beforeEach(function () {
598 client
.connect('host', 8675);
599 client
._sock
._websocket
._open();
602 it('should interpret version 000.000 as a repeater', function () {
603 client
._repeaterID
= '\x01\x02\x03\x04\x05';
604 send_ver('000.000', client
);
605 expect(client
._rfb_version
).to
.equal(0);
607 var sent_data
= client
._sock
._websocket
._get_sent_data();
608 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
611 it('should interpret version 003.003 as version 3.3', function () {
612 send_ver('003.003', client
);
613 expect(client
._rfb_version
).to
.equal(3.3);
616 it('should interpret version 003.006 as version 3.3', function () {
617 send_ver('003.006', client
);
618 expect(client
._rfb_version
).to
.equal(3.3);
621 it('should interpret version 003.889 as version 3.3', function () {
622 send_ver('003.889', client
);
623 expect(client
._rfb_version
).to
.equal(3.3);
626 it('should interpret version 003.007 as version 3.7', function () {
627 send_ver('003.007', client
);
628 expect(client
._rfb_version
).to
.equal(3.7);
631 it('should interpret version 003.008 as version 3.8', function () {
632 send_ver('003.008', client
);
633 expect(client
._rfb_version
).to
.equal(3.8);
636 it('should interpret version 004.000 as version 3.8', function () {
637 send_ver('004.000', client
);
638 expect(client
._rfb_version
).to
.equal(3.8);
641 it('should interpret version 004.001 as version 3.8', function () {
642 send_ver('004.001', client
);
643 expect(client
._rfb_version
).to
.equal(3.8);
646 it('should interpret version 005.000 as version 3.8', function () {
647 send_ver('005.000', client
);
648 expect(client
._rfb_version
).to
.equal(3.8);
651 it('should fail on an invalid version', function () {
652 sinon
.spy(client
, "_fail");
653 send_ver('002.000', client
);
654 expect(client
._fail
).to
.have
.been
.calledOnce
;
659 beforeEach(function () {
661 client
.connect('host', 8675);
662 client
._sock
._websocket
._open();
665 it('should handle two step repeater negotiation', function () {
666 client
._repeaterID
= '\x01\x02\x03\x04\x05';
668 send_ver('000.000', client
);
669 expect(client
._rfb_version
).to
.equal(0);
670 var sent_data
= client
._sock
._websocket
._get_sent_data();
671 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
672 expect(sent_data
).to
.have
.length(250);
674 send_ver('003.008', client
);
675 expect(client
._rfb_version
).to
.equal(3.8);
678 it('should send back the interpreted version', function () {
679 send_ver('004.000', client
);
681 var expected_str
= 'RFB 003.008\n';
683 for (var i
= 0; i
< expected_str
.length
; i
++) {
684 expected
[i
] = expected_str
.charCodeAt(i
);
687 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
690 it('should transition to the Security state on successful negotiation', function () {
691 send_ver('003.008', client
);
692 expect(client
._rfb_init_state
).to
.equal('Security');
696 describe('Security', function () {
699 beforeEach(function () {
701 client
.connect('host', 8675);
702 client
._sock
._websocket
._open();
703 client
._rfb_init_state
= 'Security';
706 it('should simply receive the auth scheme when for versions < 3.7', function () {
707 client
._rfb_version
= 3.6;
708 var auth_scheme_raw
= [1, 2, 3, 4];
709 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
710 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
711 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
712 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
715 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
716 client
._rfb_version
= 3.7;
717 var auth_schemes
= [2, 1, 2];
718 client
._sock
._websocket
._receive_data(auth_schemes
);
719 expect(client
._rfb_auth_scheme
).to
.equal(2);
720 expect(client
._sock
).to
.have
.sent(new Uint8Array([2]));
723 it('should fail if there are no supported schemes for versions >= 3.7', function () {
724 sinon
.spy(client
, "_fail");
725 client
._rfb_version
= 3.7;
726 var auth_schemes
= [1, 32];
727 client
._sock
._websocket
._receive_data(auth_schemes
);
728 expect(client
._fail
).to
.have
.been
.calledOnce
;
731 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
732 client
._rfb_version
= 3.7;
733 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
734 sinon
.spy(client
, '_fail');
735 client
._sock
._websocket
._receive_data(failure_data
);
737 expect(client
._fail
).to
.have
.been
.calledOnce
;
738 expect(client
._fail
).to
.have
.been
.calledWith(
739 'Error while negotiating with server','Security failure: whoops');
742 it('should transition to the Authentication state and continue on successful negotiation', function () {
743 client
._rfb_version
= 3.7;
744 var auth_schemes
= [1, 1];
745 client
._negotiate_authentication
= sinon
.spy();
746 client
._sock
._websocket
._receive_data(auth_schemes
);
747 expect(client
._rfb_init_state
).to
.equal('Authentication');
748 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
752 describe('Authentication', function () {
755 beforeEach(function () {
757 client
.connect('host', 8675);
758 client
._sock
._websocket
._open();
759 client
._rfb_init_state
= 'Security';
762 function send_security(type
, cl
) {
763 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
766 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
767 client
._rfb_version
= 3.6;
768 var err_msg
= "Whoopsies";
769 var data
= [0, 0, 0, 0];
770 var err_len
= err_msg
.length
;
771 push32(data
, err_len
);
772 for (var i
= 0; i
< err_len
; i
++) {
773 data
.push(err_msg
.charCodeAt(i
));
776 sinon
.spy(client
, '_fail');
777 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
778 expect(client
._fail
).to
.have
.been
.calledWith(
779 'Authentication failure', 'Whoopsies');
782 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
783 client
._rfb_version
= 3.8;
784 send_security(1, client
);
785 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
788 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
789 client
._rfb_version
= 3.7;
790 send_security(1, client
);
791 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
794 it('should fail on an unknown auth scheme', function () {
795 sinon
.spy(client
, "_fail");
796 client
._rfb_version
= 3.8;
797 send_security(57, client
);
798 expect(client
._fail
).to
.have
.been
.calledOnce
;
801 describe('VNC Authentication (type 2) Handler', function () {
804 beforeEach(function () {
806 client
.connect('host', 8675);
807 client
._sock
._websocket
._open();
808 client
._rfb_init_state
= 'Security';
809 client
._rfb_version
= 3.8;
812 it('should call the passwordRequired callback if missing a password', function () {
813 client
.set_onPasswordRequired(sinon
.spy());
814 send_security(2, client
);
816 var spy
= client
.get_onPasswordRequired();
817 expect(client
._rfb_password
.length
).to
.equal(0);
818 expect(spy
).to
.have
.been
.calledOnce
;
821 it('should encrypt the password with DES and then send it back', function () {
822 client
._rfb_password
= 'passwd';
823 send_security(2, client
);
824 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
827 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
828 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
830 var des_pass
= RFB
.genDES('passwd', challenge
);
831 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
834 it('should transition to SecurityResult immediately after sending the password', function () {
835 client
._rfb_password
= 'passwd';
836 send_security(2, client
);
839 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
840 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
842 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
846 describe('XVP Authentication (type 22) Handler', function () {
849 beforeEach(function () {
851 client
.connect('host', 8675);
852 client
._sock
._websocket
._open();
853 client
._rfb_init_state
= 'Security';
854 client
._rfb_version
= 3.8;
857 it('should fall through to standard VNC authentication upon completion', function () {
858 client
.set_xvp_password_sep('#');
859 client
._rfb_password
= 'user#target#password';
860 client
._negotiate_std_vnc_auth
= sinon
.spy();
861 send_security(22, client
);
862 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
865 it('should call the passwordRequired callback if the password is missing', function() {
866 client
.set_onPasswordRequired(sinon
.spy());
867 client
._rfb_password
= '';
868 send_security(22, client
);
870 var spy
= client
.get_onPasswordRequired();
871 expect(client
._rfb_password
.length
).to
.equal(0);
872 expect(spy
).to
.have
.been
.calledOnce
;
875 it('should call the passwordRequired callback if the password is improperly formatted', function() {
876 client
.set_onPasswordRequired(sinon
.spy());
877 client
._rfb_password
= 'user@target';
878 send_security(22, client
);
880 var spy
= client
.get_onPasswordRequired();
881 expect(spy
).to
.have
.been
.calledOnce
;
884 it('should split the password, send the first two parts, and pass on the last part', function () {
885 client
.set_xvp_password_sep('#');
886 client
._rfb_password
= 'user#target#password';
887 client
._negotiate_std_vnc_auth
= sinon
.spy();
889 send_security(22, client
);
891 expect(client
._rfb_password
).to
.equal('password');
893 var expected
= [22, 4, 6]; // auth selection, len user, len target
894 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
896 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
900 describe('TightVNC Authentication (type 16) Handler', function () {
903 beforeEach(function () {
905 client
.connect('host', 8675);
906 client
._sock
._websocket
._open();
907 client
._rfb_init_state
= 'Security';
908 client
._rfb_version
= 3.8;
909 send_security(16, client
);
910 client
._sock
._websocket
._get_sent_data(); // skip the security reply
913 function send_num_str_pairs(pairs
, client
) {
914 var pairs_len
= pairs
.length
;
916 push32(data
, pairs_len
);
918 for (var i
= 0; i
< pairs_len
; i
++) {
919 push32(data
, pairs
[i
][0]);
921 for (j
= 0; j
< 4; j
++) {
922 data
.push(pairs
[i
][1].charCodeAt(j
));
924 for (j
= 0; j
< 8; j
++) {
925 data
.push(pairs
[i
][2].charCodeAt(j
));
929 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
932 it('should skip tunnel negotiation if no tunnels are requested', function () {
933 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
934 expect(client
._rfb_tightvnc
).to
.be
.true;
937 it('should fail if no supported tunnels are listed', function () {
938 sinon
.spy(client
, "_fail");
939 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
940 expect(client
._fail
).to
.have
.been
.calledOnce
;
943 it('should choose the notunnel tunnel type', function () {
944 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
945 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
948 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
949 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
950 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
951 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
952 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
953 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
956 /*it('should attempt to use VNC auth over no auth when possible', function () {
957 client._rfb_tightvnc = true;
958 client._negotiate_std_vnc_auth = sinon.spy();
959 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
960 expect(client._sock).to.have.sent([0, 0, 0, 1]);
961 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
962 expect(client._rfb_auth_scheme).to.equal(2);
963 });*/ // while this would make sense, the original code doesn't actually do this
965 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
966 client
._rfb_tightvnc
= true;
967 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
968 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
969 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
972 it('should accept VNC authentication and transition to that', function () {
973 client
._rfb_tightvnc
= true;
974 client
._negotiate_std_vnc_auth
= sinon
.spy();
975 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
976 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
977 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
978 expect(client
._rfb_auth_scheme
).to
.equal(2);
981 it('should fail if there are no supported auth types', function () {
982 sinon
.spy(client
, "_fail");
983 client
._rfb_tightvnc
= true;
984 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
985 expect(client
._fail
).to
.have
.been
.calledOnce
;
990 describe('SecurityResult', function () {
993 beforeEach(function () {
995 client
.connect('host', 8675);
996 client
._sock
._websocket
._open();
997 client
._rfb_init_state
= 'SecurityResult';
1000 it('should fall through to ServerInitialisation on a response code of 0', function () {
1001 client
._updateConnectionState
= sinon
.spy();
1002 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1003 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1006 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1007 client
._rfb_version
= 3.8;
1008 sinon
.spy(client
, '_fail');
1009 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1010 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1011 expect(client
._fail
).to
.have
.been
.calledWith(
1012 'Authentication failure', 'whoops');
1015 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1016 sinon
.spy(client
, '_fail');
1017 client
._rfb_version
= 3.7;
1018 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
1019 expect(client
._fail
).to
.have
.been
.calledWith('Authentication failure');
1023 describe('ClientInitialisation', function () {
1026 beforeEach(function () {
1027 client
= make_rfb();
1028 client
.connect('host', 8675);
1029 client
._sock
._websocket
._open();
1030 client
._rfb_init_state
= 'SecurityResult';
1033 it('should transition to the ServerInitialisation state', function () {
1034 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1035 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1038 it('should send 1 if we are in shared mode', function () {
1039 client
.set_shared(true);
1040 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1041 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1044 it('should send 0 if we are not in shared mode', function () {
1045 client
.set_shared(false);
1046 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1047 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1051 describe('ServerInitialisation', function () {
1054 beforeEach(function () {
1055 client
= make_rfb();
1056 client
.connect('host', 8675);
1057 client
._sock
._websocket
._open();
1058 client
._rfb_init_state
= 'ServerInitialisation';
1061 function send_server_init(opts
, client
) {
1062 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1063 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1064 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1065 for (var opt
in opts
) {
1066 full_opts
[opt
] = opts
[opt
];
1070 push16(data
, full_opts
.width
);
1071 push16(data
, full_opts
.height
);
1073 data
.push(full_opts
.bpp
);
1074 data
.push(full_opts
.depth
);
1075 data
.push(full_opts
.big_endian
);
1076 data
.push(full_opts
.true_color
);
1078 push16(data
, full_opts
.red_max
);
1079 push16(data
, full_opts
.green_max
);
1080 push16(data
, full_opts
.blue_max
);
1081 push8(data
, full_opts
.red_shift
);
1082 push8(data
, full_opts
.green_shift
);
1083 push8(data
, full_opts
.blue_shift
);
1090 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1093 push32(name_data
, full_opts
.name
.length
);
1094 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1095 name_data
.push(full_opts
.name
.charCodeAt(i
));
1097 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1100 it('should set the framebuffer width and height', function () {
1101 send_server_init({ width
: 32, height
: 84 }, client
);
1102 expect(client
._fb_width
).to
.equal(32);
1103 expect(client
._fb_height
).to
.equal(84);
1106 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1108 it('should set the framebuffer name and call the callback', function () {
1109 client
.set_onDesktopName(sinon
.spy());
1110 send_server_init({ name
: 'some name' }, client
);
1112 var spy
= client
.get_onDesktopName();
1113 expect(client
._fb_name
).to
.equal('some name');
1114 expect(spy
).to
.have
.been
.calledOnce
;
1115 expect(spy
.args
[0][1]).to
.equal('some name');
1118 it('should handle the extended init message of the tight encoding', function () {
1119 // NB(sross): we don't actually do anything with it, so just test that we can
1120 // read it w/o throwing an error
1121 client
._rfb_tightvnc
= true;
1122 send_server_init({}, client
);
1124 var tight_data
= [];
1125 push16(tight_data
, 1);
1126 push16(tight_data
, 2);
1127 push16(tight_data
, 3);
1128 push16(tight_data
, 0);
1129 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1132 client
._sock
._websocket
._receive_data(tight_data
);
1134 expect(client
._rfb_connection_state
).to
.equal('connected');
1137 it('should set the true color mode on the display to the configuration variable', function () {
1138 client
.set_true_color(false);
1139 sinon
.spy(client
._display
, 'set_true_color');
1140 send_server_init({ true_color
: 1 }, client
);
1141 expect(client
._display
.set_true_color
).to
.have
.been
.calledOnce
;
1142 expect(client
._display
.set_true_color
).to
.have
.been
.calledWith(false);
1145 it('should call the resize callback and resize the display', function () {
1146 client
.set_onFBResize(sinon
.spy());
1147 sinon
.spy(client
._display
, 'resize');
1148 send_server_init({ width
: 27, height
: 32 }, client
);
1150 var spy
= client
.get_onFBResize();
1151 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1152 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1153 expect(spy
).to
.have
.been
.calledOnce
;
1154 expect(spy
.args
[0][1]).to
.equal(27);
1155 expect(spy
.args
[0][2]).to
.equal(32);
1158 it('should grab the mouse and keyboard', function () {
1159 sinon
.spy(client
._keyboard
, 'grab');
1160 sinon
.spy(client
._mouse
, 'grab');
1161 send_server_init({}, client
);
1162 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1163 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1166 it('should set the BPP and depth to 4 and 3 respectively if in true color mode', function () {
1167 client
.set_true_color(true);
1168 send_server_init({}, client
);
1169 expect(client
._fb_Bpp
).to
.equal(4);
1170 expect(client
._fb_depth
).to
.equal(3);
1173 it('should set the BPP and depth to 1 and 1 respectively if not in true color mode', function () {
1174 client
.set_true_color(false);
1175 send_server_init({}, client
);
1176 expect(client
._fb_Bpp
).to
.equal(1);
1177 expect(client
._fb_depth
).to
.equal(1);
1180 // TODO(directxman12): test the various options in this configuration matrix
1181 it('should reply with the pixel format, client encodings, and initial update request', function () {
1182 client
.set_true_color(true);
1183 client
.set_local_cursor(false);
1184 // we skip the cursor encoding
1185 var expected
= {_sQ
: new Uint8Array(34 + 4 * (client
._encodings
.length
- 1)),
1187 flush: function () {}};
1188 RFB
.messages
.pixelFormat(expected
, 4, 3, true);
1189 RFB
.messages
.clientEncodings(expected
, client
._encodings
, false, true);
1190 RFB
.messages
.fbUpdateRequest(expected
, false, 0, 0, 27, 32);
1192 send_server_init({ width
: 27, height
: 32 }, client
);
1193 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
1196 it('should transition to the "connected" state', function () {
1197 send_server_init({}, client
);
1198 expect(client
._rfb_connection_state
).to
.equal('connected');
1203 describe('Protocol Message Processing After Completing Initialization', function () {
1206 beforeEach(function () {
1207 client
= make_rfb();
1208 client
.connect('host', 8675);
1209 client
._sock
._websocket
._open();
1210 client
._rfb_connection_state
= 'connected';
1211 client
._fb_name
= 'some device';
1212 client
._fb_width
= 640;
1213 client
._fb_height
= 20;
1216 describe('Framebuffer Update Handling', function () {
1219 beforeEach(function () {
1220 client
= make_rfb();
1221 client
.connect('host', 8675);
1222 client
._sock
._websocket
._open();
1223 client
._rfb_connection_state
= 'connected';
1224 client
._fb_name
= 'some device';
1225 client
._fb_width
= 640;
1226 client
._fb_height
= 20;
1229 var target_data_arr
= [
1230 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1231 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1232 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1233 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1237 var target_data_check_arr
= [
1238 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1239 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1240 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1241 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1243 var target_data_check
;
1245 before(function () {
1246 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1247 target_data
= new Uint8Array(target_data_arr
);
1248 target_data_check
= new Uint8Array(target_data_check_arr
);
1251 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1254 if (!rect_cnt
|| rect_cnt
> -1) {
1256 data
.push(0); // msg type
1257 data
.push(0); // padding
1258 push16(data
, rect_cnt
|| rect_data
.length
);
1261 for (var i
= 0; i
< rect_data
.length
; i
++) {
1263 push16(data
, rect_info
[i
].x
);
1264 push16(data
, rect_info
[i
].y
);
1265 push16(data
, rect_info
[i
].width
);
1266 push16(data
, rect_info
[i
].height
);
1267 push32(data
, rect_info
[i
].encoding
);
1269 data
= data
.concat(rect_data
[i
]);
1272 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1275 it('should send an update request if there is sufficient data', function () {
1276 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1277 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1279 client
._framebufferUpdate = function () { return true; };
1280 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1282 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1285 it('should not send an update request if we need more data', function () {
1286 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1287 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1290 it('should resume receiving an update if we previously did not have enough data', function () {
1291 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1292 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1294 // just enough to set FBU.rects
1295 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1296 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1298 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1299 // 247 should *not* be used as the message type here
1300 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1301 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1304 it('should not send a request in continuous updates mode', function () {
1305 client
._enabledContinuousUpdates
= true;
1306 client
._framebufferUpdate = function () { return true; };
1307 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1309 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1312 it('should parse out information from a header before any actual data comes in', function () {
1313 client
.set_onFBUReceive(sinon
.spy());
1314 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02, encodingName
: 'RRE' };
1315 send_fbu_msg([rect_info
], [[]], client
);
1317 var spy
= client
.get_onFBUReceive();
1318 expect(spy
).to
.have
.been
.calledOnce
;
1319 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1322 it('should fire onFBUComplete when the update is complete', function () {
1323 client
.set_onFBUComplete(sinon
.spy());
1324 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: -224, encodingName
: 'last_rect' };
1325 send_fbu_msg([rect_info
], [[]], client
); // last_rect
1327 var spy
= client
.get_onFBUComplete();
1328 expect(spy
).to
.have
.been
.calledOnce
;
1329 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1332 it('should not fire onFBUComplete if we have not finished processing the update', function () {
1333 client
.set_onFBUComplete(sinon
.spy());
1334 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x00, encodingName
: 'RAW' };
1335 send_fbu_msg([rect_info
], [[]], client
);
1336 expect(client
.get_onFBUComplete()).to
.not
.have
.been
.called
;
1339 it('should call the appropriate encoding handler', function () {
1340 client
._encHandlers
[0x02] = sinon
.spy();
1341 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02 };
1342 send_fbu_msg([rect_info
], [[]], client
);
1343 expect(client
._encHandlers
[0x02]).to
.have
.been
.calledOnce
;
1346 it('should fail on an unsupported encoding', function () {
1347 sinon
.spy(client
, "_fail");
1348 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1349 send_fbu_msg([rect_info
], [[]], client
);
1350 expect(client
._fail
).to
.have
.been
.calledOnce
;
1353 it('should be able to pause and resume receiving rects if not enought data', function () {
1354 // seed some initial data to copy
1355 client
._fb_width
= 4;
1356 client
._fb_height
= 4;
1357 client
._display
.resize(4, 4);
1358 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1360 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1361 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1362 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1363 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1364 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1365 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1366 expect(client
._display
).to
.have
.displayed(target_data_check
);
1369 describe('Message Encoding Handlers', function () {
1372 beforeEach(function () {
1373 client
= make_rfb();
1374 client
.connect('host', 8675);
1375 client
._sock
._websocket
._open();
1376 client
._rfb_connection_state
= 'connected';
1377 client
._fb_name
= 'some device';
1378 // a really small frame
1379 client
._fb_width
= 4;
1380 client
._fb_height
= 4;
1381 client
._display
.resize(4, 4);
1385 it('should handle the RAW encoding', function () {
1386 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1387 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1388 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1389 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1392 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1393 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1394 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1395 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1396 send_fbu_msg(info
, rects
, client
);
1397 expect(client
._display
).to
.have
.displayed(target_data
);
1400 it('should handle the COPYRECT encoding', function () {
1401 // seed some initial data to copy
1402 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1404 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1405 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1406 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1407 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1408 send_fbu_msg(info
, rects
, client
);
1409 expect(client
._display
).to
.have
.displayed(target_data_check
);
1412 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1413 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1415 it('should handle the RRE encoding', function () {
1416 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1418 push32(rect
, 2); // 2 subrects
1419 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1420 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1424 push16(rect
, 0); // x: 0
1425 push16(rect
, 0); // y: 0
1426 push16(rect
, 2); // width: 2
1427 push16(rect
, 2); // height: 2
1428 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1432 push16(rect
, 2); // x: 2
1433 push16(rect
, 2); // y: 2
1434 push16(rect
, 2); // width: 2
1435 push16(rect
, 2); // height: 2
1437 send_fbu_msg(info
, [rect
], client
);
1438 expect(client
._display
).to
.have
.displayed(target_data_check
);
1441 describe('the HEXTILE encoding handler', function () {
1443 beforeEach(function () {
1444 client
= make_rfb();
1445 client
.connect('host', 8675);
1446 client
._sock
._websocket
._open();
1447 client
._rfb_connection_state
= 'connected';
1448 client
._fb_name
= 'some device';
1449 // a really small frame
1450 client
._fb_width
= 4;
1451 client
._fb_height
= 4;
1452 client
._display
.resize(4, 4);
1456 it('should handle a tile with fg, bg specified, normal subrects', function () {
1457 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1459 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1460 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1461 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1465 rect
.push(2); // 2 subrects
1466 rect
.push(0); // x: 0, y: 0
1467 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1468 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1469 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1470 send_fbu_msg(info
, [rect
], client
);
1471 expect(client
._display
).to
.have
.displayed(target_data_check
);
1474 it('should handle a raw tile', function () {
1475 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1477 rect
.push(0x01); // raw
1478 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1479 rect
.push(target_data
[i
+ 2]);
1480 rect
.push(target_data
[i
+ 1]);
1481 rect
.push(target_data
[i
]);
1482 rect
.push(target_data
[i
+ 3]);
1484 send_fbu_msg(info
, [rect
], client
);
1485 expect(client
._display
).to
.have
.displayed(target_data
);
1488 it('should handle a tile with only bg specified (solid bg)', function () {
1489 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1492 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1493 send_fbu_msg(info
, [rect
], client
);
1496 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1497 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1500 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1501 // set the width so we can have two tiles
1502 client
._fb_width
= 8;
1503 client
._display
.resize(8, 4);
1505 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1511 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1513 // send an empty frame
1516 send_fbu_msg(info
, [rect
], client
);
1520 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1521 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1522 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1525 it('should handle a tile with bg and coloured subrects', function () {
1526 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1528 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1529 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1530 rect
.push(2); // 2 subrects
1531 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1535 rect
.push(0); // x: 0, y: 0
1536 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1537 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1541 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1542 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1543 send_fbu_msg(info
, [rect
], client
);
1544 expect(client
._display
).to
.have
.displayed(target_data_check
);
1547 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1548 client
._fb_width
= 4;
1549 client
._fb_height
= 17;
1550 client
._display
.resize(4, 17);
1552 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1554 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1555 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1556 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1560 rect
.push(8); // 8 subrects
1562 for (i
= 0; i
< 4; i
++) {
1563 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1564 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1565 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1566 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1568 rect
.push(0x08); // anysubrects
1569 rect
.push(1); // 1 subrect
1570 rect
.push(0); // x: 0, y: 0
1571 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1572 send_fbu_msg(info
, [rect
], client
);
1575 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1576 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1577 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1580 it('should fail on an invalid subencoding', function () {
1581 sinon
.spy(client
,"_fail");
1582 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1583 var rects
= [[45]]; // an invalid subencoding
1584 send_fbu_msg(info
, rects
, client
);
1585 expect(client
._fail
).to
.have
.been
.calledOnce
;
1589 it
.skip('should handle the TIGHT encoding', function () {
1590 // TODO(directxman12): test this
1593 it
.skip('should handle the TIGHT_PNG encoding', function () {
1594 // TODO(directxman12): test this
1597 it('should handle the DesktopSize pseduo-encoding', function () {
1598 client
.set_onFBResize(sinon
.spy());
1599 sinon
.spy(client
._display
, 'resize');
1600 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1602 var spy
= client
.get_onFBResize();
1603 expect(spy
).to
.have
.been
.calledOnce
;
1604 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
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);
1613 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1616 beforeEach(function () {
1617 client
= make_rfb();
1618 client
.connect('host', 8675);
1619 client
._sock
._websocket
._open();
1620 client
._rfb_connection_state
= 'connected';
1621 client
._fb_name
= 'some device';
1622 client
._supportsSetDesktopSize
= false;
1623 // a really small frame
1624 client
._fb_width
= 4;
1625 client
._fb_height
= 4;
1626 client
._display
.resize(4, 4);
1628 sinon
.spy(client
._display
, 'resize');
1629 client
.set_onFBResize(sinon
.spy());
1632 function make_screen_data (nr_of_screens
) {
1634 push8(data
, nr_of_screens
); // number-of-screens
1635 push8(data
, 0); // padding
1636 push16(data
, 0); // padding
1637 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1638 push32(data
, 0); // id
1639 push16(data
, 0); // x-position
1640 push16(data
, 0); // y-position
1641 push16(data
, 20); // width
1642 push16(data
, 50); // height
1643 push32(data
, 0); // flags
1648 it('should handle a resize requested by this client', function () {
1649 var reason_for_change
= 1; // requested by this client
1650 var status_code
= 0; // No error
1652 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1653 width
: 20, height
: 50, encoding
: -308 }],
1654 make_screen_data(1), client
);
1656 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1657 expect(client
._fb_width
).to
.equal(20);
1658 expect(client
._fb_height
).to
.equal(50);
1660 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1661 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1663 var spy
= client
.get_onFBResize();
1664 expect(spy
).to
.have
.been
.calledOnce
;
1665 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1668 it('should handle a resize requested by another client', function () {
1669 var reason_for_change
= 2; // requested by another client
1670 var status_code
= 0; // No error
1672 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1673 width
: 20, height
: 50, encoding
: -308 }],
1674 make_screen_data(1), client
);
1676 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1677 expect(client
._fb_width
).to
.equal(20);
1678 expect(client
._fb_height
).to
.equal(50);
1680 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1681 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1683 var spy
= client
.get_onFBResize();
1684 expect(spy
).to
.have
.been
.calledOnce
;
1685 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1688 it('should be able to recieve requests which contain data for multiple screens', function () {
1689 var reason_for_change
= 2; // requested by another client
1690 var status_code
= 0; // No error
1692 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1693 width
: 60, height
: 50, encoding
: -308 }],
1694 make_screen_data(3), client
);
1696 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1697 expect(client
._fb_width
).to
.equal(60);
1698 expect(client
._fb_height
).to
.equal(50);
1700 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1701 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1703 var spy
= client
.get_onFBResize();
1704 expect(spy
).to
.have
.been
.calledOnce
;
1705 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 60, 50);
1708 it('should not handle a failed request', function () {
1709 var reason_for_change
= 1; // requested by this client
1710 var status_code
= 1; // Resize is administratively prohibited
1712 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1713 width
: 20, height
: 50, encoding
: -308 }],
1714 make_screen_data(1), client
);
1716 expect(client
._fb_width
).to
.equal(4);
1717 expect(client
._fb_height
).to
.equal(4);
1719 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1721 var spy
= client
.get_onFBResize();
1722 expect(spy
).to
.not
.have
.been
.called
;
1726 it
.skip('should handle the Cursor pseudo-encoding', function () {
1727 // TODO(directxman12): test
1730 it('should handle the last_rect pseudo-encoding', function () {
1731 client
.set_onFBUReceive(sinon
.spy());
1732 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1733 expect(client
._FBU
.rects
).to
.equal(0);
1734 expect(client
.get_onFBUReceive()).to
.have
.been
.calledOnce
;
1739 it('should set the colour map on the display on SetColourMapEntries', function () {
1740 var expected_cm
= [];
1741 var data
= [1, 0, 0, 1, 0, 4];
1743 for (i
= 0; i
< 4; i
++) {
1744 expected_cm
[i
+ 1] = [i
* 10, i
* 10 + 1, i
* 10 + 2];
1745 push16(data
, expected_cm
[i
+ 1][2] << 8);
1746 push16(data
, expected_cm
[i
+ 1][1] << 8);
1747 push16(data
, expected_cm
[i
+ 1][0] << 8);
1750 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1751 expect(client
._display
.get_colourMap()).to
.deep
.equal(expected_cm
);
1754 describe('XVP Message Handling', function () {
1755 beforeEach(function () {
1756 client
= make_rfb();
1757 client
.connect('host', 8675);
1758 client
._sock
._websocket
._open();
1759 client
._rfb_connection_state
= 'connected';
1760 client
._fb_name
= 'some device';
1761 client
._fb_width
= 27;
1762 client
._fb_height
= 32;
1765 it('should send a notification on XVP_FAIL', function () {
1766 client
.set_onNotification(sinon
.spy());
1767 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 0]));
1768 var spy
= client
.get_onNotification();
1769 expect(spy
).to
.have
.been
.calledOnce
;
1770 expect(spy
.args
[0][1]).to
.equal('XVP Operation Failed');
1773 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1774 client
.set_onXvpInit(sinon
.spy());
1775 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1776 expect(client
._rfb_xvp_ver
).to
.equal(10);
1777 expect(client
.get_onXvpInit()).to
.have
.been
.calledOnce
;
1778 expect(client
.get_onXvpInit()).to
.have
.been
.calledWith(10);
1781 it('should fail on unknown XVP message types', function () {
1782 sinon
.spy(client
, "_fail");
1783 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1784 expect(client
._fail
).to
.have
.been
.calledOnce
;
1788 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1789 var expected_str
= 'cheese!';
1790 var data
= [3, 0, 0, 0];
1791 push32(data
, expected_str
.length
);
1792 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1793 client
.set_onClipboard(sinon
.spy());
1795 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1796 var spy
= client
.get_onClipboard();
1797 expect(spy
).to
.have
.been
.calledOnce
;
1798 expect(spy
.args
[0][1]).to
.equal(expected_str
);
1801 it('should fire the bell callback on Bell', function () {
1802 client
.set_onBell(sinon
.spy());
1803 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1804 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1807 it('should respond correctly to ServerFence', function () {
1808 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1809 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1811 var payload
= "foo\x00ab9";
1813 // ClientFence and ServerFence are identical in structure
1814 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1815 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1817 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1819 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1821 expected_msg
._sQlen
= 0;
1822 incoming_msg
._sQlen
= 0;
1824 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1825 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1827 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1829 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1832 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1833 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1835 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1837 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1839 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1841 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1842 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1845 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1846 client
._enabledContinuousUpdates
= true;
1847 client
._supportsContinuousUpdates
= true;
1849 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1851 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1854 it('should update continuous updates on resize', function () {
1855 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1856 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1858 client
._FBU
.width
= 450;
1859 client
._FBU
.height
= 160;
1861 client
._encHandlers
.handle_FB_resize();
1863 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1865 client
._enabledContinuousUpdates
= true;
1867 client
._FBU
.width
= 90;
1868 client
._FBU
.height
= 700;
1870 client
._encHandlers
.handle_FB_resize();
1872 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1875 it('should fail on an unknown message type', function () {
1876 sinon
.spy(client
, "_fail");
1877 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1878 expect(client
._fail
).to
.have
.been
.calledOnce
;
1882 describe('Asynchronous Events', function () {
1883 describe('Mouse event handlers', function () {
1885 beforeEach(function () {
1886 client
= make_rfb();
1887 client
._sock
= new Websock();
1888 client
._sock
.open('ws://', 'binary');
1889 client
._sock
._websocket
._open();
1890 sinon
.spy(client
._sock
, 'flush');
1891 client
._rfb_connection_state
= 'connected';
1894 it('should not send button messages in view-only mode', function () {
1895 client
._view_only
= true;
1896 client
._mouse
._onMouseButton(0, 0, 1, 0x001);
1897 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1900 it('should not send movement messages in view-only mode', function () {
1901 client
._view_only
= true;
1902 client
._mouse
._onMouseMove(0, 0);
1903 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1906 it('should send a pointer event on mouse button presses', function () {
1907 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1908 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1909 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1910 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1913 it('should send a mask of 1 on mousedown', function () {
1914 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1915 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1916 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1917 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1920 it('should send a mask of 0 on mouseup', function () {
1921 client
._mouse_buttonMask
= 0x001;
1922 client
._mouse
._onMouseButton(10, 12, 0, 0x001);
1923 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1924 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1925 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1928 it('should send a pointer event on mouse movement', function () {
1929 client
._mouse
._onMouseMove(10, 12);
1930 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1931 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1932 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1935 it('should set the button mask so that future mouse movements use it', function () {
1936 client
._mouse
._onMouseButton(10, 12, 1, 0x010);
1937 client
._mouse
._onMouseMove(13, 9);
1938 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1939 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1940 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1941 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1944 // NB(directxman12): we don't need to test not sending messages in
1945 // non-normal modes, since we haven't grabbed input
1946 // yet (grabbing input should be checked in the lifecycle tests).
1948 it('should not send movement messages when viewport dragging', function () {
1949 client
._viewportDragging
= true;
1950 client
._display
.viewportChangePos
= sinon
.spy();
1951 client
._mouse
._onMouseMove(13, 9);
1952 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1955 it('should not send button messages when initiating viewport dragging', function () {
1956 client
._viewportDrag
= true;
1957 client
._mouse
._onMouseButton(13, 9, 0x001);
1958 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1961 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1962 client
._viewportDrag
= true;
1963 client
._mouse
._onMouseButton(13, 9, 0x001);
1964 expect(client
._viewportDragging
).to
.be
.true;
1965 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1968 it('should terminate viewport dragging on a button up event, if enabled', function () {
1969 client
._viewportDrag
= true;
1970 client
._viewportDragging
= true;
1971 client
._mouse
._onMouseButton(13, 9, 0x000);
1972 expect(client
._viewportDragging
).to
.be
.false;
1975 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1976 client
._viewportDrag
= true;
1977 client
._viewportDragging
= true;
1978 client
._viewportHasMoved
= false;
1979 client
._viewportDragPos
= { x
: 23, y
: 9 };
1980 client
._display
.viewportChangePos
= sinon
.spy();
1982 client
._mouse
._onMouseMove(10, 4);
1984 expect(client
._viewportDragging
).to
.be
.true;
1985 expect(client
._viewportHasMoved
).to
.be
.true;
1986 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 10, y
: 4 });
1987 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
1988 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(13, 5);
1992 describe('Keyboard Event Handlers', function () {
1994 beforeEach(function () {
1995 client
= make_rfb();
1996 client
._sock
= new Websock();
1997 client
._sock
.open('ws://', 'binary');
1998 client
._sock
._websocket
._open();
1999 sinon
.spy(client
._sock
, 'flush');
2002 it('should send a key message on a key press', function () {
2004 keyevent
.type
= 'keydown';
2005 keyevent
.keysym
= {};
2006 keyevent
.keysym
.keysym
= 1234;
2007 client
._keyboard
._onKeyPress(keyevent
);
2008 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
2009 RFB
.messages
.keyEvent(key_msg
, 1234, 1);
2010 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
2013 it('should not send messages in view-only mode', function () {
2014 client
._view_only
= true;
2015 client
._keyboard
._onKeyPress(1234, 1);
2016 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2020 describe('WebSocket event handlers', function () {
2022 beforeEach(function () {
2023 client
= make_rfb();
2024 this.clock
= sinon
.useFakeTimers();
2027 afterEach(function () { this.clock
.restore(); });
2030 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
2031 client
.connect('host', 8675);
2032 client
._rfb_connection_state
= 'connected';
2033 client
._normal_msg
= sinon
.spy();
2034 client
._sock
._websocket
._receive_data(new Uint8Array([]));
2035 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
2038 it('should handle a message in the connected state as a normal message', function () {
2039 client
.connect('host', 8675);
2040 client
._rfb_connection_state
= 'connected';
2041 client
._normal_msg
= sinon
.spy();
2042 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2043 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
2046 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2047 client
.connect('host', 8675);
2048 client
._rfb_init_state
= 'ProtocolVersion';
2049 client
._init_msg
= sinon
.spy();
2050 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2051 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
2054 it('should process all normal messages directly', function () {
2055 client
.connect('host', 8675);
2056 client
._sock
._websocket
._open();
2057 client
._rfb_connection_state
= 'connected';
2058 client
.set_onBell(sinon
.spy());
2059 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2060 expect(client
.get_onBell()).to
.have
.been
.calledTwice
;
2064 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2065 client
.connect('host', 8675);
2066 client
._sock
._websocket
._open();
2067 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2070 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2071 sinon
.spy(client
, "_fail");
2072 client
.connect('host', 8675);
2073 client
._rfb_connection_state
= 'some_other_state';
2074 client
._sock
._websocket
._open();
2075 expect(client
._fail
).to
.have
.been
.calledOnce
;
2079 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2080 client
.connect('host', 8675);
2081 client
._rfb_connection_state
= 'disconnecting';
2082 client
._sock
._websocket
.close();
2083 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2086 it('should fail if we get a close event while connecting', function () {
2087 sinon
.spy(client
, "_fail");
2088 client
.connect('host', 8675);
2089 client
._rfb_connection_state
= 'connecting';
2090 client
._sock
._websocket
.close();
2091 expect(client
._fail
).to
.have
.been
.calledOnce
;
2094 it('should fail if we get a close event while disconnected', function () {
2095 sinon
.spy(client
, "_fail");
2096 client
.connect('host', 8675);
2097 client
._rfb_connection_state
= 'disconnected';
2098 client
._sock
._websocket
.close();
2099 expect(client
._fail
).to
.have
.been
.calledOnce
;
2102 it('should unregister close event handler', function () {
2103 sinon
.spy(client
._sock
, 'off');
2104 client
.connect('host', 8675);
2105 client
._rfb_connection_state
= 'disconnecting';
2106 client
._sock
._websocket
.close();
2107 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2110 // error events do nothing