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('wss://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 URL', function () {
82 client
._fail
= sinon
.spy();
83 client
._rfb_connection_state
= '';
84 client
.connect(undefined);
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
.args
[0][1]).to
.equal('connecting');
353 it('should set the rfb_connection_state', function () {
354 client
._rfb_connection_state
= 'disconnecting';
355 client
._updateConnectionState('disconnected');
356 expect(client
._rfb_connection_state
).to
.equal('disconnected');
359 it('should not change the state when we are disconnected', function () {
360 client
._rfb_connection_state
= 'disconnected';
361 client
._updateConnectionState('connecting');
362 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
365 it('should ignore state changes to the same state', function () {
366 client
.set_onUpdateState(sinon
.spy());
367 client
._rfb_connection_state
= 'connecting';
368 client
._updateConnectionState('connecting');
369 var spy
= client
.get_onUpdateState();
370 expect(spy
).to
.not
.have
.been
.called
;
373 it('should ignore illegal state changes', function () {
374 client
.set_onUpdateState(sinon
.spy());
375 client
._rfb_connection_state
= 'connected';
376 client
._updateConnectionState('disconnected');
377 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
378 var spy
= client
.get_onUpdateState();
379 expect(spy
).to
.not
.have
.been
.called
;
383 describe('#_fail', function () {
385 beforeEach(function () {
386 this.clock
= sinon
.useFakeTimers();
388 client
.connect('wss://host:8675');
391 afterEach(function () {
392 this.clock
.restore();
395 it('should close the WebSocket connection', function () {
396 sinon
.spy(client
._sock
, 'close');
398 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
401 it('should transition to disconnected', function () {
402 sinon
.spy(client
, '_updateConnectionState');
404 this.clock
.tick(2000);
405 expect(client
._updateConnectionState
).to
.have
.been
.called
;
406 expect(client
._rfb_connection_state
).to
.equal('disconnected');
409 it('should set disconnect_reason', function () {
410 client
._rfb_connection_state
= 'connected';
411 client
._fail('a reason');
412 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
415 it('should not include details in disconnect_reason', function () {
416 client
._rfb_connection_state
= 'connected';
417 client
._fail('a reason', 'details');
418 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
421 it('should result in disconnect callback with message when reason given', function () {
422 client
._rfb_connection_state
= 'connected';
423 client
.set_onDisconnected(sinon
.spy());
424 client
._fail('a reason');
425 var spy
= client
.get_onDisconnected();
426 this.clock
.tick(2000);
427 expect(spy
).to
.have
.been
.calledOnce
;
428 expect(spy
.args
[0].length
).to
.equal(2);
429 expect(spy
.args
[0][1]).to
.equal('a reason');
434 describe('#_notification', function () {
436 beforeEach(function () { client
= make_rfb(); });
438 it('should call the notification callback', function () {
439 client
.set_onNotification(sinon
.spy());
440 client
._notification('notify!', 'warn');
441 var spy
= client
.get_onNotification();
442 expect(spy
).to
.have
.been
.calledOnce
;
443 expect(spy
.args
[0][1]).to
.equal('notify!');
444 expect(spy
.args
[0][2]).to
.equal('warn');
447 it('should not call the notification callback when level is invalid', function () {
448 client
.set_onNotification(sinon
.spy());
449 client
._notification('notify!', 'invalid');
450 var spy
= client
.get_onNotification();
451 expect(spy
).to
.not
.have
.been
.called
;
456 describe('Connection States', function () {
457 describe('connecting', function () {
459 beforeEach(function () { client
= make_rfb(); });
461 it('should actually connect to the websocket', function () {
462 sinon
.spy(client
._sock
, 'open');
463 client
._updateConnectionState('connecting');
464 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
467 it('should use a url specified to connect', function () {
468 sinon
.spy(client
._sock
, 'open');
469 client
._url
= 'ws://HOST:8675/PATH';
470 client
._updateConnectionState('connecting');
471 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
475 describe('disconnecting', function () {
477 beforeEach(function () {
478 this.clock
= sinon
.useFakeTimers();
480 client
.connect('wss://host:8675');
483 afterEach(function () {
484 this.clock
.restore();
487 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
488 sinon
.spy(client
, '_updateConnectionState');
489 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
490 client
._updateConnectionState('disconnecting');
491 this.clock
.tick(client
.get_disconnectTimeout() * 1000);
492 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
493 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
494 expect(client
._rfb_connection_state
).to
.equal("disconnected");
497 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
498 client
._updateConnectionState('disconnecting');
499 this.clock
.tick(client
.get_disconnectTimeout() * 500);
500 client
._sock
._websocket
.close();
501 this.clock
.tick(client
.get_disconnectTimeout() * 500 + 1);
502 expect(client
._rfb_connection_state
).to
.equal('disconnected');
505 it('should close the WebSocket connection', function () {
506 sinon
.spy(client
._sock
, 'close');
507 client
._updateConnectionState('disconnecting');
508 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
512 describe('disconnected', function () {
514 beforeEach(function () { client
= make_rfb(); });
516 it('should call the disconnect callback if the state is "disconnected"', function () {
517 client
.set_onDisconnected(sinon
.spy());
518 client
._rfb_connection_state
= 'disconnecting';
519 client
._rfb_disconnect_reason
= "error";
520 client
._updateConnectionState('disconnected');
521 var spy
= client
.get_onDisconnected();
522 expect(spy
).to
.have
.been
.calledOnce
;
523 expect(spy
.args
[0][1]).to
.equal("error");
526 it('should not call the disconnect callback if the state is not "disconnected"', function () {
527 client
.set_onDisconnected(sinon
.spy());
528 client
._updateConnectionState('disconnecting');
529 var spy
= client
.get_onDisconnected();
530 expect(spy
).to
.not
.have
.been
.called
;
533 it('should call the disconnect callback without msg when no reason given', function () {
534 client
.set_onDisconnected(sinon
.spy());
535 client
._rfb_connection_state
= 'disconnecting';
536 client
._rfb_disconnect_reason
= "";
537 client
._updateConnectionState('disconnected');
538 var spy
= client
.get_onDisconnected();
539 expect(spy
).to
.have
.been
.calledOnce
;
540 expect(spy
.args
[0].length
).to
.equal(1);
543 it('should call the updateState callback before the disconnect callback', function () {
544 client
.set_onDisconnected(sinon
.spy());
545 client
.set_onUpdateState(sinon
.spy());
546 client
._rfb_connection_state
= 'disconnecting';
547 client
._updateConnectionState('disconnected');
548 var updateStateSpy
= client
.get_onUpdateState();
549 var disconnectSpy
= client
.get_onDisconnected();
550 expect(updateStateSpy
.calledBefore(disconnectSpy
)).to
.be
.true;
554 // NB(directxman12): Connected does *nothing* in updateConnectionState
557 describe('Protocol Initialization States', function () {
558 describe('ProtocolVersion', function () {
560 beforeEach(function () {
562 client
.connect('wss://host:8675');
563 client
._sock
._websocket
._open();
566 function send_ver (ver
, client
) {
567 var arr
= new Uint8Array(12);
568 for (var i
= 0; i
< ver
.length
; i
++) {
569 arr
[i
+4] = ver
.charCodeAt(i
);
571 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
573 client
._sock
._websocket
._receive_data(arr
);
576 describe('version parsing', function () {
578 beforeEach(function () {
580 client
.connect('wss://host:8675');
581 client
._sock
._websocket
._open();
584 it('should interpret version 000.000 as a repeater', function () {
585 client
._repeaterID
= '12345';
586 send_ver('000.000', client
);
587 expect(client
._rfb_version
).to
.equal(0);
589 var sent_data
= client
._sock
._websocket
._get_sent_data();
590 expect(new Uint8Array(sent_data
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
593 it('should interpret version 003.003 as version 3.3', function () {
594 send_ver('003.003', client
);
595 expect(client
._rfb_version
).to
.equal(3.3);
598 it('should interpret version 003.006 as version 3.3', function () {
599 send_ver('003.006', client
);
600 expect(client
._rfb_version
).to
.equal(3.3);
603 it('should interpret version 003.889 as version 3.3', function () {
604 send_ver('003.889', client
);
605 expect(client
._rfb_version
).to
.equal(3.3);
608 it('should interpret version 003.007 as version 3.7', function () {
609 send_ver('003.007', client
);
610 expect(client
._rfb_version
).to
.equal(3.7);
613 it('should interpret version 003.008 as version 3.8', function () {
614 send_ver('003.008', client
);
615 expect(client
._rfb_version
).to
.equal(3.8);
618 it('should interpret version 004.000 as version 3.8', function () {
619 send_ver('004.000', client
);
620 expect(client
._rfb_version
).to
.equal(3.8);
623 it('should interpret version 004.001 as version 3.8', function () {
624 send_ver('004.001', client
);
625 expect(client
._rfb_version
).to
.equal(3.8);
628 it('should interpret version 005.000 as version 3.8', function () {
629 send_ver('005.000', client
);
630 expect(client
._rfb_version
).to
.equal(3.8);
633 it('should fail on an invalid version', function () {
634 sinon
.spy(client
, "_fail");
635 send_ver('002.000', client
);
636 expect(client
._fail
).to
.have
.been
.calledOnce
;
640 it('should handle two step repeater negotiation', function () {
641 client
._repeaterID
= '12345';
643 send_ver('000.000', client
);
644 expect(client
._rfb_version
).to
.equal(0);
645 var sent_data
= client
._sock
._websocket
._get_sent_data();
646 expect(new Uint8Array(sent_data
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
647 expect(sent_data
).to
.have
.length(250);
649 send_ver('003.008', client
);
650 expect(client
._rfb_version
).to
.equal(3.8);
653 it('should send back the interpreted version', function () {
654 send_ver('004.000', client
);
656 var expected_str
= 'RFB 003.008\n';
658 for (var i
= 0; i
< expected_str
.length
; i
++) {
659 expected
[i
] = expected_str
.charCodeAt(i
);
662 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
665 it('should transition to the Security state on successful negotiation', function () {
666 send_ver('003.008', client
);
667 expect(client
._rfb_init_state
).to
.equal('Security');
671 describe('Security', function () {
674 beforeEach(function () {
676 client
.connect('wss://host:8675');
677 client
._sock
._websocket
._open();
678 client
._rfb_init_state
= 'Security';
681 it('should simply receive the auth scheme when for versions < 3.7', function () {
682 client
._rfb_version
= 3.6;
683 var auth_scheme_raw
= [1, 2, 3, 4];
684 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
685 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
686 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
687 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
690 it('should prefer no authentication is possible', function () {
691 client
._rfb_version
= 3.7;
692 var auth_schemes
= [2, 1, 3];
693 client
._sock
._websocket
._receive_data(auth_schemes
);
694 expect(client
._rfb_auth_scheme
).to
.equal(1);
695 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
698 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
699 client
._rfb_version
= 3.7;
700 var auth_schemes
= [2, 22, 16];
701 client
._sock
._websocket
._receive_data(auth_schemes
);
702 expect(client
._rfb_auth_scheme
).to
.equal(22);
703 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
706 it('should fail if there are no supported schemes for versions >= 3.7', function () {
707 sinon
.spy(client
, "_fail");
708 client
._rfb_version
= 3.7;
709 var auth_schemes
= [1, 32];
710 client
._sock
._websocket
._receive_data(auth_schemes
);
711 expect(client
._fail
).to
.have
.been
.calledOnce
;
714 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
715 client
._rfb_version
= 3.7;
716 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
717 sinon
.spy(client
, '_fail');
718 client
._sock
._websocket
._receive_data(failure_data
);
720 expect(client
._fail
).to
.have
.been
.calledOnce
;
721 expect(client
._fail
).to
.have
.been
.calledWith(
722 'Error while negotiating with server','Security failure: whoops');
725 it('should transition to the Authentication state and continue on successful negotiation', function () {
726 client
._rfb_version
= 3.7;
727 var auth_schemes
= [1, 1];
728 client
._negotiate_authentication
= sinon
.spy();
729 client
._sock
._websocket
._receive_data(auth_schemes
);
730 expect(client
._rfb_init_state
).to
.equal('Authentication');
731 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
735 describe('Authentication', function () {
738 beforeEach(function () {
740 client
.connect('wss://host:8675');
741 client
._sock
._websocket
._open();
742 client
._rfb_init_state
= 'Security';
745 function send_security(type
, cl
) {
746 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
749 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
750 client
._rfb_version
= 3.6;
751 var err_msg
= "Whoopsies";
752 var data
= [0, 0, 0, 0];
753 var err_len
= err_msg
.length
;
754 push32(data
, err_len
);
755 for (var i
= 0; i
< err_len
; i
++) {
756 data
.push(err_msg
.charCodeAt(i
));
759 sinon
.spy(client
, '_fail');
760 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
761 expect(client
._fail
).to
.have
.been
.calledWith(
762 'Authentication failure', 'Whoopsies');
765 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
766 client
._rfb_version
= 3.8;
767 send_security(1, client
);
768 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
771 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
772 client
._rfb_version
= 3.7;
773 send_security(1, client
);
774 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
777 it('should fail on an unknown auth scheme', function () {
778 sinon
.spy(client
, "_fail");
779 client
._rfb_version
= 3.8;
780 send_security(57, client
);
781 expect(client
._fail
).to
.have
.been
.calledOnce
;
784 describe('VNC Authentication (type 2) Handler', function () {
787 beforeEach(function () {
789 client
.connect('wss://host:8675');
790 client
._sock
._websocket
._open();
791 client
._rfb_init_state
= 'Security';
792 client
._rfb_version
= 3.8;
795 it('should call the onCredentialsRequired callback if missing a password', function () {
796 client
.set_onCredentialsRequired(sinon
.spy());
797 send_security(2, client
);
800 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
801 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
803 var spy
= client
.get_onCredentialsRequired();
804 expect(client
._rfb_credentials
).to
.be
.empty
;
805 expect(spy
).to
.have
.been
.calledOnce
;
806 expect(spy
.args
[0][1]).to
.have
.members(["password"]);
809 it('should encrypt the password with DES and then send it back', function () {
810 client
._rfb_credentials
= { password
: 'passwd' };
811 send_security(2, client
);
812 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
815 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
816 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
818 var des_pass
= RFB
.genDES('passwd', challenge
);
819 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
822 it('should transition to SecurityResult immediately after sending the password', function () {
823 client
._rfb_credentials
= { password
: 'passwd' };
824 send_security(2, client
);
827 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
828 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
830 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
834 describe('XVP Authentication (type 22) Handler', function () {
837 beforeEach(function () {
839 client
.connect('wss://host:8675');
840 client
._sock
._websocket
._open();
841 client
._rfb_init_state
= 'Security';
842 client
._rfb_version
= 3.8;
845 it('should fall through to standard VNC authentication upon completion', function () {
846 client
._rfb_credentials
= { username
: 'user',
848 password
: 'password' };
849 client
._negotiate_std_vnc_auth
= sinon
.spy();
850 send_security(22, client
);
851 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
854 it('should call the onCredentialsRequired callback if all credentials are missing', function() {
855 client
.set_onCredentialsRequired(sinon
.spy());
856 client
._rfb_credentials
= {};
857 send_security(22, client
);
859 var spy
= client
.get_onCredentialsRequired();
860 expect(client
._rfb_credentials
).to
.be
.empty
;
861 expect(spy
).to
.have
.been
.calledOnce
;
862 expect(spy
.args
[0][1]).to
.have
.members(["username", "password", "target"]);
865 it('should call the onCredentialsRequired callback if some credentials are missing', function() {
866 client
.set_onCredentialsRequired(sinon
.spy());
867 client
._rfb_credentials
= { username
: 'user',
869 send_security(22, client
);
871 var spy
= client
.get_onCredentialsRequired();
872 expect(spy
).to
.have
.been
.calledOnce
;
873 expect(spy
.args
[0][1]).to
.have
.members(["username", "password", "target"]);
876 it('should send user and target separately', function () {
877 client
._rfb_credentials
= { username
: 'user',
879 password
: 'password' };
880 client
._negotiate_std_vnc_auth
= sinon
.spy();
882 send_security(22, client
);
884 var expected
= [22, 4, 6]; // auth selection, len user, len target
885 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
887 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
891 describe('TightVNC Authentication (type 16) Handler', function () {
894 beforeEach(function () {
896 client
.connect('wss://host:8675');
897 client
._sock
._websocket
._open();
898 client
._rfb_init_state
= 'Security';
899 client
._rfb_version
= 3.8;
900 send_security(16, client
);
901 client
._sock
._websocket
._get_sent_data(); // skip the security reply
904 function send_num_str_pairs(pairs
, client
) {
905 var pairs_len
= pairs
.length
;
907 push32(data
, pairs_len
);
909 for (var i
= 0; i
< pairs_len
; i
++) {
910 push32(data
, pairs
[i
][0]);
912 for (j
= 0; j
< 4; j
++) {
913 data
.push(pairs
[i
][1].charCodeAt(j
));
915 for (j
= 0; j
< 8; j
++) {
916 data
.push(pairs
[i
][2].charCodeAt(j
));
920 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
923 it('should skip tunnel negotiation if no tunnels are requested', function () {
924 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
925 expect(client
._rfb_tightvnc
).to
.be
.true;
928 it('should fail if no supported tunnels are listed', function () {
929 sinon
.spy(client
, "_fail");
930 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
931 expect(client
._fail
).to
.have
.been
.calledOnce
;
934 it('should choose the notunnel tunnel type', function () {
935 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
936 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
939 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
940 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
941 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
942 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
943 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
944 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
947 /*it('should attempt to use VNC auth over no auth when possible', function () {
948 client._rfb_tightvnc = true;
949 client._negotiate_std_vnc_auth = sinon.spy();
950 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
951 expect(client._sock).to.have.sent([0, 0, 0, 1]);
952 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
953 expect(client._rfb_auth_scheme).to.equal(2);
954 });*/ // while this would make sense, the original code doesn't actually do this
956 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
957 client
._rfb_tightvnc
= true;
958 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
959 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
960 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
963 it('should accept VNC authentication and transition to that', function () {
964 client
._rfb_tightvnc
= true;
965 client
._negotiate_std_vnc_auth
= sinon
.spy();
966 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
967 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
968 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
969 expect(client
._rfb_auth_scheme
).to
.equal(2);
972 it('should fail if there are no supported auth types', function () {
973 sinon
.spy(client
, "_fail");
974 client
._rfb_tightvnc
= true;
975 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
976 expect(client
._fail
).to
.have
.been
.calledOnce
;
981 describe('SecurityResult', function () {
984 beforeEach(function () {
986 client
.connect('wss://host:8675');
987 client
._sock
._websocket
._open();
988 client
._rfb_init_state
= 'SecurityResult';
991 it('should fall through to ServerInitialisation on a response code of 0', function () {
992 client
._updateConnectionState
= sinon
.spy();
993 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
994 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
997 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
998 client
._rfb_version
= 3.8;
999 sinon
.spy(client
, '_fail');
1000 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1001 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1002 expect(client
._fail
).to
.have
.been
.calledWith(
1003 'Authentication failure', 'whoops');
1006 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1007 sinon
.spy(client
, '_fail');
1008 client
._rfb_version
= 3.7;
1009 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
1010 expect(client
._fail
).to
.have
.been
.calledWith('Authentication failure');
1014 describe('ClientInitialisation', function () {
1017 beforeEach(function () {
1018 client
= make_rfb();
1019 client
.connect('wss://host:8675');
1020 client
._sock
._websocket
._open();
1021 client
._rfb_init_state
= 'SecurityResult';
1024 it('should transition to the ServerInitialisation state', function () {
1025 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1026 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1029 it('should send 1 if we are in shared mode', function () {
1030 client
.set_shared(true);
1031 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1032 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1035 it('should send 0 if we are not in shared mode', function () {
1036 client
.set_shared(false);
1037 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1038 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1042 describe('ServerInitialisation', function () {
1045 beforeEach(function () {
1046 client
= make_rfb();
1047 client
.connect('wss://host:8675');
1048 client
._sock
._websocket
._open();
1049 client
._rfb_init_state
= 'ServerInitialisation';
1052 function send_server_init(opts
, client
) {
1053 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1054 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1055 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1056 for (var opt
in opts
) {
1057 full_opts
[opt
] = opts
[opt
];
1061 push16(data
, full_opts
.width
);
1062 push16(data
, full_opts
.height
);
1064 data
.push(full_opts
.bpp
);
1065 data
.push(full_opts
.depth
);
1066 data
.push(full_opts
.big_endian
);
1067 data
.push(full_opts
.true_color
);
1069 push16(data
, full_opts
.red_max
);
1070 push16(data
, full_opts
.green_max
);
1071 push16(data
, full_opts
.blue_max
);
1072 push8(data
, full_opts
.red_shift
);
1073 push8(data
, full_opts
.green_shift
);
1074 push8(data
, full_opts
.blue_shift
);
1081 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1084 push32(name_data
, full_opts
.name
.length
);
1085 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1086 name_data
.push(full_opts
.name
.charCodeAt(i
));
1088 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1091 it('should set the framebuffer width and height', function () {
1092 send_server_init({ width
: 32, height
: 84 }, client
);
1093 expect(client
._fb_width
).to
.equal(32);
1094 expect(client
._fb_height
).to
.equal(84);
1097 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1099 it('should set the framebuffer name and call the callback', function () {
1100 client
.set_onDesktopName(sinon
.spy());
1101 send_server_init({ name
: 'some name' }, client
);
1103 var spy
= client
.get_onDesktopName();
1104 expect(client
._fb_name
).to
.equal('some name');
1105 expect(spy
).to
.have
.been
.calledOnce
;
1106 expect(spy
.args
[0][1]).to
.equal('some name');
1109 it('should handle the extended init message of the tight encoding', function () {
1110 // NB(sross): we don't actually do anything with it, so just test that we can
1111 // read it w/o throwing an error
1112 client
._rfb_tightvnc
= true;
1113 send_server_init({}, client
);
1115 var tight_data
= [];
1116 push16(tight_data
, 1);
1117 push16(tight_data
, 2);
1118 push16(tight_data
, 3);
1119 push16(tight_data
, 0);
1120 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1123 client
._sock
._websocket
._receive_data(tight_data
);
1125 expect(client
._rfb_connection_state
).to
.equal('connected');
1128 it('should call the resize callback and resize the display', function () {
1129 client
.set_onFBResize(sinon
.spy());
1130 sinon
.spy(client
._display
, 'resize');
1131 send_server_init({ width
: 27, height
: 32 }, client
);
1133 var spy
= client
.get_onFBResize();
1134 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1135 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1136 expect(spy
).to
.have
.been
.calledOnce
;
1137 expect(spy
.args
[0][1]).to
.equal(27);
1138 expect(spy
.args
[0][2]).to
.equal(32);
1141 it('should grab the mouse and keyboard', function () {
1142 sinon
.spy(client
._keyboard
, 'grab');
1143 sinon
.spy(client
._mouse
, 'grab');
1144 send_server_init({}, client
);
1145 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1146 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1149 describe('Initial Update Request', function () {
1150 beforeEach(function () {
1151 sinon
.spy(RFB
.messages
, "pixelFormat");
1152 sinon
.spy(RFB
.messages
, "clientEncodings");
1153 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1156 afterEach(function () {
1157 RFB
.messages
.pixelFormat
.restore();
1158 RFB
.messages
.clientEncodings
.restore();
1159 RFB
.messages
.fbUpdateRequest
.restore();
1162 // TODO(directxman12): test the various options in this configuration matrix
1163 it('should reply with the pixel format, client encodings, and initial update request', function () {
1164 send_server_init({ width
: 27, height
: 32 }, client
);
1166 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1167 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1168 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1169 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1170 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1171 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1172 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1173 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1176 it('should reply with restricted settings for Intel AMT servers', function () {
1177 send_server_init({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1179 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1180 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1181 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1182 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1183 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1184 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1185 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1186 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1187 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1191 it('should transition to the "connected" state', function () {
1192 send_server_init({}, client
);
1193 expect(client
._rfb_connection_state
).to
.equal('connected');
1198 describe('Protocol Message Processing After Completing Initialization', function () {
1201 beforeEach(function () {
1202 client
= make_rfb();
1203 client
.connect('wss://host:8675');
1204 client
._sock
._websocket
._open();
1205 client
._rfb_connection_state
= 'connected';
1206 client
._fb_name
= 'some device';
1207 client
._fb_width
= 640;
1208 client
._fb_height
= 20;
1211 describe('Framebuffer Update Handling', function () {
1212 var target_data_arr
= [
1213 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1214 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1215 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1216 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1220 var target_data_check_arr
= [
1221 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1222 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1223 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1224 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1226 var target_data_check
;
1228 before(function () {
1229 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1230 target_data
= new Uint8Array(target_data_arr
);
1231 target_data_check
= new Uint8Array(target_data_check_arr
);
1234 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1237 if (!rect_cnt
|| rect_cnt
> -1) {
1239 data
.push(0); // msg type
1240 data
.push(0); // padding
1241 push16(data
, rect_cnt
|| rect_data
.length
);
1244 for (var i
= 0; i
< rect_data
.length
; i
++) {
1246 push16(data
, rect_info
[i
].x
);
1247 push16(data
, rect_info
[i
].y
);
1248 push16(data
, rect_info
[i
].width
);
1249 push16(data
, rect_info
[i
].height
);
1250 push32(data
, rect_info
[i
].encoding
);
1252 data
= data
.concat(rect_data
[i
]);
1255 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1258 it('should send an update request if there is sufficient data', function () {
1259 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1260 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1262 client
._framebufferUpdate = function () { return true; };
1263 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1265 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1268 it('should not send an update request if we need more data', function () {
1269 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1270 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1273 it('should resume receiving an update if we previously did not have enough data', function () {
1274 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1275 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1277 // just enough to set FBU.rects
1278 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1279 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1281 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1282 // 247 should *not* be used as the message type here
1283 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1284 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1287 it('should not send a request in continuous updates mode', function () {
1288 client
._enabledContinuousUpdates
= true;
1289 client
._framebufferUpdate = function () { return true; };
1290 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1292 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1295 it('should fail on an unsupported encoding', function () {
1296 sinon
.spy(client
, "_fail");
1297 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1298 send_fbu_msg([rect_info
], [[]], client
);
1299 expect(client
._fail
).to
.have
.been
.calledOnce
;
1302 it('should be able to pause and resume receiving rects if not enought data', function () {
1303 // seed some initial data to copy
1304 client
._fb_width
= 4;
1305 client
._fb_height
= 4;
1306 client
._display
.resize(4, 4);
1307 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1309 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1310 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1311 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1312 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1313 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1314 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1315 expect(client
._display
).to
.have
.displayed(target_data_check
);
1318 describe('Message Encoding Handlers', function () {
1321 beforeEach(function () {
1322 client
= make_rfb();
1323 client
.connect('wss://host:8675');
1324 client
._sock
._websocket
._open();
1325 client
._rfb_connection_state
= 'connected';
1326 client
._fb_name
= 'some device';
1327 // a really small frame
1328 client
._fb_width
= 4;
1329 client
._fb_height
= 4;
1330 client
._fb_depth
= 24;
1331 client
._display
.resize(4, 4);
1334 it('should handle the RAW encoding', function () {
1335 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1336 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1337 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1338 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1341 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1342 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1343 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1344 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1345 send_fbu_msg(info
, rects
, client
);
1346 expect(client
._display
).to
.have
.displayed(target_data
);
1349 it('should handle the RAW encoding in low colour mode', function () {
1350 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1351 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1352 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1353 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1355 [0x03, 0x03, 0x03, 0x03],
1356 [0x0c, 0x0c, 0x0c, 0x0c],
1357 [0x0c, 0x0c, 0x03, 0x03],
1358 [0x0c, 0x0c, 0x03, 0x03]];
1359 client
._fb_depth
= 8;
1360 send_fbu_msg(info
, rects
, client
);
1361 expect(client
._display
).to
.have
.displayed(target_data_check
);
1364 it('should handle the COPYRECT encoding', function () {
1365 // seed some initial data to copy
1366 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1368 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1369 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1370 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1371 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1372 send_fbu_msg(info
, rects
, client
);
1373 expect(client
._display
).to
.have
.displayed(target_data_check
);
1376 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1377 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1379 it('should handle the RRE encoding', function () {
1380 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1382 push32(rect
, 2); // 2 subrects
1383 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1384 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1388 push16(rect
, 0); // x: 0
1389 push16(rect
, 0); // y: 0
1390 push16(rect
, 2); // width: 2
1391 push16(rect
, 2); // height: 2
1392 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1396 push16(rect
, 2); // x: 2
1397 push16(rect
, 2); // y: 2
1398 push16(rect
, 2); // width: 2
1399 push16(rect
, 2); // height: 2
1401 send_fbu_msg(info
, [rect
], client
);
1402 expect(client
._display
).to
.have
.displayed(target_data_check
);
1405 describe('the HEXTILE encoding handler', function () {
1407 beforeEach(function () {
1408 client
= make_rfb();
1409 client
.connect('wss://host:8675');
1410 client
._sock
._websocket
._open();
1411 client
._rfb_connection_state
= 'connected';
1412 client
._fb_name
= 'some device';
1413 // a really small frame
1414 client
._fb_width
= 4;
1415 client
._fb_height
= 4;
1416 client
._display
.resize(4, 4);
1419 it('should handle a tile with fg, bg specified, normal subrects', function () {
1420 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1422 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1423 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1424 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1428 rect
.push(2); // 2 subrects
1429 rect
.push(0); // x: 0, y: 0
1430 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1431 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1432 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1433 send_fbu_msg(info
, [rect
], client
);
1434 expect(client
._display
).to
.have
.displayed(target_data_check
);
1437 it('should handle a raw tile', function () {
1438 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1440 rect
.push(0x01); // raw
1441 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1442 rect
.push(target_data
[i
+ 2]);
1443 rect
.push(target_data
[i
+ 1]);
1444 rect
.push(target_data
[i
]);
1445 rect
.push(target_data
[i
+ 3]);
1447 send_fbu_msg(info
, [rect
], client
);
1448 expect(client
._display
).to
.have
.displayed(target_data
);
1451 it('should handle a tile with only bg specified (solid bg)', function () {
1452 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1455 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1456 send_fbu_msg(info
, [rect
], client
);
1459 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1460 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1463 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1464 // set the width so we can have two tiles
1465 client
._fb_width
= 8;
1466 client
._display
.resize(8, 4);
1468 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1474 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1476 // send an empty frame
1479 send_fbu_msg(info
, [rect
], client
);
1483 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1484 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1485 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1488 it('should handle a tile with bg and coloured subrects', function () {
1489 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1491 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1492 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1493 rect
.push(2); // 2 subrects
1494 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1498 rect
.push(0); // x: 0, y: 0
1499 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1500 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1504 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1505 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1506 send_fbu_msg(info
, [rect
], client
);
1507 expect(client
._display
).to
.have
.displayed(target_data_check
);
1510 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1511 client
._fb_width
= 4;
1512 client
._fb_height
= 17;
1513 client
._display
.resize(4, 17);
1515 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1517 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1518 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1519 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1523 rect
.push(8); // 8 subrects
1525 for (i
= 0; i
< 4; i
++) {
1526 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1527 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1528 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1529 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1531 rect
.push(0x08); // anysubrects
1532 rect
.push(1); // 1 subrect
1533 rect
.push(0); // x: 0, y: 0
1534 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1535 send_fbu_msg(info
, [rect
], client
);
1538 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1539 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1540 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1543 it('should fail on an invalid subencoding', function () {
1544 sinon
.spy(client
,"_fail");
1545 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1546 var rects
= [[45]]; // an invalid subencoding
1547 send_fbu_msg(info
, rects
, client
);
1548 expect(client
._fail
).to
.have
.been
.calledOnce
;
1552 it
.skip('should handle the TIGHT encoding', function () {
1553 // TODO(directxman12): test this
1556 it
.skip('should handle the TIGHT_PNG encoding', function () {
1557 // TODO(directxman12): test this
1560 it('should handle the DesktopSize pseduo-encoding', function () {
1561 client
.set_onFBResize(sinon
.spy());
1562 sinon
.spy(client
._display
, 'resize');
1563 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1565 var spy
= client
.get_onFBResize();
1566 expect(spy
).to
.have
.been
.calledOnce
;
1567 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1569 expect(client
._fb_width
).to
.equal(20);
1570 expect(client
._fb_height
).to
.equal(50);
1572 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1573 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1576 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1577 beforeEach(function () {
1578 client
._supportsSetDesktopSize
= false;
1579 // a really small frame
1580 client
._fb_width
= 4;
1581 client
._fb_height
= 4;
1582 client
._display
.resize(4, 4);
1583 sinon
.spy(client
._display
, 'resize');
1584 client
.set_onFBResize(sinon
.spy());
1587 function make_screen_data (nr_of_screens
) {
1589 push8(data
, nr_of_screens
); // number-of-screens
1590 push8(data
, 0); // padding
1591 push16(data
, 0); // padding
1592 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1593 push32(data
, 0); // id
1594 push16(data
, 0); // x-position
1595 push16(data
, 0); // y-position
1596 push16(data
, 20); // width
1597 push16(data
, 50); // height
1598 push32(data
, 0); // flags
1603 it('should call callback when resize is supported', function () {
1604 client
.set_onCapabilities(sinon
.spy());
1606 expect(client
._supportsSetDesktopSize
).to
.be
.false;
1607 expect(client
.get_capabilities().resize
).to
.be
.false;
1609 var reason_for_change
= 0; // server initiated
1610 var status_code
= 0; // No error
1612 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1613 width
: 4, height
: 4, encoding
: -308 }],
1614 make_screen_data(1), client
);
1616 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1617 expect(client
.get_onCapabilities()).to
.have
.been
.calledOnce
;
1618 expect(client
.get_onCapabilities().args
[0][1].resize
).to
.be
.true;
1619 expect(client
.get_capabilities().resize
).to
.be
.true;
1622 it('should handle a resize requested by this client', function () {
1623 var reason_for_change
= 1; // requested by this client
1624 var status_code
= 0; // No error
1626 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1627 width
: 20, height
: 50, encoding
: -308 }],
1628 make_screen_data(1), client
);
1630 expect(client
._fb_width
).to
.equal(20);
1631 expect(client
._fb_height
).to
.equal(50);
1633 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1634 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1636 var spy
= client
.get_onFBResize();
1637 expect(spy
).to
.have
.been
.calledOnce
;
1638 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1641 it('should handle a resize requested by another client', function () {
1642 var reason_for_change
= 2; // requested by another client
1643 var status_code
= 0; // No error
1645 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1646 width
: 20, height
: 50, encoding
: -308 }],
1647 make_screen_data(1), client
);
1649 expect(client
._fb_width
).to
.equal(20);
1650 expect(client
._fb_height
).to
.equal(50);
1652 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1653 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1655 var spy
= client
.get_onFBResize();
1656 expect(spy
).to
.have
.been
.calledOnce
;
1657 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1660 it('should be able to recieve requests which contain data for multiple screens', function () {
1661 var reason_for_change
= 2; // requested by another client
1662 var status_code
= 0; // No error
1664 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1665 width
: 60, height
: 50, encoding
: -308 }],
1666 make_screen_data(3), client
);
1668 expect(client
._fb_width
).to
.equal(60);
1669 expect(client
._fb_height
).to
.equal(50);
1671 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1672 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1674 var spy
= client
.get_onFBResize();
1675 expect(spy
).to
.have
.been
.calledOnce
;
1676 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 60, 50);
1679 it('should not handle a failed request', function () {
1680 var reason_for_change
= 1; // requested by this client
1681 var status_code
= 1; // Resize is administratively prohibited
1683 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1684 width
: 20, height
: 50, encoding
: -308 }],
1685 make_screen_data(1), client
);
1687 expect(client
._fb_width
).to
.equal(4);
1688 expect(client
._fb_height
).to
.equal(4);
1690 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1692 var spy
= client
.get_onFBResize();
1693 expect(spy
).to
.not
.have
.been
.called
;
1697 it
.skip('should handle the Cursor pseudo-encoding', function () {
1698 // TODO(directxman12): test
1701 it('should handle the last_rect pseudo-encoding', function () {
1702 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1703 expect(client
._FBU
.rects
).to
.equal(0);
1708 describe('XVP Message Handling', function () {
1709 it('should send a notification on XVP_FAIL', function () {
1710 client
.set_onNotification(sinon
.spy());
1711 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 0]));
1712 var spy
= client
.get_onNotification();
1713 expect(spy
).to
.have
.been
.calledOnce
;
1714 expect(spy
.args
[0][1]).to
.equal('XVP Operation Failed');
1717 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1718 client
.set_onCapabilities(sinon
.spy());
1719 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1720 expect(client
._rfb_xvp_ver
).to
.equal(10);
1721 expect(client
.get_onCapabilities()).to
.have
.been
.calledOnce
;
1722 expect(client
.get_onCapabilities().args
[0][1].power
).to
.be
.true;
1723 expect(client
.get_capabilities().power
).to
.be
.true;
1726 it('should fail on unknown XVP message types', function () {
1727 sinon
.spy(client
, "_fail");
1728 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1729 expect(client
._fail
).to
.have
.been
.calledOnce
;
1733 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1734 var expected_str
= 'cheese!';
1735 var data
= [3, 0, 0, 0];
1736 push32(data
, expected_str
.length
);
1737 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1738 client
.set_onClipboard(sinon
.spy());
1740 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1741 var spy
= client
.get_onClipboard();
1742 expect(spy
).to
.have
.been
.calledOnce
;
1743 expect(spy
.args
[0][1]).to
.equal(expected_str
);
1746 it('should fire the bell callback on Bell', function () {
1747 client
.set_onBell(sinon
.spy());
1748 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1749 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1752 it('should respond correctly to ServerFence', function () {
1753 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1754 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1756 var payload
= "foo\x00ab9";
1758 // ClientFence and ServerFence are identical in structure
1759 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1760 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1762 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1764 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1766 expected_msg
._sQlen
= 0;
1767 incoming_msg
._sQlen
= 0;
1769 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1770 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1772 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1774 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1777 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1778 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1780 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1782 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1784 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1786 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1787 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1790 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1791 client
._enabledContinuousUpdates
= true;
1792 client
._supportsContinuousUpdates
= true;
1794 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1796 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1799 it('should update continuous updates on resize', function () {
1800 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1801 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1803 client
._resize(450, 160);
1805 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1807 client
._enabledContinuousUpdates
= true;
1809 client
._resize(90, 700);
1811 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1814 it('should fail on an unknown message type', function () {
1815 sinon
.spy(client
, "_fail");
1816 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1817 expect(client
._fail
).to
.have
.been
.calledOnce
;
1821 describe('Asynchronous Events', function () {
1822 describe('Mouse event handlers', function () {
1824 beforeEach(function () {
1825 client
= make_rfb();
1826 client
._sock
= new Websock();
1827 client
._sock
.open('ws://', 'binary');
1828 client
._sock
._websocket
._open();
1829 sinon
.spy(client
._sock
, 'flush');
1830 client
._rfb_connection_state
= 'connected';
1833 it('should not send button messages in view-only mode', function () {
1834 client
._view_only
= true;
1835 client
._mouse
._onMouseButton(0, 0, 1, 0x001);
1836 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1839 it('should not send movement messages in view-only mode', function () {
1840 client
._view_only
= true;
1841 client
._mouse
._onMouseMove(0, 0);
1842 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1845 it('should send a pointer event on mouse button presses', function () {
1846 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1847 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1848 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1849 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1852 it('should send a mask of 1 on mousedown', function () {
1853 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1854 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1855 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1856 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1859 it('should send a mask of 0 on mouseup', function () {
1860 client
._mouse_buttonMask
= 0x001;
1861 client
._mouse
._onMouseButton(10, 12, 0, 0x001);
1862 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1863 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1864 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1867 it('should send a pointer event on mouse movement', function () {
1868 client
._mouse
._onMouseMove(10, 12);
1869 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1870 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1871 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1874 it('should set the button mask so that future mouse movements use it', function () {
1875 client
._mouse
._onMouseButton(10, 12, 1, 0x010);
1876 client
._mouse
._onMouseMove(13, 9);
1877 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1878 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1879 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1880 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1883 // NB(directxman12): we don't need to test not sending messages in
1884 // non-normal modes, since we haven't grabbed input
1885 // yet (grabbing input should be checked in the lifecycle tests).
1887 it('should not send movement messages when viewport dragging', function () {
1888 client
._viewportDragging
= true;
1889 client
._display
.viewportChangePos
= sinon
.spy();
1890 client
._mouse
._onMouseMove(13, 9);
1891 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1894 it('should not send button messages when initiating viewport dragging', function () {
1895 client
._viewportDrag
= true;
1896 client
._mouse
._onMouseButton(13, 9, 0x001);
1897 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1900 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1901 client
._viewportDrag
= true;
1902 client
._mouse
._onMouseButton(13, 9, 0x001);
1903 expect(client
._viewportDragging
).to
.be
.true;
1904 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1907 it('should terminate viewport dragging on a button up event, if enabled', function () {
1908 client
._viewportDrag
= true;
1909 client
._viewportDragging
= true;
1910 client
._mouse
._onMouseButton(13, 9, 0x000);
1911 expect(client
._viewportDragging
).to
.be
.false;
1914 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1917 var newX
= 123 + 11 * window
.devicePixelRatio
;
1918 var newY
= 109 + 4 * window
.devicePixelRatio
;
1920 client
._viewportDrag
= true;
1921 client
._viewportDragging
= true;
1922 client
._viewportHasMoved
= false;
1923 client
._viewportDragPos
= { x
: oldX
, y
: oldY
};
1924 client
._display
.viewportChangePos
= sinon
.spy();
1926 client
._mouse
._onMouseMove(newX
, newY
);
1928 expect(client
._viewportDragging
).to
.be
.true;
1929 expect(client
._viewportHasMoved
).to
.be
.true;
1930 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: newX
, y
: newY
});
1931 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
1932 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(oldX
- newX
, oldY
- newY
);
1936 describe('Keyboard Event Handlers', function () {
1938 beforeEach(function () {
1939 client
= make_rfb();
1940 client
._sock
= new Websock();
1941 client
._sock
.open('ws://', 'binary');
1942 client
._sock
._websocket
._open();
1943 sinon
.spy(client
._sock
, 'flush');
1944 client
._rfb_connection_state
= 'connected';
1945 client
._view_only
= false;
1948 it('should send a key message on a key press', function () {
1950 client
._keyboard
._onKeyEvent(0x41, 'KeyA', true);
1951 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
1952 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
1953 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
1956 it('should not send messages in view-only mode', function () {
1957 client
._view_only
= true;
1958 client
._keyboard
._onKeyEvent('a', 'KeyA', true);
1959 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1963 describe('WebSocket event handlers', function () {
1965 beforeEach(function () {
1966 client
= make_rfb();
1967 client
.connect('wss://host:8675');
1968 this.clock
= sinon
.useFakeTimers();
1971 afterEach(function () { this.clock
.restore(); });
1974 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
1975 client
._rfb_connection_state
= 'connected';
1976 client
._normal_msg
= sinon
.spy();
1977 client
._sock
._websocket
._receive_data(new Uint8Array([]));
1978 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
1981 it('should handle a message in the connected state as a normal message', function () {
1982 client
._rfb_connection_state
= 'connected';
1983 client
._normal_msg
= sinon
.spy();
1984 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1985 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
1988 it('should handle a message in any non-disconnected/failed state like an init message', function () {
1989 client
._rfb_init_state
= 'ProtocolVersion';
1990 client
._init_msg
= sinon
.spy();
1991 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1992 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
1995 it('should process all normal messages directly', function () {
1996 client
._sock
._websocket
._open();
1997 client
._rfb_connection_state
= 'connected';
1998 client
.set_onBell(sinon
.spy());
1999 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2000 expect(client
.get_onBell()).to
.have
.been
.calledTwice
;
2004 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2005 client
._sock
._websocket
._open();
2006 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2009 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2010 sinon
.spy(client
, "_fail");
2011 client
._rfb_connection_state
= 'some_other_state';
2012 client
._sock
._websocket
._open();
2013 expect(client
._fail
).to
.have
.been
.calledOnce
;
2017 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2018 client
._rfb_connection_state
= 'disconnecting';
2019 client
._sock
._websocket
.close();
2020 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2023 it('should fail if we get a close event while connecting', function () {
2024 sinon
.spy(client
, "_fail");
2025 client
._rfb_connection_state
= 'connecting';
2026 client
._sock
._websocket
.close();
2027 expect(client
._fail
).to
.have
.been
.calledOnce
;
2030 it('should fail if we get a close event while disconnected', function () {
2031 sinon
.spy(client
, "_fail");
2032 client
._rfb_connection_state
= 'disconnected';
2033 client
._sock
._websocket
.close();
2034 expect(client
._fail
).to
.have
.been
.calledOnce
;
2037 it('should unregister close event handler', function () {
2038 sinon
.spy(client
._sock
, 'off');
2039 client
._rfb_connection_state
= 'disconnecting';
2040 client
._sock
._websocket
.close();
2041 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2044 // error events do nothing