1 /* jshint expr: true */
2 var assert
= chai
.assert
;
3 var expect
= chai
.expect
;
5 import RFB
from '../core/rfb.js';
6 import Websock
from '../core/websock.js';
7 import { encodings
} from '../core/encodings.js';
9 import FakeWebSocket
from './fake.websocket.js';
10 import sinon
from '../vendor/sinon.js';
12 function make_rfb (extra_opts
) {
17 extra_opts
.target
= extra_opts
.target
|| document
.createElement('canvas');
18 return new RFB(extra_opts
);
21 var push8 = function (arr
, num
) {
26 var push16 = function (arr
, num
) {
28 arr
.push((num
>> 8) & 0xFF,
32 var push32 = function (arr
, num
) {
34 arr
.push((num
>> 24) & 0xFF,
40 describe('Remote Frame Buffer Protocol Client', function() {
42 before(FakeWebSocket
.replace
);
43 after(FakeWebSocket
.restore
);
46 this.clock
= sinon
.useFakeTimers();
47 // Use a single set of buffers instead of reallocating to
49 var sock
= new Websock();
50 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
51 var rQ
= new Uint8Array(sock
._rQbufferSize
);
53 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
54 Websock
.prototype._allocate_buffers = function () {
62 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
66 describe('Public API Basic Behavior', function () {
68 beforeEach(function () {
72 describe('#connect', function () {
73 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
75 it('should set the current state to "connecting"', function () {
76 client
.connect('host', 8675);
77 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
78 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('connecting');
81 it('should not try to connect if we are missing a host', function () {
82 client
._fail
= sinon
.spy();
83 client
._rfb_connection_state
= '';
84 client
.connect(undefined, 8675);
85 expect(client
._fail
).to
.have
.been
.calledOnce
;
86 expect(client
._updateConnectionState
).to
.not
.have
.been
.called
;
87 expect(client
._rfb_connection_state
).to
.equal('');
91 describe('#disconnect', function () {
92 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
94 it('should set the current state to "disconnecting"', function () {
96 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
97 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('disconnecting');
100 it('should unregister error event handler', function () {
101 sinon
.spy(client
._sock
, 'off');
103 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
106 it('should unregister message event handler', function () {
107 sinon
.spy(client
._sock
, 'off');
109 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
112 it('should unregister open event handler', function () {
113 sinon
.spy(client
._sock
, 'off');
115 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
119 describe('#sendCredentials', function () {
120 beforeEach(function () { this.clock
= sinon
.useFakeTimers(); });
121 afterEach(function () { this.clock
.restore(); });
123 it('should set the rfb credentials properly"', function () {
124 client
.sendCredentials({ password
: 'pass' });
125 expect(client
._rfb_credentials
).to
.deep
.equal({ password
: 'pass' });
128 it('should call init_msg "soon"', function () {
129 client
._init_msg
= sinon
.spy();
130 client
.sendCredentials({ password
: 'pass' });
132 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
136 describe('#sendCtrlAlDel', function () {
137 beforeEach(function () {
138 client
._sock
= new Websock();
139 client
._sock
.open('ws://', 'binary');
140 client
._sock
._websocket
._open();
141 sinon
.spy(client
._sock
, 'flush');
142 client
._rfb_connection_state
= 'connected';
143 client
._view_only
= false;
146 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
147 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
148 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
149 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
150 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
151 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
152 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
153 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
155 client
.sendCtrlAltDel();
156 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
159 it('should not send the keys if we are not in a normal state', function () {
160 client
._rfb_connection_state
= "broken";
161 client
.sendCtrlAltDel();
162 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
165 it('should not send the keys if we are set as view_only', function () {
166 client
._view_only
= true;
167 client
.sendCtrlAltDel();
168 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
172 describe('#sendKey', function () {
173 beforeEach(function () {
174 client
._sock
= new Websock();
175 client
._sock
.open('ws://', 'binary');
176 client
._sock
._websocket
._open();
177 sinon
.spy(client
._sock
, 'flush');
178 client
._rfb_connection_state
= 'connected';
179 client
._view_only
= false;
182 it('should send a single key with the given code and state (down = true)', function () {
183 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
184 RFB
.messages
.keyEvent(expected
, 123, 1);
185 client
.sendKey(123, 'Key123', true);
186 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
189 it('should send both a down and up event if the state is not specified', function () {
190 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
191 RFB
.messages
.keyEvent(expected
, 123, 1);
192 RFB
.messages
.keyEvent(expected
, 123, 0);
193 client
.sendKey(123, 'Key123');
194 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
197 it('should not send the key if we are not in a normal state', function () {
198 client
._rfb_connection_state
= "broken";
199 client
.sendKey(123, 'Key123');
200 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
203 it('should not send the key if we are set as view_only', function () {
204 client
._view_only
= true;
205 client
.sendKey(123, 'Key123');
206 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
209 it('should send QEMU extended events if supported', function () {
210 client
._qemuExtKeyEventSupported
= true;
211 var expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
212 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
213 client
.sendKey(0x20, 'Space', true);
214 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
217 it('should not send QEMU extended events if unknown key code', function () {
218 client
._qemuExtKeyEventSupported
= true;
219 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
220 RFB
.messages
.keyEvent(expected
, 123, 1);
221 client
.sendKey(123, 'FooBar', true);
222 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
226 describe('#clipboardPasteFrom', function () {
227 beforeEach(function () {
228 client
._sock
= new Websock();
229 client
._sock
.open('ws://', 'binary');
230 client
._sock
._websocket
._open();
231 sinon
.spy(client
._sock
, 'flush');
232 client
._rfb_connection_state
= 'connected';
233 client
._view_only
= false;
236 it('should send the given text in a paste event', function () {
237 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
238 RFB
.messages
.clientCutText(expected
, 'abc');
239 client
.clipboardPasteFrom('abc');
240 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
243 it('should not send the text if we are not in a normal state', function () {
244 client
._rfb_connection_state
= "broken";
245 client
.clipboardPasteFrom('abc');
246 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
250 describe("#requestDesktopSize", function () {
251 beforeEach(function() {
252 client
._sock
= new Websock();
253 client
._sock
.open('ws://', 'binary');
254 client
._sock
._websocket
._open();
255 sinon
.spy(client
._sock
, 'flush');
256 client
._rfb_connection_state
= 'connected';
257 client
._view_only
= false;
258 client
._supportsSetDesktopSize
= true;
261 it('should send the request with the given width and height', function () {
262 var expected
= [251];
263 push8(expected
, 0); // padding
264 push16(expected
, 1); // width
265 push16(expected
, 2); // height
266 push8(expected
, 1); // number-of-screens
267 push8(expected
, 0); // padding before screen array
268 push32(expected
, 0); // id
269 push16(expected
, 0); // x-position
270 push16(expected
, 0); // y-position
271 push16(expected
, 1); // width
272 push16(expected
, 2); // height
273 push32(expected
, 0); // flags
275 client
.requestDesktopSize(1, 2);
276 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
279 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
280 client
._supportsSetDesktopSize
= false;
281 client
.requestDesktopSize(1,2);
282 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
285 it('should not send the request if we are not in a normal state', function () {
286 client
._rfb_connection_state
= "broken";
287 client
.requestDesktopSize(1,2);
288 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
292 describe("XVP operations", function () {
293 beforeEach(function () {
294 client
._sock
= new Websock();
295 client
._sock
.open('ws://', 'binary');
296 client
._sock
._websocket
._open();
297 sinon
.spy(client
._sock
, 'flush');
298 client
._rfb_connection_state
= 'connected';
299 client
._view_only
= false;
300 client
._rfb_xvp_ver
= 1;
303 it('should send the shutdown signal on #machineShutdown', function () {
304 client
.machineShutdown();
305 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
308 it('should send the reboot signal on #machineReboot', function () {
309 client
.machineReboot();
310 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
313 it('should send the reset signal on #machineReset', function () {
314 client
.machineReset();
315 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
318 it('should not send XVP operations with higher versions than we support', function () {
320 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
325 describe('Misc Internals', function () {
326 describe('#_updateConnectionState', function () {
328 beforeEach(function () {
329 this.clock
= sinon
.useFakeTimers();
333 afterEach(function () {
334 this.clock
.restore();
337 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
338 var spy
= sinon
.spy();
339 client
._disconnTimer
= setTimeout(spy
, 50);
340 client
._updateConnectionState('connecting');
342 expect(spy
).to
.not
.have
.been
.called
;
343 expect(client
._disconnTimer
).to
.be
.null;
346 it('should call the updateState callback', function () {
347 client
.set_onUpdateState(sinon
.spy());
348 client
._updateConnectionState('connecting');
349 var spy
= client
.get_onUpdateState();
350 expect(spy
).to
.have
.been
.calledOnce
;
351 expect(spy
.args
[0][1]).to
.equal('connecting');
354 it('should set the rfb_connection_state', function () {
355 client
._rfb_connection_state
= 'disconnecting';
356 client
._updateConnectionState('disconnected');
357 expect(client
._rfb_connection_state
).to
.equal('disconnected');
360 it('should not change the state when we are disconnected', function () {
361 client
._rfb_connection_state
= 'disconnected';
362 client
._updateConnectionState('connecting');
363 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
366 it('should ignore state changes to the same state', function () {
367 client
.set_onUpdateState(sinon
.spy());
368 client
._rfb_connection_state
= 'connecting';
369 client
._updateConnectionState('connecting');
370 var spy
= client
.get_onUpdateState();
371 expect(spy
).to
.not
.have
.been
.called
;
374 it('should ignore illegal state changes', function () {
375 client
.set_onUpdateState(sinon
.spy());
376 client
._rfb_connection_state
= 'connected';
377 client
._updateConnectionState('disconnected');
378 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
379 var spy
= client
.get_onUpdateState();
380 expect(spy
).to
.not
.have
.been
.called
;
384 describe('#_fail', function () {
386 beforeEach(function () {
387 this.clock
= sinon
.useFakeTimers();
389 client
.connect('host', 8675);
392 afterEach(function () {
393 this.clock
.restore();
396 it('should close the WebSocket connection', function () {
397 sinon
.spy(client
._sock
, 'close');
399 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
402 it('should transition to disconnected', function () {
403 sinon
.spy(client
, '_updateConnectionState');
405 this.clock
.tick(2000);
406 expect(client
._updateConnectionState
).to
.have
.been
.called
;
407 expect(client
._rfb_connection_state
).to
.equal('disconnected');
410 it('should set disconnect_reason', function () {
411 client
._rfb_connection_state
= 'connected';
412 client
._fail('a reason');
413 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
416 it('should not include details in disconnect_reason', function () {
417 client
._rfb_connection_state
= 'connected';
418 client
._fail('a reason', 'details');
419 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
422 it('should result in disconnect callback with message when reason given', function () {
423 client
._rfb_connection_state
= 'connected';
424 client
.set_onDisconnected(sinon
.spy());
425 client
._fail('a reason');
426 var spy
= client
.get_onDisconnected();
427 this.clock
.tick(2000);
428 expect(spy
).to
.have
.been
.calledOnce
;
429 expect(spy
.args
[0].length
).to
.equal(2);
430 expect(spy
.args
[0][1]).to
.equal('a reason');
435 describe('#_notification', function () {
437 beforeEach(function () { client
= make_rfb(); });
439 it('should call the notification callback', function () {
440 client
.set_onNotification(sinon
.spy());
441 client
._notification('notify!', 'warn');
442 var spy
= client
.get_onNotification();
443 expect(spy
).to
.have
.been
.calledOnce
;
444 expect(spy
.args
[0][1]).to
.equal('notify!');
445 expect(spy
.args
[0][2]).to
.equal('warn');
448 it('should not call the notification callback when level is invalid', function () {
449 client
.set_onNotification(sinon
.spy());
450 client
._notification('notify!', 'invalid');
451 var spy
= client
.get_onNotification();
452 expect(spy
).to
.not
.have
.been
.called
;
457 describe('Connection States', function () {
458 describe('connecting', function () {
460 beforeEach(function () { client
= make_rfb(); });
462 it('should reset the variable states', function () {
463 sinon
.spy(client
, '_init_vars');
464 client
._updateConnectionState('connecting');
465 expect(client
._init_vars
).to
.have
.been
.calledOnce
;
468 it('should actually connect to the websocket', function () {
469 sinon
.spy(client
._sock
, 'open');
470 client
._updateConnectionState('connecting');
471 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
474 it('should use wss:// to connect if encryption is 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 ws:// to connect if encryption is not enabled', function () {
482 sinon
.spy(client
._sock
, 'open');
483 client
.set_encrypt(true);
484 client
._updateConnectionState('connecting');
485 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
488 it('should use a uri with the host, port, and path specified to connect', function () {
489 sinon
.spy(client
._sock
, 'open');
490 client
.set_encrypt(false);
491 client
._rfb_host
= 'HOST';
492 client
._rfb_port
= 8675;
493 client
._rfb_path
= 'PATH';
494 client
._updateConnectionState('connecting');
495 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
498 it('should not include a port in the uri if not specified in connect', function () {
499 sinon
.spy(client
._sock
, 'open');
500 client
.set_encrypt(true);
501 client
.connect('HOST', undefined)
502 expect(client
._sock
.open
).to
.have
.been
.calledWith('wss://HOST/');
506 describe('disconnecting', function () {
508 beforeEach(function () {
509 this.clock
= sinon
.useFakeTimers();
511 client
.connect('host', 8675);
514 afterEach(function () {
515 this.clock
.restore();
518 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
519 sinon
.spy(client
, '_updateConnectionState');
520 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
521 client
._updateConnectionState('disconnecting');
522 this.clock
.tick(client
.get_disconnectTimeout() * 1000);
523 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
524 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
525 expect(client
._rfb_connection_state
).to
.equal("disconnected");
528 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
529 client
._updateConnectionState('disconnecting');
530 this.clock
.tick(client
.get_disconnectTimeout() * 500);
531 client
._sock
._websocket
.close();
532 this.clock
.tick(client
.get_disconnectTimeout() * 500 + 1);
533 expect(client
._rfb_connection_state
).to
.equal('disconnected');
536 it('should close the WebSocket connection', function () {
537 sinon
.spy(client
._sock
, 'close');
538 client
._updateConnectionState('disconnecting');
539 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
543 describe('disconnected', function () {
545 beforeEach(function () { client
= make_rfb(); });
547 it('should call the disconnect callback if the state is "disconnected"', function () {
548 client
.set_onDisconnected(sinon
.spy());
549 client
._rfb_connection_state
= 'disconnecting';
550 client
._rfb_disconnect_reason
= "error";
551 client
._updateConnectionState('disconnected');
552 var spy
= client
.get_onDisconnected();
553 expect(spy
).to
.have
.been
.calledOnce
;
554 expect(spy
.args
[0][1]).to
.equal("error");
557 it('should not call the disconnect callback if the state is not "disconnected"', function () {
558 client
.set_onDisconnected(sinon
.spy());
559 client
._updateConnectionState('disconnecting');
560 var spy
= client
.get_onDisconnected();
561 expect(spy
).to
.not
.have
.been
.called
;
564 it('should call the disconnect callback without msg when no reason given', function () {
565 client
.set_onDisconnected(sinon
.spy());
566 client
._rfb_connection_state
= 'disconnecting';
567 client
._rfb_disconnect_reason
= "";
568 client
._updateConnectionState('disconnected');
569 var spy
= client
.get_onDisconnected();
570 expect(spy
).to
.have
.been
.calledOnce
;
571 expect(spy
.args
[0].length
).to
.equal(1);
574 it('should call the updateState callback before the disconnect callback', function () {
575 client
.set_onDisconnected(sinon
.spy());
576 client
.set_onUpdateState(sinon
.spy());
577 client
._rfb_connection_state
= 'disconnecting';
578 client
._updateConnectionState('disconnected');
579 var updateStateSpy
= client
.get_onUpdateState();
580 var disconnectSpy
= client
.get_onDisconnected();
581 expect(updateStateSpy
.calledBefore(disconnectSpy
)).to
.be
.true;
585 // NB(directxman12): Connected does *nothing* in updateConnectionState
588 describe('Protocol Initialization States', function () {
589 describe('ProtocolVersion', function () {
590 beforeEach(function () {
591 this.clock
= sinon
.useFakeTimers();
594 afterEach(function () {
595 this.clock
.restore();
598 function send_ver (ver
, client
) {
599 var arr
= new Uint8Array(12);
600 for (var i
= 0; i
< ver
.length
; i
++) {
601 arr
[i
+4] = ver
.charCodeAt(i
);
603 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
605 client
._sock
._websocket
._receive_data(arr
);
608 describe('version parsing', function () {
610 beforeEach(function () {
612 client
.connect('host', 8675);
613 client
._sock
._websocket
._open();
616 it('should interpret version 000.000 as a repeater', function () {
617 client
._repeaterID
= '\x01\x02\x03\x04\x05';
618 send_ver('000.000', client
);
619 expect(client
._rfb_version
).to
.equal(0);
621 var sent_data
= client
._sock
._websocket
._get_sent_data();
622 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
625 it('should interpret version 003.003 as version 3.3', function () {
626 send_ver('003.003', client
);
627 expect(client
._rfb_version
).to
.equal(3.3);
630 it('should interpret version 003.006 as version 3.3', function () {
631 send_ver('003.006', client
);
632 expect(client
._rfb_version
).to
.equal(3.3);
635 it('should interpret version 003.889 as version 3.3', function () {
636 send_ver('003.889', client
);
637 expect(client
._rfb_version
).to
.equal(3.3);
640 it('should interpret version 003.007 as version 3.7', function () {
641 send_ver('003.007', client
);
642 expect(client
._rfb_version
).to
.equal(3.7);
645 it('should interpret version 003.008 as version 3.8', function () {
646 send_ver('003.008', client
);
647 expect(client
._rfb_version
).to
.equal(3.8);
650 it('should interpret version 004.000 as version 3.8', function () {
651 send_ver('004.000', client
);
652 expect(client
._rfb_version
).to
.equal(3.8);
655 it('should interpret version 004.001 as version 3.8', function () {
656 send_ver('004.001', client
);
657 expect(client
._rfb_version
).to
.equal(3.8);
660 it('should interpret version 005.000 as version 3.8', function () {
661 send_ver('005.000', client
);
662 expect(client
._rfb_version
).to
.equal(3.8);
665 it('should fail on an invalid version', function () {
666 sinon
.spy(client
, "_fail");
667 send_ver('002.000', client
);
668 expect(client
._fail
).to
.have
.been
.calledOnce
;
673 beforeEach(function () {
675 client
.connect('host', 8675);
676 client
._sock
._websocket
._open();
679 it('should handle two step repeater negotiation', function () {
680 client
._repeaterID
= '\x01\x02\x03\x04\x05';
682 send_ver('000.000', client
);
683 expect(client
._rfb_version
).to
.equal(0);
684 var sent_data
= client
._sock
._websocket
._get_sent_data();
685 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
686 expect(sent_data
).to
.have
.length(250);
688 send_ver('003.008', client
);
689 expect(client
._rfb_version
).to
.equal(3.8);
692 it('should send back the interpreted version', function () {
693 send_ver('004.000', client
);
695 var expected_str
= 'RFB 003.008\n';
697 for (var i
= 0; i
< expected_str
.length
; i
++) {
698 expected
[i
] = expected_str
.charCodeAt(i
);
701 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
704 it('should transition to the Security state on successful negotiation', function () {
705 send_ver('003.008', client
);
706 expect(client
._rfb_init_state
).to
.equal('Security');
710 describe('Security', function () {
713 beforeEach(function () {
715 client
.connect('host', 8675);
716 client
._sock
._websocket
._open();
717 client
._rfb_init_state
= 'Security';
720 it('should simply receive the auth scheme when for versions < 3.7', function () {
721 client
._rfb_version
= 3.6;
722 var auth_scheme_raw
= [1, 2, 3, 4];
723 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
724 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
725 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
726 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
729 it('should prefer no authentication is possible', function () {
730 client
._rfb_version
= 3.7;
731 var auth_schemes
= [2, 1, 3];
732 client
._sock
._websocket
._receive_data(auth_schemes
);
733 expect(client
._rfb_auth_scheme
).to
.equal(1);
734 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
737 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
738 client
._rfb_version
= 3.7;
739 var auth_schemes
= [2, 22, 16];
740 client
._sock
._websocket
._receive_data(auth_schemes
);
741 expect(client
._rfb_auth_scheme
).to
.equal(22);
742 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
745 it('should fail if there are no supported schemes for versions >= 3.7', function () {
746 sinon
.spy(client
, "_fail");
747 client
._rfb_version
= 3.7;
748 var auth_schemes
= [1, 32];
749 client
._sock
._websocket
._receive_data(auth_schemes
);
750 expect(client
._fail
).to
.have
.been
.calledOnce
;
753 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
754 client
._rfb_version
= 3.7;
755 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
756 sinon
.spy(client
, '_fail');
757 client
._sock
._websocket
._receive_data(failure_data
);
759 expect(client
._fail
).to
.have
.been
.calledOnce
;
760 expect(client
._fail
).to
.have
.been
.calledWith(
761 'Error while negotiating with server','Security failure: whoops');
764 it('should transition to the Authentication state and continue on successful negotiation', function () {
765 client
._rfb_version
= 3.7;
766 var auth_schemes
= [1, 1];
767 client
._negotiate_authentication
= sinon
.spy();
768 client
._sock
._websocket
._receive_data(auth_schemes
);
769 expect(client
._rfb_init_state
).to
.equal('Authentication');
770 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
774 describe('Authentication', function () {
777 beforeEach(function () {
779 client
.connect('host', 8675);
780 client
._sock
._websocket
._open();
781 client
._rfb_init_state
= 'Security';
784 function send_security(type
, cl
) {
785 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
788 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
789 client
._rfb_version
= 3.6;
790 var err_msg
= "Whoopsies";
791 var data
= [0, 0, 0, 0];
792 var err_len
= err_msg
.length
;
793 push32(data
, err_len
);
794 for (var i
= 0; i
< err_len
; i
++) {
795 data
.push(err_msg
.charCodeAt(i
));
798 sinon
.spy(client
, '_fail');
799 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
800 expect(client
._fail
).to
.have
.been
.calledWith(
801 'Authentication failure', 'Whoopsies');
804 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
805 client
._rfb_version
= 3.8;
806 send_security(1, client
);
807 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
810 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
811 client
._rfb_version
= 3.7;
812 send_security(1, client
);
813 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
816 it('should fail on an unknown auth scheme', function () {
817 sinon
.spy(client
, "_fail");
818 client
._rfb_version
= 3.8;
819 send_security(57, client
);
820 expect(client
._fail
).to
.have
.been
.calledOnce
;
823 describe('VNC Authentication (type 2) Handler', function () {
826 beforeEach(function () {
828 client
.connect('host', 8675);
829 client
._sock
._websocket
._open();
830 client
._rfb_init_state
= 'Security';
831 client
._rfb_version
= 3.8;
834 it('should call the onCredentialsRequired callback if missing a password', function () {
835 client
.set_onCredentialsRequired(sinon
.spy());
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 var spy
= client
.get_onCredentialsRequired();
843 expect(client
._rfb_credentials
).to
.be
.empty
;
844 expect(spy
).to
.have
.been
.calledOnce
;
845 expect(spy
.args
[0][1]).to
.have
.members(["password"]);
848 it('should encrypt the password with DES and then send it back', function () {
849 client
._rfb_credentials
= { password
: 'passwd' };
850 send_security(2, client
);
851 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
854 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
855 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
857 var des_pass
= RFB
.genDES('passwd', challenge
);
858 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
861 it('should transition to SecurityResult immediately after sending the password', function () {
862 client
._rfb_credentials
= { password
: 'passwd' };
863 send_security(2, client
);
866 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
867 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
869 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
873 describe('XVP Authentication (type 22) Handler', function () {
876 beforeEach(function () {
878 client
.connect('host', 8675);
879 client
._sock
._websocket
._open();
880 client
._rfb_init_state
= 'Security';
881 client
._rfb_version
= 3.8;
884 it('should fall through to standard VNC authentication upon completion', function () {
885 client
._rfb_credentials
= { username
: 'user',
887 password
: 'password' };
888 client
._negotiate_std_vnc_auth
= sinon
.spy();
889 send_security(22, client
);
890 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
893 it('should call the onCredentialsRequired callback if all credentials are missing', function() {
894 client
.set_onCredentialsRequired(sinon
.spy());
895 client
._rfb_credentials
= {};
896 send_security(22, client
);
898 var spy
= client
.get_onCredentialsRequired();
899 expect(client
._rfb_credentials
).to
.be
.empty
;
900 expect(spy
).to
.have
.been
.calledOnce
;
901 expect(spy
.args
[0][1]).to
.have
.members(["username", "password", "target"]);
904 it('should call the onCredentialsRequired callback if some credentials are missing', function() {
905 client
.set_onCredentialsRequired(sinon
.spy());
906 client
._rfb_credentials
= { username
: 'user',
908 send_security(22, client
);
910 var spy
= client
.get_onCredentialsRequired();
911 expect(spy
).to
.have
.been
.calledOnce
;
912 expect(spy
.args
[0][1]).to
.have
.members(["username", "password", "target"]);
915 it('should send user and target separately', function () {
916 client
._rfb_credentials
= { username
: 'user',
918 password
: 'password' };
919 client
._negotiate_std_vnc_auth
= sinon
.spy();
921 send_security(22, client
);
923 var expected
= [22, 4, 6]; // auth selection, len user, len target
924 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
926 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
930 describe('TightVNC Authentication (type 16) Handler', function () {
933 beforeEach(function () {
935 client
.connect('host', 8675);
936 client
._sock
._websocket
._open();
937 client
._rfb_init_state
= 'Security';
938 client
._rfb_version
= 3.8;
939 send_security(16, client
);
940 client
._sock
._websocket
._get_sent_data(); // skip the security reply
943 function send_num_str_pairs(pairs
, client
) {
944 var pairs_len
= pairs
.length
;
946 push32(data
, pairs_len
);
948 for (var i
= 0; i
< pairs_len
; i
++) {
949 push32(data
, pairs
[i
][0]);
951 for (j
= 0; j
< 4; j
++) {
952 data
.push(pairs
[i
][1].charCodeAt(j
));
954 for (j
= 0; j
< 8; j
++) {
955 data
.push(pairs
[i
][2].charCodeAt(j
));
959 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
962 it('should skip tunnel negotiation if no tunnels are requested', function () {
963 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
964 expect(client
._rfb_tightvnc
).to
.be
.true;
967 it('should fail if no supported tunnels are listed', function () {
968 sinon
.spy(client
, "_fail");
969 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
970 expect(client
._fail
).to
.have
.been
.calledOnce
;
973 it('should choose the notunnel tunnel type', function () {
974 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
975 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
978 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
979 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
980 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
981 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
982 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
983 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
986 /*it('should attempt to use VNC auth over no auth when possible', function () {
987 client._rfb_tightvnc = true;
988 client._negotiate_std_vnc_auth = sinon.spy();
989 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
990 expect(client._sock).to.have.sent([0, 0, 0, 1]);
991 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
992 expect(client._rfb_auth_scheme).to.equal(2);
993 });*/ // while this would make sense, the original code doesn't actually do this
995 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
996 client
._rfb_tightvnc
= true;
997 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
998 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
999 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1002 it('should accept VNC authentication and transition to that', function () {
1003 client
._rfb_tightvnc
= true;
1004 client
._negotiate_std_vnc_auth
= sinon
.spy();
1005 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
1006 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1007 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1008 expect(client
._rfb_auth_scheme
).to
.equal(2);
1011 it('should fail if there are no supported auth types', function () {
1012 sinon
.spy(client
, "_fail");
1013 client
._rfb_tightvnc
= true;
1014 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
1015 expect(client
._fail
).to
.have
.been
.calledOnce
;
1020 describe('SecurityResult', function () {
1023 beforeEach(function () {
1024 client
= make_rfb();
1025 client
.connect('host', 8675);
1026 client
._sock
._websocket
._open();
1027 client
._rfb_init_state
= 'SecurityResult';
1030 it('should fall through to ServerInitialisation on a response code of 0', function () {
1031 client
._updateConnectionState
= sinon
.spy();
1032 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1033 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1036 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1037 client
._rfb_version
= 3.8;
1038 sinon
.spy(client
, '_fail');
1039 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1040 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1041 expect(client
._fail
).to
.have
.been
.calledWith(
1042 'Authentication failure', 'whoops');
1045 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1046 sinon
.spy(client
, '_fail');
1047 client
._rfb_version
= 3.7;
1048 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
1049 expect(client
._fail
).to
.have
.been
.calledWith('Authentication failure');
1053 describe('ClientInitialisation', function () {
1056 beforeEach(function () {
1057 client
= make_rfb();
1058 client
.connect('host', 8675);
1059 client
._sock
._websocket
._open();
1060 client
._rfb_init_state
= 'SecurityResult';
1063 it('should transition to the ServerInitialisation state', function () {
1064 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1065 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1068 it('should send 1 if we are in shared mode', function () {
1069 client
.set_shared(true);
1070 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1071 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1074 it('should send 0 if we are not in shared mode', function () {
1075 client
.set_shared(false);
1076 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1077 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1081 describe('ServerInitialisation', function () {
1084 beforeEach(function () {
1085 client
= make_rfb();
1086 client
.connect('host', 8675);
1087 client
._sock
._websocket
._open();
1088 client
._rfb_init_state
= 'ServerInitialisation';
1091 function send_server_init(opts
, client
) {
1092 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1093 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1094 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1095 for (var opt
in opts
) {
1096 full_opts
[opt
] = opts
[opt
];
1100 push16(data
, full_opts
.width
);
1101 push16(data
, full_opts
.height
);
1103 data
.push(full_opts
.bpp
);
1104 data
.push(full_opts
.depth
);
1105 data
.push(full_opts
.big_endian
);
1106 data
.push(full_opts
.true_color
);
1108 push16(data
, full_opts
.red_max
);
1109 push16(data
, full_opts
.green_max
);
1110 push16(data
, full_opts
.blue_max
);
1111 push8(data
, full_opts
.red_shift
);
1112 push8(data
, full_opts
.green_shift
);
1113 push8(data
, full_opts
.blue_shift
);
1120 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1123 push32(name_data
, full_opts
.name
.length
);
1124 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1125 name_data
.push(full_opts
.name
.charCodeAt(i
));
1127 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1130 it('should set the framebuffer width and height', function () {
1131 send_server_init({ width
: 32, height
: 84 }, client
);
1132 expect(client
._fb_width
).to
.equal(32);
1133 expect(client
._fb_height
).to
.equal(84);
1136 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1138 it('should set the framebuffer name and call the callback', function () {
1139 client
.set_onDesktopName(sinon
.spy());
1140 send_server_init({ name
: 'some name' }, client
);
1142 var spy
= client
.get_onDesktopName();
1143 expect(client
._fb_name
).to
.equal('some name');
1144 expect(spy
).to
.have
.been
.calledOnce
;
1145 expect(spy
.args
[0][1]).to
.equal('some name');
1148 it('should handle the extended init message of the tight encoding', function () {
1149 // NB(sross): we don't actually do anything with it, so just test that we can
1150 // read it w/o throwing an error
1151 client
._rfb_tightvnc
= true;
1152 send_server_init({}, client
);
1154 var tight_data
= [];
1155 push16(tight_data
, 1);
1156 push16(tight_data
, 2);
1157 push16(tight_data
, 3);
1158 push16(tight_data
, 0);
1159 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1162 client
._sock
._websocket
._receive_data(tight_data
);
1164 expect(client
._rfb_connection_state
).to
.equal('connected');
1167 it('should call the resize callback and resize the display', function () {
1168 client
.set_onFBResize(sinon
.spy());
1169 sinon
.spy(client
._display
, 'resize');
1170 send_server_init({ width
: 27, height
: 32 }, client
);
1172 var spy
= client
.get_onFBResize();
1173 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1174 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1175 expect(spy
).to
.have
.been
.calledOnce
;
1176 expect(spy
.args
[0][1]).to
.equal(27);
1177 expect(spy
.args
[0][2]).to
.equal(32);
1180 it('should grab the mouse and keyboard', function () {
1181 sinon
.spy(client
._keyboard
, 'grab');
1182 sinon
.spy(client
._mouse
, 'grab');
1183 send_server_init({}, client
);
1184 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1185 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1188 describe('Initial Update Request', function () {
1189 beforeEach(function () {
1190 sinon
.spy(RFB
.messages
, "pixelFormat");
1191 sinon
.spy(RFB
.messages
, "clientEncodings");
1192 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1195 afterEach(function () {
1196 RFB
.messages
.pixelFormat
.restore();
1197 RFB
.messages
.clientEncodings
.restore();
1198 RFB
.messages
.fbUpdateRequest
.restore();
1201 // TODO(directxman12): test the various options in this configuration matrix
1202 it('should reply with the pixel format, client encodings, and initial update request', function () {
1203 send_server_init({ width
: 27, height
: 32 }, client
);
1205 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1206 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1207 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1208 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1209 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1210 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1211 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1212 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1215 it('should reply with restricted settings for Intel AMT servers', function () {
1216 send_server_init({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1218 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1219 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1220 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1221 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1222 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1223 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1224 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1225 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1226 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1230 it('should transition to the "connected" state', function () {
1231 send_server_init({}, client
);
1232 expect(client
._rfb_connection_state
).to
.equal('connected');
1237 describe('Protocol Message Processing After Completing Initialization', function () {
1240 beforeEach(function () {
1241 client
= make_rfb();
1242 client
.connect('host', 8675);
1243 client
._sock
._websocket
._open();
1244 client
._rfb_connection_state
= 'connected';
1245 client
._fb_name
= 'some device';
1246 client
._fb_width
= 640;
1247 client
._fb_height
= 20;
1250 describe('Framebuffer Update Handling', function () {
1253 beforeEach(function () {
1254 client
= make_rfb();
1255 client
.connect('host', 8675);
1256 client
._sock
._websocket
._open();
1257 client
._rfb_connection_state
= 'connected';
1258 client
._fb_name
= 'some device';
1259 client
._fb_width
= 640;
1260 client
._fb_height
= 20;
1263 var target_data_arr
= [
1264 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1265 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1266 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1267 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1271 var target_data_check_arr
= [
1272 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1273 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1274 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1275 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1277 var target_data_check
;
1279 before(function () {
1280 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1281 target_data
= new Uint8Array(target_data_arr
);
1282 target_data_check
= new Uint8Array(target_data_check_arr
);
1285 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1288 if (!rect_cnt
|| rect_cnt
> -1) {
1290 data
.push(0); // msg type
1291 data
.push(0); // padding
1292 push16(data
, rect_cnt
|| rect_data
.length
);
1295 for (var i
= 0; i
< rect_data
.length
; i
++) {
1297 push16(data
, rect_info
[i
].x
);
1298 push16(data
, rect_info
[i
].y
);
1299 push16(data
, rect_info
[i
].width
);
1300 push16(data
, rect_info
[i
].height
);
1301 push32(data
, rect_info
[i
].encoding
);
1303 data
= data
.concat(rect_data
[i
]);
1306 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1309 it('should send an update request if there is sufficient data', function () {
1310 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1311 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1313 client
._framebufferUpdate = function () { return true; };
1314 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1316 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1319 it('should not send an update request if we need more data', function () {
1320 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1321 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1324 it('should resume receiving an update if we previously did not have enough data', function () {
1325 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1326 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1328 // just enough to set FBU.rects
1329 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1330 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1332 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1333 // 247 should *not* be used as the message type here
1334 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1335 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1338 it('should not send a request in continuous updates mode', function () {
1339 client
._enabledContinuousUpdates
= true;
1340 client
._framebufferUpdate = function () { return true; };
1341 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1343 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1346 it('should parse out information from a header before any actual data comes in', function () {
1347 client
.set_onFBUReceive(sinon
.spy());
1348 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02, encodingName
: 'RRE' };
1349 send_fbu_msg([rect_info
], [[]], client
);
1351 var spy
= client
.get_onFBUReceive();
1352 expect(spy
).to
.have
.been
.calledOnce
;
1353 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1356 it('should fire onFBUComplete when the update is complete', function () {
1357 client
.set_onFBUComplete(sinon
.spy());
1358 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: -224, encodingName
: 'last_rect' };
1359 send_fbu_msg([rect_info
], [[]], client
); // last_rect
1361 var spy
= client
.get_onFBUComplete();
1362 expect(spy
).to
.have
.been
.calledOnce
;
1365 it('should not fire onFBUComplete if we have not finished processing the update', function () {
1366 client
.set_onFBUComplete(sinon
.spy());
1367 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x00, encodingName
: 'RAW' };
1368 send_fbu_msg([rect_info
], [[]], client
);
1369 expect(client
.get_onFBUComplete()).to
.not
.have
.been
.called
;
1372 it('should fail on an unsupported encoding', function () {
1373 sinon
.spy(client
, "_fail");
1374 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1375 send_fbu_msg([rect_info
], [[]], client
);
1376 expect(client
._fail
).to
.have
.been
.calledOnce
;
1379 it('should be able to pause and resume receiving rects if not enought data', function () {
1380 // seed some initial data to copy
1381 client
._fb_width
= 4;
1382 client
._fb_height
= 4;
1383 client
._display
.resize(4, 4);
1384 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1386 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1387 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1388 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1389 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1390 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1391 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1392 expect(client
._display
).to
.have
.displayed(target_data_check
);
1395 describe('Message Encoding Handlers', function () {
1398 beforeEach(function () {
1399 client
= make_rfb();
1400 client
.connect('host', 8675);
1401 client
._sock
._websocket
._open();
1402 client
._rfb_connection_state
= 'connected';
1403 client
._fb_name
= 'some device';
1404 // a really small frame
1405 client
._fb_width
= 4;
1406 client
._fb_height
= 4;
1407 client
._fb_depth
= 24;
1408 client
._display
.resize(4, 4);
1411 it('should handle the RAW encoding', function () {
1412 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1413 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1414 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1415 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1418 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1419 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1420 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1421 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1422 send_fbu_msg(info
, rects
, client
);
1423 expect(client
._display
).to
.have
.displayed(target_data
);
1426 it('should handle the RAW encoding in low colour mode', function () {
1427 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1428 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1429 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1430 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1432 [0x03, 0x03, 0x03, 0x03],
1433 [0x0c, 0x0c, 0x0c, 0x0c],
1434 [0x0c, 0x0c, 0x03, 0x03],
1435 [0x0c, 0x0c, 0x03, 0x03]];
1436 client
._fb_depth
= 8;
1437 send_fbu_msg(info
, rects
, client
);
1438 expect(client
._display
).to
.have
.displayed(target_data_check
);
1441 it('should handle the COPYRECT encoding', function () {
1442 // seed some initial data to copy
1443 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1445 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1446 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1447 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1448 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1449 send_fbu_msg(info
, rects
, client
);
1450 expect(client
._display
).to
.have
.displayed(target_data_check
);
1453 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1454 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1456 it('should handle the RRE encoding', function () {
1457 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1459 push32(rect
, 2); // 2 subrects
1460 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1461 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1465 push16(rect
, 0); // x: 0
1466 push16(rect
, 0); // y: 0
1467 push16(rect
, 2); // width: 2
1468 push16(rect
, 2); // height: 2
1469 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1473 push16(rect
, 2); // x: 2
1474 push16(rect
, 2); // y: 2
1475 push16(rect
, 2); // width: 2
1476 push16(rect
, 2); // height: 2
1478 send_fbu_msg(info
, [rect
], client
);
1479 expect(client
._display
).to
.have
.displayed(target_data_check
);
1482 describe('the HEXTILE encoding handler', function () {
1484 beforeEach(function () {
1485 client
= make_rfb();
1486 client
.connect('host', 8675);
1487 client
._sock
._websocket
._open();
1488 client
._rfb_connection_state
= 'connected';
1489 client
._fb_name
= 'some device';
1490 // a really small frame
1491 client
._fb_width
= 4;
1492 client
._fb_height
= 4;
1493 client
._display
.resize(4, 4);
1496 it('should handle a tile with fg, bg specified, normal subrects', function () {
1497 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1499 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1500 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1501 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1505 rect
.push(2); // 2 subrects
1506 rect
.push(0); // x: 0, y: 0
1507 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1508 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1509 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1510 send_fbu_msg(info
, [rect
], client
);
1511 expect(client
._display
).to
.have
.displayed(target_data_check
);
1514 it('should handle a raw tile', function () {
1515 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1517 rect
.push(0x01); // raw
1518 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1519 rect
.push(target_data
[i
+ 2]);
1520 rect
.push(target_data
[i
+ 1]);
1521 rect
.push(target_data
[i
]);
1522 rect
.push(target_data
[i
+ 3]);
1524 send_fbu_msg(info
, [rect
], client
);
1525 expect(client
._display
).to
.have
.displayed(target_data
);
1528 it('should handle a tile with only bg specified (solid bg)', function () {
1529 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1532 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1533 send_fbu_msg(info
, [rect
], client
);
1536 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1537 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1540 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1541 // set the width so we can have two tiles
1542 client
._fb_width
= 8;
1543 client
._display
.resize(8, 4);
1545 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1551 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1553 // send an empty frame
1556 send_fbu_msg(info
, [rect
], client
);
1560 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1561 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1562 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1565 it('should handle a tile with bg and coloured subrects', function () {
1566 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1568 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1569 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1570 rect
.push(2); // 2 subrects
1571 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1575 rect
.push(0); // x: 0, y: 0
1576 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1577 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1581 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1582 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1583 send_fbu_msg(info
, [rect
], client
);
1584 expect(client
._display
).to
.have
.displayed(target_data_check
);
1587 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1588 client
._fb_width
= 4;
1589 client
._fb_height
= 17;
1590 client
._display
.resize(4, 17);
1592 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1594 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1595 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1596 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1600 rect
.push(8); // 8 subrects
1602 for (i
= 0; i
< 4; i
++) {
1603 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1604 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1605 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1606 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1608 rect
.push(0x08); // anysubrects
1609 rect
.push(1); // 1 subrect
1610 rect
.push(0); // x: 0, y: 0
1611 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1612 send_fbu_msg(info
, [rect
], client
);
1615 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1616 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1617 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1620 it('should fail on an invalid subencoding', function () {
1621 sinon
.spy(client
,"_fail");
1622 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1623 var rects
= [[45]]; // an invalid subencoding
1624 send_fbu_msg(info
, rects
, client
);
1625 expect(client
._fail
).to
.have
.been
.calledOnce
;
1629 it
.skip('should handle the TIGHT encoding', function () {
1630 // TODO(directxman12): test this
1633 it
.skip('should handle the TIGHT_PNG encoding', function () {
1634 // TODO(directxman12): test this
1637 it('should handle the DesktopSize pseduo-encoding', function () {
1638 client
.set_onFBResize(sinon
.spy());
1639 sinon
.spy(client
._display
, 'resize');
1640 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1642 var spy
= client
.get_onFBResize();
1643 expect(spy
).to
.have
.been
.calledOnce
;
1644 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1646 expect(client
._fb_width
).to
.equal(20);
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(20, 50);
1653 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1656 beforeEach(function () {
1657 client
= make_rfb();
1658 client
.connect('host', 8675);
1659 client
._sock
._websocket
._open();
1660 client
._rfb_connection_state
= 'connected';
1661 client
._fb_name
= 'some device';
1662 client
._supportsSetDesktopSize
= false;
1663 // a really small frame
1664 client
._fb_width
= 4;
1665 client
._fb_height
= 4;
1666 client
._display
.resize(4, 4);
1667 sinon
.spy(client
._display
, 'resize');
1668 client
.set_onFBResize(sinon
.spy());
1671 function make_screen_data (nr_of_screens
) {
1673 push8(data
, nr_of_screens
); // number-of-screens
1674 push8(data
, 0); // padding
1675 push16(data
, 0); // padding
1676 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1677 push32(data
, 0); // id
1678 push16(data
, 0); // x-position
1679 push16(data
, 0); // y-position
1680 push16(data
, 20); // width
1681 push16(data
, 50); // height
1682 push32(data
, 0); // flags
1687 it('should call callback when resize is supported', function () {
1688 client
.set_onCapabilities(sinon
.spy());
1690 expect(client
._supportsSetDesktopSize
).to
.be
.false;
1691 expect(client
.get_capabilities().resize
).to
.be
.false;
1693 var reason_for_change
= 0; // server initiated
1694 var status_code
= 0; // No error
1696 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1697 width
: 4, height
: 4, encoding
: -308 }],
1698 make_screen_data(1), client
);
1700 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1701 expect(client
.get_onCapabilities()).to
.have
.been
.calledOnce
;
1702 expect(client
.get_onCapabilities().args
[0][1].resize
).to
.be
.true;
1703 expect(client
.get_capabilities().resize
).to
.be
.true;
1706 it('should handle a resize requested by this client', function () {
1707 var reason_for_change
= 1; // requested by this client
1708 var status_code
= 0; // No error
1710 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1711 width
: 20, height
: 50, encoding
: -308 }],
1712 make_screen_data(1), client
);
1714 expect(client
._fb_width
).to
.equal(20);
1715 expect(client
._fb_height
).to
.equal(50);
1717 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1718 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1720 var spy
= client
.get_onFBResize();
1721 expect(spy
).to
.have
.been
.calledOnce
;
1722 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1725 it('should handle a resize requested by another client', function () {
1726 var reason_for_change
= 2; // requested by another client
1727 var status_code
= 0; // No error
1729 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1730 width
: 20, height
: 50, encoding
: -308 }],
1731 make_screen_data(1), client
);
1733 expect(client
._fb_width
).to
.equal(20);
1734 expect(client
._fb_height
).to
.equal(50);
1736 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1737 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1739 var spy
= client
.get_onFBResize();
1740 expect(spy
).to
.have
.been
.calledOnce
;
1741 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1744 it('should be able to recieve requests which contain data for multiple screens', function () {
1745 var reason_for_change
= 2; // requested by another client
1746 var status_code
= 0; // No error
1748 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1749 width
: 60, height
: 50, encoding
: -308 }],
1750 make_screen_data(3), client
);
1752 expect(client
._fb_width
).to
.equal(60);
1753 expect(client
._fb_height
).to
.equal(50);
1755 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1756 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1758 var spy
= client
.get_onFBResize();
1759 expect(spy
).to
.have
.been
.calledOnce
;
1760 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 60, 50);
1763 it('should not handle a failed request', function () {
1764 var reason_for_change
= 1; // requested by this client
1765 var status_code
= 1; // Resize is administratively prohibited
1767 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1768 width
: 20, height
: 50, encoding
: -308 }],
1769 make_screen_data(1), client
);
1771 expect(client
._fb_width
).to
.equal(4);
1772 expect(client
._fb_height
).to
.equal(4);
1774 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1776 var spy
= client
.get_onFBResize();
1777 expect(spy
).to
.not
.have
.been
.called
;
1781 it
.skip('should handle the Cursor pseudo-encoding', function () {
1782 // TODO(directxman12): test
1785 it('should handle the last_rect pseudo-encoding', function () {
1786 client
.set_onFBUReceive(sinon
.spy());
1787 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1788 expect(client
._FBU
.rects
).to
.equal(0);
1789 expect(client
.get_onFBUReceive()).to
.have
.been
.calledOnce
;
1794 describe('XVP Message Handling', function () {
1795 beforeEach(function () {
1796 client
= make_rfb();
1797 client
.connect('host', 8675);
1798 client
._sock
._websocket
._open();
1799 client
._rfb_connection_state
= 'connected';
1800 client
._fb_name
= 'some device';
1801 client
._fb_width
= 27;
1802 client
._fb_height
= 32;
1805 it('should send a notification on XVP_FAIL', function () {
1806 client
.set_onNotification(sinon
.spy());
1807 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 0]));
1808 var spy
= client
.get_onNotification();
1809 expect(spy
).to
.have
.been
.calledOnce
;
1810 expect(spy
.args
[0][1]).to
.equal('XVP Operation Failed');
1813 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1814 client
.set_onCapabilities(sinon
.spy());
1815 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1816 expect(client
._rfb_xvp_ver
).to
.equal(10);
1817 expect(client
.get_onCapabilities()).to
.have
.been
.calledOnce
;
1818 expect(client
.get_onCapabilities().args
[0][1].power
).to
.be
.true;
1819 expect(client
.get_capabilities().power
).to
.be
.true;
1822 it('should fail on unknown XVP message types', function () {
1823 sinon
.spy(client
, "_fail");
1824 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1825 expect(client
._fail
).to
.have
.been
.calledOnce
;
1829 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1830 var expected_str
= 'cheese!';
1831 var data
= [3, 0, 0, 0];
1832 push32(data
, expected_str
.length
);
1833 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1834 client
.set_onClipboard(sinon
.spy());
1836 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1837 var spy
= client
.get_onClipboard();
1838 expect(spy
).to
.have
.been
.calledOnce
;
1839 expect(spy
.args
[0][1]).to
.equal(expected_str
);
1842 it('should fire the bell callback on Bell', function () {
1843 client
.set_onBell(sinon
.spy());
1844 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1845 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1848 it('should respond correctly to ServerFence', function () {
1849 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1850 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1852 var payload
= "foo\x00ab9";
1854 // ClientFence and ServerFence are identical in structure
1855 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1856 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1858 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1860 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1862 expected_msg
._sQlen
= 0;
1863 incoming_msg
._sQlen
= 0;
1865 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1866 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1868 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1870 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1873 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1874 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1876 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1878 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1880 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1882 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1883 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1886 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1887 client
._enabledContinuousUpdates
= true;
1888 client
._supportsContinuousUpdates
= true;
1890 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1892 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1895 it('should update continuous updates on resize', function () {
1896 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1897 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1899 client
._resize(450, 160);
1901 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1903 client
._enabledContinuousUpdates
= true;
1905 client
._resize(90, 700);
1907 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1910 it('should fail on an unknown message type', function () {
1911 sinon
.spy(client
, "_fail");
1912 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1913 expect(client
._fail
).to
.have
.been
.calledOnce
;
1917 describe('Asynchronous Events', function () {
1918 describe('Mouse event handlers', function () {
1920 beforeEach(function () {
1921 client
= make_rfb();
1922 client
._sock
= new Websock();
1923 client
._sock
.open('ws://', 'binary');
1924 client
._sock
._websocket
._open();
1925 sinon
.spy(client
._sock
, 'flush');
1926 client
._rfb_connection_state
= 'connected';
1929 it('should not send button messages in view-only mode', function () {
1930 client
._view_only
= true;
1931 client
._mouse
._onMouseButton(0, 0, 1, 0x001);
1932 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1935 it('should not send movement messages in view-only mode', function () {
1936 client
._view_only
= true;
1937 client
._mouse
._onMouseMove(0, 0);
1938 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1941 it('should send a pointer event on mouse button presses', function () {
1942 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1943 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1944 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1945 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1948 it('should send a mask of 1 on mousedown', function () {
1949 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1950 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1951 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1952 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1955 it('should send a mask of 0 on mouseup', function () {
1956 client
._mouse_buttonMask
= 0x001;
1957 client
._mouse
._onMouseButton(10, 12, 0, 0x001);
1958 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1959 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1960 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1963 it('should send a pointer event on mouse movement', function () {
1964 client
._mouse
._onMouseMove(10, 12);
1965 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1966 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1967 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1970 it('should set the button mask so that future mouse movements use it', function () {
1971 client
._mouse
._onMouseButton(10, 12, 1, 0x010);
1972 client
._mouse
._onMouseMove(13, 9);
1973 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1974 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1975 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1976 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1979 // NB(directxman12): we don't need to test not sending messages in
1980 // non-normal modes, since we haven't grabbed input
1981 // yet (grabbing input should be checked in the lifecycle tests).
1983 it('should not send movement messages when viewport dragging', function () {
1984 client
._viewportDragging
= true;
1985 client
._display
.viewportChangePos
= sinon
.spy();
1986 client
._mouse
._onMouseMove(13, 9);
1987 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1990 it('should not send button messages when initiating viewport dragging', function () {
1991 client
._viewportDrag
= true;
1992 client
._mouse
._onMouseButton(13, 9, 0x001);
1993 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1996 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1997 client
._viewportDrag
= true;
1998 client
._mouse
._onMouseButton(13, 9, 0x001);
1999 expect(client
._viewportDragging
).to
.be
.true;
2000 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
2003 it('should terminate viewport dragging on a button up event, if enabled', function () {
2004 client
._viewportDrag
= true;
2005 client
._viewportDragging
= true;
2006 client
._mouse
._onMouseButton(13, 9, 0x000);
2007 expect(client
._viewportDragging
).to
.be
.false;
2010 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
2013 var newX
= 123 + 11 * window
.devicePixelRatio
;
2014 var newY
= 109 + 4 * window
.devicePixelRatio
;
2016 client
._viewportDrag
= true;
2017 client
._viewportDragging
= true;
2018 client
._viewportHasMoved
= false;
2019 client
._viewportDragPos
= { x
: oldX
, y
: oldY
};
2020 client
._display
.viewportChangePos
= sinon
.spy();
2022 client
._mouse
._onMouseMove(newX
, newY
);
2024 expect(client
._viewportDragging
).to
.be
.true;
2025 expect(client
._viewportHasMoved
).to
.be
.true;
2026 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: newX
, y
: newY
});
2027 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
2028 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(oldX
- newX
, oldY
- newY
);
2032 describe('Keyboard Event Handlers', function () {
2034 beforeEach(function () {
2035 client
= make_rfb();
2036 client
._sock
= new Websock();
2037 client
._sock
.open('ws://', 'binary');
2038 client
._sock
._websocket
._open();
2039 sinon
.spy(client
._sock
, 'flush');
2040 client
._rfb_connection_state
= 'connected';
2041 client
._view_only
= false;
2044 it('should send a key message on a key press', function () {
2046 client
._keyboard
._onKeyEvent(0x41, 'KeyA', true);
2047 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
2048 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
2049 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
2052 it('should not send messages in view-only mode', function () {
2053 client
._view_only
= true;
2054 client
._keyboard
._onKeyEvent('a', 'KeyA', true);
2055 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2059 describe('WebSocket event handlers', function () {
2061 beforeEach(function () {
2062 client
= make_rfb();
2063 this.clock
= sinon
.useFakeTimers();
2066 afterEach(function () { this.clock
.restore(); });
2069 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
2070 client
.connect('host', 8675);
2071 client
._rfb_connection_state
= 'connected';
2072 client
._normal_msg
= sinon
.spy();
2073 client
._sock
._websocket
._receive_data(new Uint8Array([]));
2074 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
2077 it('should handle a message in the connected state as a normal message', function () {
2078 client
.connect('host', 8675);
2079 client
._rfb_connection_state
= 'connected';
2080 client
._normal_msg
= sinon
.spy();
2081 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2082 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
2085 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2086 client
.connect('host', 8675);
2087 client
._rfb_init_state
= 'ProtocolVersion';
2088 client
._init_msg
= sinon
.spy();
2089 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2090 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
2093 it('should process all normal messages directly', function () {
2094 client
.connect('host', 8675);
2095 client
._sock
._websocket
._open();
2096 client
._rfb_connection_state
= 'connected';
2097 client
.set_onBell(sinon
.spy());
2098 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2099 expect(client
.get_onBell()).to
.have
.been
.calledTwice
;
2103 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2104 client
.connect('host', 8675);
2105 client
._sock
._websocket
._open();
2106 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2109 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2110 sinon
.spy(client
, "_fail");
2111 client
.connect('host', 8675);
2112 client
._rfb_connection_state
= 'some_other_state';
2113 client
._sock
._websocket
._open();
2114 expect(client
._fail
).to
.have
.been
.calledOnce
;
2118 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2119 client
.connect('host', 8675);
2120 client
._rfb_connection_state
= 'disconnecting';
2121 client
._sock
._websocket
.close();
2122 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2125 it('should fail if we get a close event while connecting', function () {
2126 sinon
.spy(client
, "_fail");
2127 client
.connect('host', 8675);
2128 client
._rfb_connection_state
= 'connecting';
2129 client
._sock
._websocket
.close();
2130 expect(client
._fail
).to
.have
.been
.calledOnce
;
2133 it('should fail if we get a close event while disconnected', function () {
2134 sinon
.spy(client
, "_fail");
2135 client
.connect('host', 8675);
2136 client
._rfb_connection_state
= 'disconnected';
2137 client
._sock
._websocket
.close();
2138 expect(client
._fail
).to
.have
.been
.calledOnce
;
2141 it('should unregister close event handler', function () {
2142 sinon
.spy(client
._sock
, 'off');
2143 client
.connect('host', 8675);
2144 client
._rfb_connection_state
= 'disconnecting';
2145 client
._sock
._websocket
.close();
2146 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2149 // error events do nothing