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';
8 import FakeWebSocket
from './fake.websocket.js';
11 import sinonChai
from '../node_modules/sinon-chai/lib/sinon-chai.js';
14 function make_rfb (extra_opts
) {
19 extra_opts
.target
= extra_opts
.target
|| document
.createElement('canvas');
20 return new RFB(extra_opts
);
23 var push8 = function (arr
, num
) {
28 var push16 = function (arr
, num
) {
30 arr
.push((num
>> 8) & 0xFF,
34 var push32 = function (arr
, num
) {
36 arr
.push((num
>> 24) & 0xFF,
42 describe('Remote Frame Buffer Protocol Client', function() {
44 before(FakeWebSocket
.replace
);
45 after(FakeWebSocket
.restore
);
48 this.clock
= sinon
.useFakeTimers();
49 // Use a single set of buffers instead of reallocating to
51 var sock
= new Websock();
52 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
53 var rQ
= new Uint8Array(sock
._rQbufferSize
);
55 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
56 Websock
.prototype._allocate_buffers = function () {
64 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
68 describe('Public API Basic Behavior', function () {
70 beforeEach(function () {
74 describe('#connect', function () {
75 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
77 it('should set the current state to "connecting"', function () {
78 client
.connect('host', 8675);
79 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
80 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('connecting');
83 it('should not try to connect if we are missing a host', function () {
84 client
._fail
= sinon
.spy();
85 client
._rfb_connection_state
= '';
86 client
.connect(undefined, 8675);
87 expect(client
._fail
).to
.have
.been
.calledOnce
;
88 expect(client
._updateConnectionState
).to
.not
.have
.been
.called
;
89 expect(client
._rfb_connection_state
).to
.equal('');
92 it('should not try to connect if we are missing a port', function () {
93 client
._fail
= sinon
.spy();
94 client
._rfb_connection_state
= '';
95 client
.connect('abc');
96 expect(client
._fail
).to
.have
.been
.calledOnce
;
97 expect(client
._updateConnectionState
).to
.not
.have
.been
.called
;
98 expect(client
._rfb_connection_state
).to
.equal('');
102 describe('#disconnect', function () {
103 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
105 it('should set the current state to "disconnecting"', function () {
107 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
108 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('disconnecting');
111 it('should unregister error event handler', function () {
112 sinon
.spy(client
._sock
, 'off');
114 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
117 it('should unregister message event handler', function () {
118 sinon
.spy(client
._sock
, 'off');
120 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
123 it('should unregister open event handler', function () {
124 sinon
.spy(client
._sock
, 'off');
126 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
130 describe('#sendPassword', function () {
131 beforeEach(function () { this.clock
= sinon
.useFakeTimers(); });
132 afterEach(function () { this.clock
.restore(); });
134 it('should set the rfb password properly"', function () {
135 client
.sendPassword('pass');
136 expect(client
._rfb_password
).to
.equal('pass');
139 it('should call init_msg "soon"', function () {
140 client
._init_msg
= sinon
.spy();
141 client
.sendPassword('pass');
143 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
147 describe('#sendCtrlAlDel', function () {
148 beforeEach(function () {
149 client
._sock
= new Websock();
150 client
._sock
.open('ws://', 'binary');
151 client
._sock
._websocket
._open();
152 sinon
.spy(client
._sock
, 'flush');
153 client
._rfb_connection_state
= 'connected';
154 client
._view_only
= false;
157 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
158 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
159 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
160 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
161 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
162 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
163 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
164 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
166 client
.sendCtrlAltDel();
167 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
170 it('should not send the keys if we are not in a normal state', function () {
171 client
._rfb_connection_state
= "broken";
172 client
.sendCtrlAltDel();
173 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
176 it('should not send the keys if we are set as view_only', function () {
177 client
._view_only
= true;
178 client
.sendCtrlAltDel();
179 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
183 describe('#sendKey', function () {
184 beforeEach(function () {
185 client
._sock
= new Websock();
186 client
._sock
.open('ws://', 'binary');
187 client
._sock
._websocket
._open();
188 sinon
.spy(client
._sock
, 'flush');
189 client
._rfb_connection_state
= 'connected';
190 client
._view_only
= false;
193 it('should send a single key with the given code and state (down = true)', function () {
194 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
195 RFB
.messages
.keyEvent(expected
, 123, 1);
196 client
.sendKey(123, 'Key123', true);
197 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
200 it('should send both a down and up event if the state is not specified', function () {
201 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
202 RFB
.messages
.keyEvent(expected
, 123, 1);
203 RFB
.messages
.keyEvent(expected
, 123, 0);
204 client
.sendKey(123, 'Key123');
205 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
208 it('should not send the key if we are not in a normal state', function () {
209 client
._rfb_connection_state
= "broken";
210 client
.sendKey(123, 'Key123');
211 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
214 it('should not send the key if we are set as view_only', function () {
215 client
._view_only
= true;
216 client
.sendKey(123, 'Key123');
217 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
220 it('should send QEMU extended events if supported', function () {
221 client
._qemuExtKeyEventSupported
= true;
222 var expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
223 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
224 client
.sendKey(0x20, 'Space', true);
225 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
228 it('should not send QEMU extended events if unknown key code', function () {
229 client
._qemuExtKeyEventSupported
= true;
230 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
231 RFB
.messages
.keyEvent(expected
, 123, 1);
232 client
.sendKey(123, 'FooBar', true);
233 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
237 describe('#clipboardPasteFrom', function () {
238 beforeEach(function () {
239 client
._sock
= new Websock();
240 client
._sock
.open('ws://', 'binary');
241 client
._sock
._websocket
._open();
242 sinon
.spy(client
._sock
, 'flush');
243 client
._rfb_connection_state
= 'connected';
244 client
._view_only
= false;
247 it('should send the given text in a paste event', function () {
248 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
249 RFB
.messages
.clientCutText(expected
, 'abc');
250 client
.clipboardPasteFrom('abc');
251 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
254 it('should not send the text if we are not in a normal state', function () {
255 client
._rfb_connection_state
= "broken";
256 client
.clipboardPasteFrom('abc');
257 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
261 describe("#requestDesktopSize", function () {
262 beforeEach(function() {
263 client
._sock
= new Websock();
264 client
._sock
.open('ws://', 'binary');
265 client
._sock
._websocket
._open();
266 sinon
.spy(client
._sock
, 'flush');
267 client
._rfb_connection_state
= 'connected';
268 client
._view_only
= false;
269 client
._supportsSetDesktopSize
= true;
272 it('should send the request with the given width and height', function () {
273 var expected
= [251];
274 push8(expected
, 0); // padding
275 push16(expected
, 1); // width
276 push16(expected
, 2); // height
277 push8(expected
, 1); // number-of-screens
278 push8(expected
, 0); // padding before screen array
279 push32(expected
, 0); // id
280 push16(expected
, 0); // x-position
281 push16(expected
, 0); // y-position
282 push16(expected
, 1); // width
283 push16(expected
, 2); // height
284 push32(expected
, 0); // flags
286 client
.requestDesktopSize(1, 2);
287 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
290 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
291 client
._supportsSetDesktopSize
= false;
292 client
.requestDesktopSize(1,2);
293 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
296 it('should not send the request if we are not in a normal state', function () {
297 client
._rfb_connection_state
= "broken";
298 client
.requestDesktopSize(1,2);
299 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
303 describe("XVP operations", function () {
304 beforeEach(function () {
305 client
._sock
= new Websock();
306 client
._sock
.open('ws://', 'binary');
307 client
._sock
._websocket
._open();
308 sinon
.spy(client
._sock
, 'flush');
309 client
._rfb_connection_state
= 'connected';
310 client
._view_only
= false;
311 client
._rfb_xvp_ver
= 1;
314 it('should send the shutdown signal on #xvpShutdown', function () {
315 client
.xvpShutdown();
316 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
319 it('should send the reboot signal on #xvpReboot', function () {
321 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
324 it('should send the reset signal on #xvpReset', function () {
326 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
329 it('should support sending arbitrary XVP operations via #xvpOp', function () {
331 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x07]));
334 it('should not send XVP operations with higher versions than we support', function () {
335 expect(client
.xvpOp(2, 7)).to
.be
.false;
336 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
341 describe('Misc Internals', function () {
342 describe('#_updateConnectionState', function () {
344 beforeEach(function () {
345 this.clock
= sinon
.useFakeTimers();
349 afterEach(function () {
350 this.clock
.restore();
353 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
354 var spy
= sinon
.spy();
355 client
._disconnTimer
= setTimeout(spy
, 50);
356 client
._updateConnectionState('connecting');
358 expect(spy
).to
.not
.have
.been
.called
;
359 expect(client
._disconnTimer
).to
.be
.null;
362 it('should call the updateState callback', function () {
363 client
.set_onUpdateState(sinon
.spy());
364 client
._updateConnectionState('connecting');
365 var spy
= client
.get_onUpdateState();
366 expect(spy
).to
.have
.been
.calledOnce
;
367 expect(spy
.args
[0][1]).to
.equal('connecting');
370 it('should set the rfb_connection_state', function () {
371 client
._rfb_connection_state
= 'disconnecting';
372 client
._updateConnectionState('disconnected');
373 expect(client
._rfb_connection_state
).to
.equal('disconnected');
376 it('should not change the state when we are disconnected', function () {
377 client
._rfb_connection_state
= 'disconnected';
378 client
._updateConnectionState('connecting');
379 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
382 it('should ignore state changes to the same state', function () {
383 client
.set_onUpdateState(sinon
.spy());
384 client
._rfb_connection_state
= 'connecting';
385 client
._updateConnectionState('connecting');
386 var spy
= client
.get_onUpdateState();
387 expect(spy
).to
.not
.have
.been
.called
;
390 it('should ignore illegal state changes', function () {
391 client
.set_onUpdateState(sinon
.spy());
392 client
._rfb_connection_state
= 'connected';
393 client
._updateConnectionState('disconnected');
394 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
395 var spy
= client
.get_onUpdateState();
396 expect(spy
).to
.not
.have
.been
.called
;
400 describe('#_fail', function () {
402 beforeEach(function () {
403 this.clock
= sinon
.useFakeTimers();
405 client
.connect('host', 8675);
408 afterEach(function () {
409 this.clock
.restore();
412 it('should close the WebSocket connection', function () {
413 sinon
.spy(client
._sock
, 'close');
415 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
418 it('should transition to disconnected', function () {
419 sinon
.spy(client
, '_updateConnectionState');
421 this.clock
.tick(2000);
422 expect(client
._updateConnectionState
).to
.have
.been
.called
;
423 expect(client
._rfb_connection_state
).to
.equal('disconnected');
426 it('should set disconnect_reason', function () {
427 client
._rfb_connection_state
= 'connected';
428 client
._fail('a reason');
429 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
432 it('should not include details in disconnect_reason', function () {
433 client
._rfb_connection_state
= 'connected';
434 client
._fail('a reason', 'details');
435 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
438 it('should result in disconnect callback with message when reason given', function () {
439 client
._rfb_connection_state
= 'connected';
440 client
.set_onDisconnected(sinon
.spy());
441 client
._fail('a reason');
442 var spy
= client
.get_onDisconnected();
443 this.clock
.tick(2000);
444 expect(spy
).to
.have
.been
.calledOnce
;
445 expect(spy
.args
[0].length
).to
.equal(2);
446 expect(spy
.args
[0][1]).to
.equal('a reason');
451 describe('#_notification', function () {
453 beforeEach(function () { client
= make_rfb(); });
455 it('should call the notification callback', function () {
456 client
.set_onNotification(sinon
.spy());
457 client
._notification('notify!', 'warn');
458 var spy
= client
.get_onNotification();
459 expect(spy
).to
.have
.been
.calledOnce
;
460 expect(spy
.args
[0][1]).to
.equal('notify!');
461 expect(spy
.args
[0][2]).to
.equal('warn');
464 it('should not call the notification callback when level is invalid', function () {
465 client
.set_onNotification(sinon
.spy());
466 client
._notification('notify!', 'invalid');
467 var spy
= client
.get_onNotification();
468 expect(spy
).to
.not
.have
.been
.called
;
473 describe('Connection States', function () {
474 describe('connecting', function () {
476 beforeEach(function () { client
= make_rfb(); });
478 it('should reset the variable states', function () {
479 sinon
.spy(client
, '_init_vars');
480 client
._updateConnectionState('connecting');
481 expect(client
._init_vars
).to
.have
.been
.calledOnce
;
484 it('should actually connect to the websocket', function () {
485 sinon
.spy(client
._sock
, 'open');
486 client
._updateConnectionState('connecting');
487 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
490 it('should use wss:// to connect if encryption is enabled', function () {
491 sinon
.spy(client
._sock
, 'open');
492 client
.set_encrypt(true);
493 client
._updateConnectionState('connecting');
494 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
497 it('should use ws:// to connect if encryption is not enabled', function () {
498 sinon
.spy(client
._sock
, 'open');
499 client
.set_encrypt(true);
500 client
._updateConnectionState('connecting');
501 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
504 it('should use a uri with the host, port, and path specified to connect', function () {
505 sinon
.spy(client
._sock
, 'open');
506 client
.set_encrypt(false);
507 client
._rfb_host
= 'HOST';
508 client
._rfb_port
= 8675;
509 client
._rfb_path
= 'PATH';
510 client
._updateConnectionState('connecting');
511 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
515 describe('disconnecting', function () {
517 beforeEach(function () {
518 this.clock
= sinon
.useFakeTimers();
520 client
.connect('host', 8675);
523 afterEach(function () {
524 this.clock
.restore();
527 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
528 sinon
.spy(client
, '_updateConnectionState');
529 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
530 client
._updateConnectionState('disconnecting');
531 this.clock
.tick(client
.get_disconnectTimeout() * 1000);
532 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
533 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
534 expect(client
._rfb_connection_state
).to
.equal("disconnected");
537 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
538 client
._updateConnectionState('disconnecting');
539 this.clock
.tick(client
.get_disconnectTimeout() * 500);
540 client
._sock
._websocket
.close();
541 this.clock
.tick(client
.get_disconnectTimeout() * 500 + 1);
542 expect(client
._rfb_connection_state
).to
.equal('disconnected');
545 it('should close the WebSocket connection', function () {
546 sinon
.spy(client
._sock
, 'close');
547 client
._updateConnectionState('disconnecting');
548 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
552 describe('disconnected', function () {
554 beforeEach(function () { client
= make_rfb(); });
556 it('should call the disconnect callback if the state is "disconnected"', function () {
557 client
.set_onDisconnected(sinon
.spy());
558 client
._rfb_connection_state
= 'disconnecting';
559 client
._rfb_disconnect_reason
= "error";
560 client
._updateConnectionState('disconnected');
561 var spy
= client
.get_onDisconnected();
562 expect(spy
).to
.have
.been
.calledOnce
;
563 expect(spy
.args
[0][1]).to
.equal("error");
566 it('should not call the disconnect callback if the state is not "disconnected"', function () {
567 client
.set_onDisconnected(sinon
.spy());
568 client
._updateConnectionState('disconnecting');
569 var spy
= client
.get_onDisconnected();
570 expect(spy
).to
.not
.have
.been
.called
;
573 it('should call the disconnect callback without msg when no reason given', function () {
574 client
.set_onDisconnected(sinon
.spy());
575 client
._rfb_connection_state
= 'disconnecting';
576 client
._rfb_disconnect_reason
= "";
577 client
._updateConnectionState('disconnected');
578 var spy
= client
.get_onDisconnected();
579 expect(spy
).to
.have
.been
.calledOnce
;
580 expect(spy
.args
[0].length
).to
.equal(1);
583 it('should call the updateState callback before the disconnect callback', function () {
584 client
.set_onDisconnected(sinon
.spy());
585 client
.set_onUpdateState(sinon
.spy());
586 client
._rfb_connection_state
= 'disconnecting';
587 client
._updateConnectionState('disconnected');
588 var updateStateSpy
= client
.get_onUpdateState();
589 var disconnectSpy
= client
.get_onDisconnected();
590 expect(updateStateSpy
.calledBefore(disconnectSpy
)).to
.be
.true;
594 // NB(directxman12): Connected does *nothing* in updateConnectionState
597 describe('Protocol Initialization States', function () {
598 describe('ProtocolVersion', function () {
599 beforeEach(function () {
600 this.clock
= sinon
.useFakeTimers();
603 afterEach(function () {
604 this.clock
.restore();
607 function send_ver (ver
, client
) {
608 var arr
= new Uint8Array(12);
609 for (var i
= 0; i
< ver
.length
; i
++) {
610 arr
[i
+4] = ver
.charCodeAt(i
);
612 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
614 client
._sock
._websocket
._receive_data(arr
);
617 describe('version parsing', function () {
619 beforeEach(function () {
621 client
.connect('host', 8675);
622 client
._sock
._websocket
._open();
625 it('should interpret version 000.000 as a repeater', function () {
626 client
._repeaterID
= '\x01\x02\x03\x04\x05';
627 send_ver('000.000', client
);
628 expect(client
._rfb_version
).to
.equal(0);
630 var sent_data
= client
._sock
._websocket
._get_sent_data();
631 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
634 it('should interpret version 003.003 as version 3.3', function () {
635 send_ver('003.003', client
);
636 expect(client
._rfb_version
).to
.equal(3.3);
639 it('should interpret version 003.006 as version 3.3', function () {
640 send_ver('003.006', client
);
641 expect(client
._rfb_version
).to
.equal(3.3);
644 it('should interpret version 003.889 as version 3.3', function () {
645 send_ver('003.889', client
);
646 expect(client
._rfb_version
).to
.equal(3.3);
649 it('should interpret version 003.007 as version 3.7', function () {
650 send_ver('003.007', client
);
651 expect(client
._rfb_version
).to
.equal(3.7);
654 it('should interpret version 003.008 as version 3.8', function () {
655 send_ver('003.008', client
);
656 expect(client
._rfb_version
).to
.equal(3.8);
659 it('should interpret version 004.000 as version 3.8', function () {
660 send_ver('004.000', client
);
661 expect(client
._rfb_version
).to
.equal(3.8);
664 it('should interpret version 004.001 as version 3.8', function () {
665 send_ver('004.001', client
);
666 expect(client
._rfb_version
).to
.equal(3.8);
669 it('should interpret version 005.000 as version 3.8', function () {
670 send_ver('005.000', client
);
671 expect(client
._rfb_version
).to
.equal(3.8);
674 it('should fail on an invalid version', function () {
675 sinon
.spy(client
, "_fail");
676 send_ver('002.000', client
);
677 expect(client
._fail
).to
.have
.been
.calledOnce
;
682 beforeEach(function () {
684 client
.connect('host', 8675);
685 client
._sock
._websocket
._open();
688 it('should handle two step repeater negotiation', function () {
689 client
._repeaterID
= '\x01\x02\x03\x04\x05';
691 send_ver('000.000', client
);
692 expect(client
._rfb_version
).to
.equal(0);
693 var sent_data
= client
._sock
._websocket
._get_sent_data();
694 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
695 expect(sent_data
).to
.have
.length(250);
697 send_ver('003.008', client
);
698 expect(client
._rfb_version
).to
.equal(3.8);
701 it('should send back the interpreted version', function () {
702 send_ver('004.000', client
);
704 var expected_str
= 'RFB 003.008\n';
706 for (var i
= 0; i
< expected_str
.length
; i
++) {
707 expected
[i
] = expected_str
.charCodeAt(i
);
710 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
713 it('should transition to the Security state on successful negotiation', function () {
714 send_ver('003.008', client
);
715 expect(client
._rfb_init_state
).to
.equal('Security');
719 describe('Security', function () {
722 beforeEach(function () {
724 client
.connect('host', 8675);
725 client
._sock
._websocket
._open();
726 client
._rfb_init_state
= 'Security';
729 it('should simply receive the auth scheme when for versions < 3.7', function () {
730 client
._rfb_version
= 3.6;
731 var auth_scheme_raw
= [1, 2, 3, 4];
732 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
733 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
734 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
735 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
738 it('should prefer no authentication is possible', function () {
739 client
._rfb_version
= 3.7;
740 var auth_schemes
= [2, 1, 3];
741 client
._sock
._websocket
._receive_data(auth_schemes
);
742 expect(client
._rfb_auth_scheme
).to
.equal(1);
743 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
746 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
747 client
._rfb_version
= 3.7;
748 var auth_schemes
= [2, 22, 16];
749 client
._sock
._websocket
._receive_data(auth_schemes
);
750 expect(client
._rfb_auth_scheme
).to
.equal(22);
751 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
754 it('should fail if there are no supported schemes for versions >= 3.7', function () {
755 sinon
.spy(client
, "_fail");
756 client
._rfb_version
= 3.7;
757 var auth_schemes
= [1, 32];
758 client
._sock
._websocket
._receive_data(auth_schemes
);
759 expect(client
._fail
).to
.have
.been
.calledOnce
;
762 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
763 client
._rfb_version
= 3.7;
764 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
765 sinon
.spy(client
, '_fail');
766 client
._sock
._websocket
._receive_data(failure_data
);
768 expect(client
._fail
).to
.have
.been
.calledOnce
;
769 expect(client
._fail
).to
.have
.been
.calledWith(
770 'Error while negotiating with server','Security failure: whoops');
773 it('should transition to the Authentication state and continue on successful negotiation', function () {
774 client
._rfb_version
= 3.7;
775 var auth_schemes
= [1, 1];
776 client
._negotiate_authentication
= sinon
.spy();
777 client
._sock
._websocket
._receive_data(auth_schemes
);
778 expect(client
._rfb_init_state
).to
.equal('Authentication');
779 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
783 describe('Authentication', function () {
786 beforeEach(function () {
788 client
.connect('host', 8675);
789 client
._sock
._websocket
._open();
790 client
._rfb_init_state
= 'Security';
793 function send_security(type
, cl
) {
794 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
797 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
798 client
._rfb_version
= 3.6;
799 var err_msg
= "Whoopsies";
800 var data
= [0, 0, 0, 0];
801 var err_len
= err_msg
.length
;
802 push32(data
, err_len
);
803 for (var i
= 0; i
< err_len
; i
++) {
804 data
.push(err_msg
.charCodeAt(i
));
807 sinon
.spy(client
, '_fail');
808 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
809 expect(client
._fail
).to
.have
.been
.calledWith(
810 'Authentication failure', 'Whoopsies');
813 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
814 client
._rfb_version
= 3.8;
815 send_security(1, client
);
816 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
819 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
820 client
._rfb_version
= 3.7;
821 send_security(1, client
);
822 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
825 it('should fail on an unknown auth scheme', function () {
826 sinon
.spy(client
, "_fail");
827 client
._rfb_version
= 3.8;
828 send_security(57, client
);
829 expect(client
._fail
).to
.have
.been
.calledOnce
;
832 describe('VNC Authentication (type 2) Handler', function () {
835 beforeEach(function () {
837 client
.connect('host', 8675);
838 client
._sock
._websocket
._open();
839 client
._rfb_init_state
= 'Security';
840 client
._rfb_version
= 3.8;
843 it('should call the passwordRequired callback if missing a password', function () {
844 client
.set_onPasswordRequired(sinon
.spy());
845 send_security(2, client
);
847 var spy
= client
.get_onPasswordRequired();
848 expect(client
._rfb_password
.length
).to
.equal(0);
849 expect(spy
).to
.have
.been
.calledOnce
;
852 it('should encrypt the password with DES and then send it back', function () {
853 client
._rfb_password
= 'passwd';
854 send_security(2, client
);
855 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
858 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
859 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
861 var des_pass
= RFB
.genDES('passwd', challenge
);
862 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
865 it('should transition to SecurityResult immediately after sending the password', function () {
866 client
._rfb_password
= 'passwd';
867 send_security(2, client
);
870 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
871 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
873 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
877 describe('XVP Authentication (type 22) Handler', function () {
880 beforeEach(function () {
882 client
.connect('host', 8675);
883 client
._sock
._websocket
._open();
884 client
._rfb_init_state
= 'Security';
885 client
._rfb_version
= 3.8;
888 it('should fall through to standard VNC authentication upon completion', function () {
889 client
.set_xvp_password_sep('#');
890 client
._rfb_password
= 'user#target#password';
891 client
._negotiate_std_vnc_auth
= sinon
.spy();
892 send_security(22, client
);
893 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
896 it('should call the passwordRequired callback if the password is missing', function() {
897 client
.set_onPasswordRequired(sinon
.spy());
898 client
._rfb_password
= '';
899 send_security(22, client
);
901 var spy
= client
.get_onPasswordRequired();
902 expect(client
._rfb_password
.length
).to
.equal(0);
903 expect(spy
).to
.have
.been
.calledOnce
;
906 it('should call the passwordRequired callback if the password is improperly formatted', function() {
907 client
.set_onPasswordRequired(sinon
.spy());
908 client
._rfb_password
= 'user@target';
909 send_security(22, client
);
911 var spy
= client
.get_onPasswordRequired();
912 expect(spy
).to
.have
.been
.calledOnce
;
915 it('should split the password, send the first two parts, and pass on the last part', function () {
916 client
.set_xvp_password_sep('#');
917 client
._rfb_password
= 'user#target#password';
918 client
._negotiate_std_vnc_auth
= sinon
.spy();
920 send_security(22, client
);
922 expect(client
._rfb_password
).to
.equal('password');
924 var expected
= [22, 4, 6]; // auth selection, len user, len target
925 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
927 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
931 describe('TightVNC Authentication (type 16) Handler', function () {
934 beforeEach(function () {
936 client
.connect('host', 8675);
937 client
._sock
._websocket
._open();
938 client
._rfb_init_state
= 'Security';
939 client
._rfb_version
= 3.8;
940 send_security(16, client
);
941 client
._sock
._websocket
._get_sent_data(); // skip the security reply
944 function send_num_str_pairs(pairs
, client
) {
945 var pairs_len
= pairs
.length
;
947 push32(data
, pairs_len
);
949 for (var i
= 0; i
< pairs_len
; i
++) {
950 push32(data
, pairs
[i
][0]);
952 for (j
= 0; j
< 4; j
++) {
953 data
.push(pairs
[i
][1].charCodeAt(j
));
955 for (j
= 0; j
< 8; j
++) {
956 data
.push(pairs
[i
][2].charCodeAt(j
));
960 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
963 it('should skip tunnel negotiation if no tunnels are requested', function () {
964 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
965 expect(client
._rfb_tightvnc
).to
.be
.true;
968 it('should fail if no supported tunnels are listed', function () {
969 sinon
.spy(client
, "_fail");
970 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
971 expect(client
._fail
).to
.have
.been
.calledOnce
;
974 it('should choose the notunnel tunnel type', function () {
975 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
976 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
979 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
980 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
981 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
982 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
983 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
984 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
987 /*it('should attempt to use VNC auth over no auth when possible', function () {
988 client._rfb_tightvnc = true;
989 client._negotiate_std_vnc_auth = sinon.spy();
990 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
991 expect(client._sock).to.have.sent([0, 0, 0, 1]);
992 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
993 expect(client._rfb_auth_scheme).to.equal(2);
994 });*/ // while this would make sense, the original code doesn't actually do this
996 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
997 client
._rfb_tightvnc
= true;
998 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
999 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1000 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1003 it('should accept VNC authentication and transition to that', function () {
1004 client
._rfb_tightvnc
= true;
1005 client
._negotiate_std_vnc_auth
= sinon
.spy();
1006 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
1007 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1008 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1009 expect(client
._rfb_auth_scheme
).to
.equal(2);
1012 it('should fail if there are no supported auth types', function () {
1013 sinon
.spy(client
, "_fail");
1014 client
._rfb_tightvnc
= true;
1015 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
1016 expect(client
._fail
).to
.have
.been
.calledOnce
;
1021 describe('SecurityResult', function () {
1024 beforeEach(function () {
1025 client
= make_rfb();
1026 client
.connect('host', 8675);
1027 client
._sock
._websocket
._open();
1028 client
._rfb_init_state
= 'SecurityResult';
1031 it('should fall through to ServerInitialisation on a response code of 0', function () {
1032 client
._updateConnectionState
= sinon
.spy();
1033 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1034 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1037 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1038 client
._rfb_version
= 3.8;
1039 sinon
.spy(client
, '_fail');
1040 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1041 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1042 expect(client
._fail
).to
.have
.been
.calledWith(
1043 'Authentication failure', 'whoops');
1046 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1047 sinon
.spy(client
, '_fail');
1048 client
._rfb_version
= 3.7;
1049 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
1050 expect(client
._fail
).to
.have
.been
.calledWith('Authentication failure');
1054 describe('ClientInitialisation', function () {
1057 beforeEach(function () {
1058 client
= make_rfb();
1059 client
.connect('host', 8675);
1060 client
._sock
._websocket
._open();
1061 client
._rfb_init_state
= 'SecurityResult';
1064 it('should transition to the ServerInitialisation state', function () {
1065 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1066 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1069 it('should send 1 if we are in shared mode', function () {
1070 client
.set_shared(true);
1071 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1072 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1075 it('should send 0 if we are not in shared mode', function () {
1076 client
.set_shared(false);
1077 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1078 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1082 describe('ServerInitialisation', function () {
1085 beforeEach(function () {
1086 client
= make_rfb();
1087 client
.connect('host', 8675);
1088 client
._sock
._websocket
._open();
1089 client
._rfb_init_state
= 'ServerInitialisation';
1092 function send_server_init(opts
, client
) {
1093 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1094 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1095 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1096 for (var opt
in opts
) {
1097 full_opts
[opt
] = opts
[opt
];
1101 push16(data
, full_opts
.width
);
1102 push16(data
, full_opts
.height
);
1104 data
.push(full_opts
.bpp
);
1105 data
.push(full_opts
.depth
);
1106 data
.push(full_opts
.big_endian
);
1107 data
.push(full_opts
.true_color
);
1109 push16(data
, full_opts
.red_max
);
1110 push16(data
, full_opts
.green_max
);
1111 push16(data
, full_opts
.blue_max
);
1112 push8(data
, full_opts
.red_shift
);
1113 push8(data
, full_opts
.green_shift
);
1114 push8(data
, full_opts
.blue_shift
);
1121 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1124 push32(name_data
, full_opts
.name
.length
);
1125 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1126 name_data
.push(full_opts
.name
.charCodeAt(i
));
1128 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1131 it('should set the framebuffer width and height', function () {
1132 send_server_init({ width
: 32, height
: 84 }, client
);
1133 expect(client
._fb_width
).to
.equal(32);
1134 expect(client
._fb_height
).to
.equal(84);
1137 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1139 it('should set the framebuffer name and call the callback', function () {
1140 client
.set_onDesktopName(sinon
.spy());
1141 send_server_init({ name
: 'some name' }, client
);
1143 var spy
= client
.get_onDesktopName();
1144 expect(client
._fb_name
).to
.equal('some name');
1145 expect(spy
).to
.have
.been
.calledOnce
;
1146 expect(spy
.args
[0][1]).to
.equal('some name');
1149 it('should handle the extended init message of the tight encoding', function () {
1150 // NB(sross): we don't actually do anything with it, so just test that we can
1151 // read it w/o throwing an error
1152 client
._rfb_tightvnc
= true;
1153 send_server_init({}, client
);
1155 var tight_data
= [];
1156 push16(tight_data
, 1);
1157 push16(tight_data
, 2);
1158 push16(tight_data
, 3);
1159 push16(tight_data
, 0);
1160 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1163 client
._sock
._websocket
._receive_data(tight_data
);
1165 expect(client
._rfb_connection_state
).to
.equal('connected');
1168 it('should call the resize callback and resize the display', function () {
1169 client
.set_onFBResize(sinon
.spy());
1170 sinon
.spy(client
._display
, 'resize');
1171 send_server_init({ width
: 27, height
: 32 }, client
);
1173 var spy
= client
.get_onFBResize();
1174 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1175 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1176 expect(spy
).to
.have
.been
.calledOnce
;
1177 expect(spy
.args
[0][1]).to
.equal(27);
1178 expect(spy
.args
[0][2]).to
.equal(32);
1181 it('should grab the mouse and keyboard', function () {
1182 sinon
.spy(client
._keyboard
, 'grab');
1183 sinon
.spy(client
._mouse
, 'grab');
1184 send_server_init({}, client
);
1185 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1186 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1189 // TODO(directxman12): test the various options in this configuration matrix
1190 it('should reply with the pixel format, client encodings, and initial update request', function () {
1191 // FIXME: We need to be flexible about what encodings are requested
1194 var expected
= {_sQ
: new Uint8Array(34),
1196 flush: function () {}};
1197 RFB
.messages
.pixelFormat(expected
, 4, 3, true);
1198 RFB
.messages
.clientEncodings(expected
, client
._encodings
, false, true);
1199 RFB
.messages
.fbUpdateRequest(expected
, false, 0, 0, 27, 32);
1201 send_server_init({ width
: 27, height
: 32 }, client
);
1202 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
1205 it('should transition to the "connected" state', function () {
1206 send_server_init({}, client
);
1207 expect(client
._rfb_connection_state
).to
.equal('connected');
1212 describe('Protocol Message Processing After Completing Initialization', function () {
1215 beforeEach(function () {
1216 client
= make_rfb();
1217 client
.connect('host', 8675);
1218 client
._sock
._websocket
._open();
1219 client
._rfb_connection_state
= 'connected';
1220 client
._fb_name
= 'some device';
1221 client
._fb_width
= 640;
1222 client
._fb_height
= 20;
1225 describe('Framebuffer Update Handling', function () {
1228 beforeEach(function () {
1229 client
= make_rfb();
1230 client
.connect('host', 8675);
1231 client
._sock
._websocket
._open();
1232 client
._rfb_connection_state
= 'connected';
1233 client
._fb_name
= 'some device';
1234 client
._fb_width
= 640;
1235 client
._fb_height
= 20;
1238 var target_data_arr
= [
1239 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1240 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1241 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1242 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1246 var target_data_check_arr
= [
1247 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1248 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1249 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1250 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1252 var target_data_check
;
1254 before(function () {
1255 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1256 target_data
= new Uint8Array(target_data_arr
);
1257 target_data_check
= new Uint8Array(target_data_check_arr
);
1260 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1263 if (!rect_cnt
|| rect_cnt
> -1) {
1265 data
.push(0); // msg type
1266 data
.push(0); // padding
1267 push16(data
, rect_cnt
|| rect_data
.length
);
1270 for (var i
= 0; i
< rect_data
.length
; i
++) {
1272 push16(data
, rect_info
[i
].x
);
1273 push16(data
, rect_info
[i
].y
);
1274 push16(data
, rect_info
[i
].width
);
1275 push16(data
, rect_info
[i
].height
);
1276 push32(data
, rect_info
[i
].encoding
);
1278 data
= data
.concat(rect_data
[i
]);
1281 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1284 it('should send an update request if there is sufficient data', function () {
1285 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1286 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1288 client
._framebufferUpdate = function () { return true; };
1289 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1291 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1294 it('should not send an update request if we need more data', function () {
1295 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1296 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1299 it('should resume receiving an update if we previously did not have enough data', function () {
1300 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1301 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1303 // just enough to set FBU.rects
1304 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1305 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1307 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1308 // 247 should *not* be used as the message type here
1309 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1310 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1313 it('should not send a request in continuous updates mode', function () {
1314 client
._enabledContinuousUpdates
= true;
1315 client
._framebufferUpdate = function () { return true; };
1316 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1318 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1321 it('should parse out information from a header before any actual data comes in', function () {
1322 client
.set_onFBUReceive(sinon
.spy());
1323 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02, encodingName
: 'RRE' };
1324 send_fbu_msg([rect_info
], [[]], client
);
1326 var spy
= client
.get_onFBUReceive();
1327 expect(spy
).to
.have
.been
.calledOnce
;
1328 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1331 it('should fire onFBUComplete when the update is complete', function () {
1332 client
.set_onFBUComplete(sinon
.spy());
1333 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: -224, encodingName
: 'last_rect' };
1334 send_fbu_msg([rect_info
], [[]], client
); // last_rect
1336 var spy
= client
.get_onFBUComplete();
1337 expect(spy
).to
.have
.been
.calledOnce
;
1340 it('should not fire onFBUComplete if we have not finished processing the update', function () {
1341 client
.set_onFBUComplete(sinon
.spy());
1342 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x00, encodingName
: 'RAW' };
1343 send_fbu_msg([rect_info
], [[]], client
);
1344 expect(client
.get_onFBUComplete()).to
.not
.have
.been
.called
;
1347 it('should fail on an unsupported encoding', function () {
1348 sinon
.spy(client
, "_fail");
1349 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1350 send_fbu_msg([rect_info
], [[]], client
);
1351 expect(client
._fail
).to
.have
.been
.calledOnce
;
1354 it('should be able to pause and resume receiving rects if not enought data', function () {
1355 // seed some initial data to copy
1356 client
._fb_width
= 4;
1357 client
._fb_height
= 4;
1358 client
._display
.resize(4, 4);
1359 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1361 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1362 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1363 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1364 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1365 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1366 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1367 expect(client
._display
).to
.have
.displayed(target_data_check
);
1370 describe('Message Encoding Handlers', function () {
1373 beforeEach(function () {
1374 client
= make_rfb();
1375 client
.connect('host', 8675);
1376 client
._sock
._websocket
._open();
1377 client
._rfb_connection_state
= 'connected';
1378 client
._fb_name
= 'some device';
1379 // a really small frame
1380 client
._fb_width
= 4;
1381 client
._fb_height
= 4;
1382 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);
1455 it('should handle a tile with fg, bg specified, normal subrects', function () {
1456 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1458 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1459 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1460 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1464 rect
.push(2); // 2 subrects
1465 rect
.push(0); // x: 0, y: 0
1466 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1467 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1468 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1469 send_fbu_msg(info
, [rect
], client
);
1470 expect(client
._display
).to
.have
.displayed(target_data_check
);
1473 it('should handle a raw tile', function () {
1474 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1476 rect
.push(0x01); // raw
1477 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1478 rect
.push(target_data
[i
+ 2]);
1479 rect
.push(target_data
[i
+ 1]);
1480 rect
.push(target_data
[i
]);
1481 rect
.push(target_data
[i
+ 3]);
1483 send_fbu_msg(info
, [rect
], client
);
1484 expect(client
._display
).to
.have
.displayed(target_data
);
1487 it('should handle a tile with only bg specified (solid bg)', function () {
1488 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1491 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1492 send_fbu_msg(info
, [rect
], client
);
1495 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1496 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1499 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1500 // set the width so we can have two tiles
1501 client
._fb_width
= 8;
1502 client
._display
.resize(8, 4);
1504 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1510 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1512 // send an empty frame
1515 send_fbu_msg(info
, [rect
], client
);
1519 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1520 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1521 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1524 it('should handle a tile with bg and coloured subrects', function () {
1525 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1527 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1528 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1529 rect
.push(2); // 2 subrects
1530 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1534 rect
.push(0); // x: 0, y: 0
1535 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1536 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1540 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1541 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1542 send_fbu_msg(info
, [rect
], client
);
1543 expect(client
._display
).to
.have
.displayed(target_data_check
);
1546 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1547 client
._fb_width
= 4;
1548 client
._fb_height
= 17;
1549 client
._display
.resize(4, 17);
1551 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1553 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1554 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1555 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1559 rect
.push(8); // 8 subrects
1561 for (i
= 0; i
< 4; i
++) {
1562 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1563 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1564 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1565 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1567 rect
.push(0x08); // anysubrects
1568 rect
.push(1); // 1 subrect
1569 rect
.push(0); // x: 0, y: 0
1570 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1571 send_fbu_msg(info
, [rect
], client
);
1574 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1575 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1576 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1579 it('should fail on an invalid subencoding', function () {
1580 sinon
.spy(client
,"_fail");
1581 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1582 var rects
= [[45]]; // an invalid subencoding
1583 send_fbu_msg(info
, rects
, client
);
1584 expect(client
._fail
).to
.have
.been
.calledOnce
;
1588 it
.skip('should handle the TIGHT encoding', function () {
1589 // TODO(directxman12): test this
1592 it
.skip('should handle the TIGHT_PNG encoding', function () {
1593 // TODO(directxman12): test this
1596 it('should handle the DesktopSize pseduo-encoding', function () {
1597 client
.set_onFBResize(sinon
.spy());
1598 sinon
.spy(client
._display
, 'resize');
1599 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1601 var spy
= client
.get_onFBResize();
1602 expect(spy
).to
.have
.been
.calledOnce
;
1603 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1605 expect(client
._fb_width
).to
.equal(20);
1606 expect(client
._fb_height
).to
.equal(50);
1608 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1609 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1612 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1615 beforeEach(function () {
1616 client
= make_rfb();
1617 client
.connect('host', 8675);
1618 client
._sock
._websocket
._open();
1619 client
._rfb_connection_state
= 'connected';
1620 client
._fb_name
= 'some device';
1621 client
._supportsSetDesktopSize
= false;
1622 // a really small frame
1623 client
._fb_width
= 4;
1624 client
._fb_height
= 4;
1625 client
._display
.resize(4, 4);
1626 sinon
.spy(client
._display
, 'resize');
1627 client
.set_onFBResize(sinon
.spy());
1630 function make_screen_data (nr_of_screens
) {
1632 push8(data
, nr_of_screens
); // number-of-screens
1633 push8(data
, 0); // padding
1634 push16(data
, 0); // padding
1635 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1636 push32(data
, 0); // id
1637 push16(data
, 0); // x-position
1638 push16(data
, 0); // y-position
1639 push16(data
, 20); // width
1640 push16(data
, 50); // height
1641 push32(data
, 0); // flags
1646 it('should handle a resize requested by this client', function () {
1647 var reason_for_change
= 1; // requested by this client
1648 var status_code
= 0; // No error
1650 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1651 width
: 20, height
: 50, encoding
: -308 }],
1652 make_screen_data(1), client
);
1654 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1655 expect(client
._fb_width
).to
.equal(20);
1656 expect(client
._fb_height
).to
.equal(50);
1658 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1659 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1661 var spy
= client
.get_onFBResize();
1662 expect(spy
).to
.have
.been
.calledOnce
;
1663 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1666 it('should handle a resize requested by another client', function () {
1667 var reason_for_change
= 2; // requested by another client
1668 var status_code
= 0; // No error
1670 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1671 width
: 20, height
: 50, encoding
: -308 }],
1672 make_screen_data(1), client
);
1674 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1675 expect(client
._fb_width
).to
.equal(20);
1676 expect(client
._fb_height
).to
.equal(50);
1678 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1679 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1681 var spy
= client
.get_onFBResize();
1682 expect(spy
).to
.have
.been
.calledOnce
;
1683 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1686 it('should be able to recieve requests which contain data for multiple screens', function () {
1687 var reason_for_change
= 2; // requested by another client
1688 var status_code
= 0; // No error
1690 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1691 width
: 60, height
: 50, encoding
: -308 }],
1692 make_screen_data(3), client
);
1694 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1695 expect(client
._fb_width
).to
.equal(60);
1696 expect(client
._fb_height
).to
.equal(50);
1698 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1699 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1701 var spy
= client
.get_onFBResize();
1702 expect(spy
).to
.have
.been
.calledOnce
;
1703 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 60, 50);
1706 it('should not handle a failed request', function () {
1707 var reason_for_change
= 1; // requested by this client
1708 var status_code
= 1; // Resize is administratively prohibited
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(4);
1715 expect(client
._fb_height
).to
.equal(4);
1717 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1719 var spy
= client
.get_onFBResize();
1720 expect(spy
).to
.not
.have
.been
.called
;
1724 it
.skip('should handle the Cursor pseudo-encoding', function () {
1725 // TODO(directxman12): test
1728 it('should handle the last_rect pseudo-encoding', function () {
1729 client
.set_onFBUReceive(sinon
.spy());
1730 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1731 expect(client
._FBU
.rects
).to
.equal(0);
1732 expect(client
.get_onFBUReceive()).to
.have
.been
.calledOnce
;
1737 describe('XVP Message Handling', function () {
1738 beforeEach(function () {
1739 client
= make_rfb();
1740 client
.connect('host', 8675);
1741 client
._sock
._websocket
._open();
1742 client
._rfb_connection_state
= 'connected';
1743 client
._fb_name
= 'some device';
1744 client
._fb_width
= 27;
1745 client
._fb_height
= 32;
1748 it('should send a notification on XVP_FAIL', function () {
1749 client
.set_onNotification(sinon
.spy());
1750 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 0]));
1751 var spy
= client
.get_onNotification();
1752 expect(spy
).to
.have
.been
.calledOnce
;
1753 expect(spy
.args
[0][1]).to
.equal('XVP Operation Failed');
1756 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1757 client
.set_onXvpInit(sinon
.spy());
1758 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1759 expect(client
._rfb_xvp_ver
).to
.equal(10);
1760 expect(client
.get_onXvpInit()).to
.have
.been
.calledOnce
;
1761 expect(client
.get_onXvpInit()).to
.have
.been
.calledWith(10);
1764 it('should fail on unknown XVP message types', function () {
1765 sinon
.spy(client
, "_fail");
1766 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1767 expect(client
._fail
).to
.have
.been
.calledOnce
;
1771 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1772 var expected_str
= 'cheese!';
1773 var data
= [3, 0, 0, 0];
1774 push32(data
, expected_str
.length
);
1775 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1776 client
.set_onClipboard(sinon
.spy());
1778 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1779 var spy
= client
.get_onClipboard();
1780 expect(spy
).to
.have
.been
.calledOnce
;
1781 expect(spy
.args
[0][1]).to
.equal(expected_str
);
1784 it('should fire the bell callback on Bell', function () {
1785 client
.set_onBell(sinon
.spy());
1786 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1787 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1790 it('should respond correctly to ServerFence', function () {
1791 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1792 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1794 var payload
= "foo\x00ab9";
1796 // ClientFence and ServerFence are identical in structure
1797 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1798 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1800 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1802 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1804 expected_msg
._sQlen
= 0;
1805 incoming_msg
._sQlen
= 0;
1807 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1808 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1810 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1812 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1815 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1816 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1818 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1820 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1822 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1824 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1825 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1828 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1829 client
._enabledContinuousUpdates
= true;
1830 client
._supportsContinuousUpdates
= true;
1832 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1834 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1837 it('should update continuous updates on resize', function () {
1838 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1839 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1841 client
._resize(450, 160);
1843 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1845 client
._enabledContinuousUpdates
= true;
1847 client
._resize(90, 700);
1849 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1852 it('should fail on an unknown message type', function () {
1853 sinon
.spy(client
, "_fail");
1854 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1855 expect(client
._fail
).to
.have
.been
.calledOnce
;
1859 describe('Asynchronous Events', function () {
1860 describe('Mouse event handlers', function () {
1862 beforeEach(function () {
1863 client
= make_rfb();
1864 client
._sock
= new Websock();
1865 client
._sock
.open('ws://', 'binary');
1866 client
._sock
._websocket
._open();
1867 sinon
.spy(client
._sock
, 'flush');
1868 client
._rfb_connection_state
= 'connected';
1871 it('should not send button messages in view-only mode', function () {
1872 client
._view_only
= true;
1873 client
._mouse
._onMouseButton(0, 0, 1, 0x001);
1874 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1877 it('should not send movement messages in view-only mode', function () {
1878 client
._view_only
= true;
1879 client
._mouse
._onMouseMove(0, 0);
1880 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1883 it('should send a pointer event on mouse button presses', function () {
1884 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1885 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1886 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1887 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1890 it('should send a mask of 1 on mousedown', function () {
1891 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1892 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1893 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1894 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1897 it('should send a mask of 0 on mouseup', function () {
1898 client
._mouse_buttonMask
= 0x001;
1899 client
._mouse
._onMouseButton(10, 12, 0, 0x001);
1900 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1901 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1902 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1905 it('should send a pointer event on mouse movement', function () {
1906 client
._mouse
._onMouseMove(10, 12);
1907 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1908 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1909 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1912 it('should set the button mask so that future mouse movements use it', function () {
1913 client
._mouse
._onMouseButton(10, 12, 1, 0x010);
1914 client
._mouse
._onMouseMove(13, 9);
1915 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1916 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1917 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1918 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1921 // NB(directxman12): we don't need to test not sending messages in
1922 // non-normal modes, since we haven't grabbed input
1923 // yet (grabbing input should be checked in the lifecycle tests).
1925 it('should not send movement messages when viewport dragging', function () {
1926 client
._viewportDragging
= true;
1927 client
._display
.viewportChangePos
= sinon
.spy();
1928 client
._mouse
._onMouseMove(13, 9);
1929 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1932 it('should not send button messages when initiating viewport dragging', function () {
1933 client
._viewportDrag
= true;
1934 client
._mouse
._onMouseButton(13, 9, 0x001);
1935 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1938 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1939 client
._viewportDrag
= true;
1940 client
._mouse
._onMouseButton(13, 9, 0x001);
1941 expect(client
._viewportDragging
).to
.be
.true;
1942 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1945 it('should terminate viewport dragging on a button up event, if enabled', function () {
1946 client
._viewportDrag
= true;
1947 client
._viewportDragging
= true;
1948 client
._mouse
._onMouseButton(13, 9, 0x000);
1949 expect(client
._viewportDragging
).to
.be
.false;
1952 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1955 var newX
= 123 + 11 * window
.devicePixelRatio
;
1956 var newY
= 109 + 4 * window
.devicePixelRatio
;
1958 client
._viewportDrag
= true;
1959 client
._viewportDragging
= true;
1960 client
._viewportHasMoved
= false;
1961 client
._viewportDragPos
= { x
: oldX
, y
: oldY
};
1962 client
._display
.viewportChangePos
= sinon
.spy();
1964 client
._mouse
._onMouseMove(newX
, newY
);
1966 expect(client
._viewportDragging
).to
.be
.true;
1967 expect(client
._viewportHasMoved
).to
.be
.true;
1968 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: newX
, y
: newY
});
1969 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
1970 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(oldX
- newX
, oldY
- newY
);
1974 describe('Keyboard Event Handlers', function () {
1976 beforeEach(function () {
1977 client
= make_rfb();
1978 client
._sock
= new Websock();
1979 client
._sock
.open('ws://', 'binary');
1980 client
._sock
._websocket
._open();
1981 sinon
.spy(client
._sock
, 'flush');
1982 client
._rfb_connection_state
= 'connected';
1983 client
._view_only
= false;
1986 it('should send a key message on a key press', function () {
1988 client
._keyboard
._onKeyEvent(0x41, 'KeyA', true);
1989 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
1990 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
1991 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
1994 it('should not send messages in view-only mode', function () {
1995 client
._view_only
= true;
1996 client
._keyboard
._onKeyEvent('a', 'KeyA', true);
1997 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2001 describe('WebSocket event handlers', function () {
2003 beforeEach(function () {
2004 client
= make_rfb();
2005 this.clock
= sinon
.useFakeTimers();
2008 afterEach(function () { this.clock
.restore(); });
2011 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
2012 client
.connect('host', 8675);
2013 client
._rfb_connection_state
= 'connected';
2014 client
._normal_msg
= sinon
.spy();
2015 client
._sock
._websocket
._receive_data(new Uint8Array([]));
2016 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
2019 it('should handle a message in the connected state as a normal message', function () {
2020 client
.connect('host', 8675);
2021 client
._rfb_connection_state
= 'connected';
2022 client
._normal_msg
= sinon
.spy();
2023 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2024 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
2027 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2028 client
.connect('host', 8675);
2029 client
._rfb_init_state
= 'ProtocolVersion';
2030 client
._init_msg
= sinon
.spy();
2031 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2032 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
2035 it('should process all normal messages directly', function () {
2036 client
.connect('host', 8675);
2037 client
._sock
._websocket
._open();
2038 client
._rfb_connection_state
= 'connected';
2039 client
.set_onBell(sinon
.spy());
2040 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2041 expect(client
.get_onBell()).to
.have
.been
.calledTwice
;
2045 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2046 client
.connect('host', 8675);
2047 client
._sock
._websocket
._open();
2048 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2051 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2052 sinon
.spy(client
, "_fail");
2053 client
.connect('host', 8675);
2054 client
._rfb_connection_state
= 'some_other_state';
2055 client
._sock
._websocket
._open();
2056 expect(client
._fail
).to
.have
.been
.calledOnce
;
2060 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2061 client
.connect('host', 8675);
2062 client
._rfb_connection_state
= 'disconnecting';
2063 client
._sock
._websocket
.close();
2064 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2067 it('should fail if we get a close event while connecting', function () {
2068 sinon
.spy(client
, "_fail");
2069 client
.connect('host', 8675);
2070 client
._rfb_connection_state
= 'connecting';
2071 client
._sock
._websocket
.close();
2072 expect(client
._fail
).to
.have
.been
.calledOnce
;
2075 it('should fail if we get a close event while disconnected', function () {
2076 sinon
.spy(client
, "_fail");
2077 client
.connect('host', 8675);
2078 client
._rfb_connection_state
= 'disconnected';
2079 client
._sock
._websocket
.close();
2080 expect(client
._fail
).to
.have
.been
.calledOnce
;
2083 it('should unregister close event handler', function () {
2084 sinon
.spy(client
._sock
, 'off');
2085 client
.connect('host', 8675);
2086 client
._rfb_connection_state
= 'disconnecting';
2087 client
._sock
._websocket
.close();
2088 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2091 // error events do nothing