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 var push8 = function (arr
, num
) {
17 var push16 = function (arr
, num
) {
19 arr
.push((num
>> 8) & 0xFF,
23 var push32 = function (arr
, num
) {
25 arr
.push((num
>> 24) & 0xFF,
31 describe('Remote Frame Buffer Protocol Client', function() {
34 before(FakeWebSocket
.replace
);
35 after(FakeWebSocket
.restore
);
38 this.clock
= clock
= sinon
.useFakeTimers();
39 // Use a single set of buffers instead of reallocating to
41 var sock
= new Websock();
42 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
43 var rQ
= new Uint8Array(sock
._rQbufferSize
);
45 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
46 Websock
.prototype._allocate_buffers = function () {
54 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
60 beforeEach(function () {
61 // Track all created RFB objects
64 afterEach(function () {
65 // Make sure every created RFB object is properly cleaned up
66 // or they might affect subsequent tests
67 rfbs
.forEach(function (rfb
) {
69 expect(rfb
._disconnect
).to
.have
.been
.called
;
74 function make_rfb (url
, options
) {
75 url
= url
|| 'wss://host:8675';
76 var rfb
= new RFB(document
.createElement('canvas'), url
, options
);
78 rfb
._sock
._websocket
._open();
79 rfb
._rfb_connection_state
= 'connected';
80 sinon
.spy(rfb
, "_disconnect");
85 describe('Connecting/Disconnecting', function () {
86 describe('#RFB', function () {
87 it('should set the current state to "connecting"', function () {
88 var client
= new RFB(document
.createElement('canvas'), 'wss://host:8675');
89 client
._rfb_connection_state
= '';
91 expect(client
._rfb_connection_state
).to
.equal('connecting');
94 it('should actually connect to the websocket', function () {
95 var client
= new RFB(document
.createElement('canvas'), 'ws://HOST:8675/PATH');
96 sinon
.spy(client
._sock
, 'open');
98 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
99 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
103 describe('#disconnect', function () {
105 beforeEach(function () {
109 it('should go to state "disconnecting" before "disconnected"', function () {
110 sinon
.spy(client
, '_updateConnectionState');
112 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
113 expect(client
._updateConnectionState
.getCall(0).args
[0])
114 .to
.equal('disconnecting');
115 expect(client
._updateConnectionState
.getCall(1).args
[0])
116 .to
.equal('disconnected');
117 expect(client
._rfb_connection_state
).to
.equal('disconnected');
120 it('should unregister error event handler', function () {
121 sinon
.spy(client
._sock
, 'off');
123 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
126 it('should unregister message event handler', function () {
127 sinon
.spy(client
._sock
, 'off');
129 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
132 it('should unregister open event handler', function () {
133 sinon
.spy(client
._sock
, 'off');
135 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
139 describe('#sendCredentials', function () {
141 beforeEach(function () {
143 client
._rfb_connection_state
= 'connecting';
146 it('should set the rfb credentials properly"', function () {
147 client
.sendCredentials({ password
: 'pass' });
148 expect(client
._rfb_credentials
).to
.deep
.equal({ password
: 'pass' });
151 it('should call init_msg "soon"', function () {
152 client
._init_msg
= sinon
.spy();
153 client
.sendCredentials({ password
: 'pass' });
155 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
160 describe('Public API Basic Behavior', function () {
162 beforeEach(function () {
166 describe('#sendCtrlAlDel', function () {
167 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
168 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
169 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
170 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
171 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
172 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
173 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
174 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
176 client
.sendCtrlAltDel();
177 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
180 it('should not send the keys if we are not in a normal state', function () {
181 sinon
.spy(client
._sock
, 'flush');
182 client
._rfb_connection_state
= "connecting";
183 client
.sendCtrlAltDel();
184 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
187 it('should not send the keys if we are set as view_only', function () {
188 sinon
.spy(client
._sock
, 'flush');
189 client
._viewOnly
= true;
190 client
.sendCtrlAltDel();
191 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
195 describe('#sendKey', function () {
196 it('should send a single key with the given code and state (down = true)', function () {
197 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
198 RFB
.messages
.keyEvent(expected
, 123, 1);
199 client
.sendKey(123, 'Key123', true);
200 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
203 it('should send both a down and up event if the state is not specified', function () {
204 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
205 RFB
.messages
.keyEvent(expected
, 123, 1);
206 RFB
.messages
.keyEvent(expected
, 123, 0);
207 client
.sendKey(123, 'Key123');
208 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
211 it('should not send the key if we are not in a normal state', function () {
212 sinon
.spy(client
._sock
, 'flush');
213 client
._rfb_connection_state
= "connecting";
214 client
.sendKey(123, 'Key123');
215 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
218 it('should not send the key if we are set as view_only', function () {
219 sinon
.spy(client
._sock
, 'flush');
220 client
._viewOnly
= true;
221 client
.sendKey(123, 'Key123');
222 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
225 it('should send QEMU extended events if supported', function () {
226 client
._qemuExtKeyEventSupported
= true;
227 var expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
228 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
229 client
.sendKey(0x20, 'Space', true);
230 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
233 it('should not send QEMU extended events if unknown key code', function () {
234 client
._qemuExtKeyEventSupported
= true;
235 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
236 RFB
.messages
.keyEvent(expected
, 123, 1);
237 client
.sendKey(123, 'FooBar', true);
238 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
242 describe('#clipboardPasteFrom', function () {
243 it('should send the given text in a paste event', function () {
244 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
245 RFB
.messages
.clientCutText(expected
, 'abc');
246 client
.clipboardPasteFrom('abc');
247 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
250 it('should not send the text if we are not in a normal state', function () {
251 sinon
.spy(client
._sock
, 'flush');
252 client
._rfb_connection_state
= "connecting";
253 client
.clipboardPasteFrom('abc');
254 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
258 describe("#requestDesktopSize", function () {
259 beforeEach(function() {
260 client
._supportsSetDesktopSize
= true;
263 it('should send the request with the given width and height', function () {
264 var expected
= [251];
265 push8(expected
, 0); // padding
266 push16(expected
, 1); // width
267 push16(expected
, 2); // height
268 push8(expected
, 1); // number-of-screens
269 push8(expected
, 0); // padding before screen array
270 push32(expected
, 0); // id
271 push16(expected
, 0); // x-position
272 push16(expected
, 0); // y-position
273 push16(expected
, 1); // width
274 push16(expected
, 2); // height
275 push32(expected
, 0); // flags
277 client
.requestDesktopSize(1, 2);
278 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
281 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
282 sinon
.spy(client
._sock
, 'flush');
283 client
._supportsSetDesktopSize
= false;
284 client
.requestDesktopSize(1,2);
285 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
288 it('should not send the request if we are not in a normal state', function () {
289 sinon
.spy(client
._sock
, 'flush');
290 client
._rfb_connection_state
= "connecting";
291 client
.requestDesktopSize(1,2);
292 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
296 describe("XVP operations", function () {
297 beforeEach(function () {
298 client
._rfb_xvp_ver
= 1;
301 it('should send the shutdown signal on #machineShutdown', function () {
302 client
.machineShutdown();
303 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
306 it('should send the reboot signal on #machineReboot', function () {
307 client
.machineReboot();
308 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
311 it('should send the reset signal on #machineReset', function () {
312 client
.machineReset();
313 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
316 it('should not send XVP operations with higher versions than we support', function () {
317 sinon
.spy(client
._sock
, 'flush');
319 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
324 describe('Misc Internals', function () {
325 describe('#_updateConnectionState', function () {
327 beforeEach(function () {
331 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
332 var spy
= sinon
.spy();
333 client
._disconnTimer
= setTimeout(spy
, 50);
334 client
._rfb_connection_state
= 'connecting';
335 client
._updateConnectionState('connected');
337 expect(spy
).to
.not
.have
.been
.called
;
338 expect(client
._disconnTimer
).to
.be
.null;
341 it('should set the rfb_connection_state', function () {
342 client
._rfb_connection_state
= 'connecting';
343 client
._updateConnectionState('connected');
344 expect(client
._rfb_connection_state
).to
.equal('connected');
347 it('should not change the state when we are disconnected', function () {
349 expect(client
._rfb_connection_state
).to
.equal('disconnected');
350 client
._updateConnectionState('connecting');
351 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
354 it('should ignore state changes to the same state', function () {
355 var connectSpy
= sinon
.spy();
356 client
.addEventListener("connect", connectSpy
);
358 expect(client
._rfb_connection_state
).to
.equal('connected');
359 client
._updateConnectionState('connected');
360 expect(connectSpy
).to
.not
.have
.been
.called
;
364 var disconnectSpy
= sinon
.spy();
365 client
.addEventListener("disconnect", disconnectSpy
);
367 expect(client
._rfb_connection_state
).to
.equal('disconnected');
368 client
._updateConnectionState('disconnected');
369 expect(disconnectSpy
).to
.not
.have
.been
.called
;
372 it('should ignore illegal state changes', function () {
373 var spy
= sinon
.spy();
374 client
.addEventListener("disconnect", spy
);
375 client
._updateConnectionState('disconnected');
376 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
377 expect(spy
).to
.not
.have
.been
.called
;
381 describe('#_fail', function () {
383 beforeEach(function () {
387 it('should close the WebSocket connection', function () {
388 sinon
.spy(client
._sock
, 'close');
390 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
393 it('should transition to disconnected', function () {
394 sinon
.spy(client
, '_updateConnectionState');
396 this.clock
.tick(2000);
397 expect(client
._updateConnectionState
).to
.have
.been
.called
;
398 expect(client
._rfb_connection_state
).to
.equal('disconnected');
401 it('should set clean_disconnect variable', function () {
402 client
._rfb_clean_disconnect
= true;
403 client
._rfb_connection_state
= 'connected';
405 expect(client
._rfb_clean_disconnect
).to
.be
.false;
408 it('should result in disconnect event with clean set to false', function () {
409 client
._rfb_connection_state
= 'connected';
410 var spy
= sinon
.spy();
411 client
.addEventListener("disconnect", spy
);
413 this.clock
.tick(2000);
414 expect(spy
).to
.have
.been
.calledOnce
;
415 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
421 describe('Connection States', function () {
422 describe('connecting', function () {
423 it('should open the websocket connection', function () {
424 var client
= new RFB(document
.createElement('canvas'),
425 'ws://HOST:8675/PATH');
426 sinon
.spy(client
._sock
, 'open');
428 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
432 describe('connected', function () {
434 beforeEach(function () {
438 it('should result in a connect event if state becomes connected', function () {
439 var spy
= sinon
.spy();
440 client
.addEventListener("connect", spy
);
441 client
._rfb_connection_state
= 'connecting';
442 client
._updateConnectionState('connected');
443 expect(spy
).to
.have
.been
.calledOnce
;
446 it('should not result in a connect event if the state is not "connected"', function () {
447 var spy
= sinon
.spy();
448 client
.addEventListener("connect", spy
);
449 client
._sock
._websocket
.open = function () {}; // explicitly don't call onopen
450 client
._updateConnectionState('connecting');
451 expect(spy
).to
.not
.have
.been
.called
;
455 describe('disconnecting', function () {
457 beforeEach(function () {
461 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
462 sinon
.spy(client
, '_updateConnectionState');
463 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
464 client
._updateConnectionState('disconnecting');
465 this.clock
.tick(3 * 1000);
466 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
467 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
468 expect(client
._rfb_connection_state
).to
.equal("disconnected");
471 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
472 client
._updateConnectionState('disconnecting');
473 this.clock
.tick(3 * 1000 / 2);
474 client
._sock
._websocket
.close();
475 this.clock
.tick(3 * 1000 / 2 + 1);
476 expect(client
._rfb_connection_state
).to
.equal('disconnected');
479 it('should close the WebSocket connection', function () {
480 sinon
.spy(client
._sock
, 'close');
481 client
._updateConnectionState('disconnecting');
482 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
485 it('should not result in a disconnect event', function () {
486 var spy
= sinon
.spy();
487 client
.addEventListener("disconnect", spy
);
488 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
489 client
._updateConnectionState('disconnecting');
490 expect(spy
).to
.not
.have
.been
.called
;
494 describe('disconnected', function () {
496 beforeEach(function () {
497 client
= new RFB(document
.createElement('canvas'), 'ws://HOST:8675/PATH');
500 it('should result in a disconnect event if state becomes "disconnected"', function () {
501 var spy
= sinon
.spy();
502 client
.addEventListener("disconnect", spy
);
503 client
._rfb_connection_state
= 'disconnecting';
504 client
._updateConnectionState('disconnected');
505 expect(spy
).to
.have
.been
.calledOnce
;
506 expect(spy
.args
[0][0].detail
.clean
).to
.be
.true;
509 it('should result in a disconnect event without msg when no reason given', function () {
510 var spy
= sinon
.spy();
511 client
.addEventListener("disconnect", spy
);
512 client
._rfb_connection_state
= 'disconnecting';
513 client
._rfb_disconnect_reason
= "";
514 client
._updateConnectionState('disconnected');
515 expect(spy
).to
.have
.been
.calledOnce
;
516 expect(spy
.args
[0].length
).to
.equal(1);
521 describe('Protocol Initialization States', function () {
523 beforeEach(function () {
525 client
._rfb_connection_state
= 'connecting';
528 describe('ProtocolVersion', function () {
529 function send_ver (ver
, client
) {
530 var arr
= new Uint8Array(12);
531 for (var i
= 0; i
< ver
.length
; i
++) {
532 arr
[i
+4] = ver
.charCodeAt(i
);
534 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
536 client
._sock
._websocket
._receive_data(arr
);
539 describe('version parsing', function () {
540 it('should interpret version 003.003 as version 3.3', function () {
541 send_ver('003.003', client
);
542 expect(client
._rfb_version
).to
.equal(3.3);
545 it('should interpret version 003.006 as version 3.3', function () {
546 send_ver('003.006', client
);
547 expect(client
._rfb_version
).to
.equal(3.3);
550 it('should interpret version 003.889 as version 3.3', function () {
551 send_ver('003.889', client
);
552 expect(client
._rfb_version
).to
.equal(3.3);
555 it('should interpret version 003.007 as version 3.7', function () {
556 send_ver('003.007', client
);
557 expect(client
._rfb_version
).to
.equal(3.7);
560 it('should interpret version 003.008 as version 3.8', function () {
561 send_ver('003.008', client
);
562 expect(client
._rfb_version
).to
.equal(3.8);
565 it('should interpret version 004.000 as version 3.8', function () {
566 send_ver('004.000', client
);
567 expect(client
._rfb_version
).to
.equal(3.8);
570 it('should interpret version 004.001 as version 3.8', function () {
571 send_ver('004.001', client
);
572 expect(client
._rfb_version
).to
.equal(3.8);
575 it('should interpret version 005.000 as version 3.8', function () {
576 send_ver('005.000', client
);
577 expect(client
._rfb_version
).to
.equal(3.8);
580 it('should fail on an invalid version', function () {
581 sinon
.spy(client
, "_fail");
582 send_ver('002.000', client
);
583 expect(client
._fail
).to
.have
.been
.calledOnce
;
587 it('should send back the interpreted version', function () {
588 send_ver('004.000', client
);
590 var expected_str
= 'RFB 003.008\n';
592 for (var i
= 0; i
< expected_str
.length
; i
++) {
593 expected
[i
] = expected_str
.charCodeAt(i
);
596 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
599 it('should transition to the Security state on successful negotiation', function () {
600 send_ver('003.008', client
);
601 expect(client
._rfb_init_state
).to
.equal('Security');
604 describe('Repeater', function () {
605 beforeEach(function () {
606 client
= make_rfb('wss://host:8675', { repeaterID
: "12345" });
607 client
._rfb_connection_state
= 'connecting';
610 it('should interpret version 000.000 as a repeater', function () {
611 send_ver('000.000', client
);
612 expect(client
._rfb_version
).to
.equal(0);
614 var sent_data
= client
._sock
._websocket
._get_sent_data();
615 expect(new Uint8Array(sent_data
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
616 expect(sent_data
).to
.have
.length(250);
619 it('should handle two step repeater negotiation', function () {
620 send_ver('000.000', client
);
621 send_ver('003.008', client
);
622 expect(client
._rfb_version
).to
.equal(3.8);
627 describe('Security', function () {
628 beforeEach(function () {
629 client
._rfb_init_state
= 'Security';
632 it('should simply receive the auth scheme when for versions < 3.7', function () {
633 client
._rfb_version
= 3.6;
634 var auth_scheme_raw
= [1, 2, 3, 4];
635 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
636 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
637 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
638 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
641 it('should prefer no authentication is possible', function () {
642 client
._rfb_version
= 3.7;
643 var auth_schemes
= [2, 1, 3];
644 client
._sock
._websocket
._receive_data(auth_schemes
);
645 expect(client
._rfb_auth_scheme
).to
.equal(1);
646 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
649 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
650 client
._rfb_version
= 3.7;
651 var auth_schemes
= [2, 22, 16];
652 client
._sock
._websocket
._receive_data(auth_schemes
);
653 expect(client
._rfb_auth_scheme
).to
.equal(22);
654 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
657 it('should fail if there are no supported schemes for versions >= 3.7', function () {
658 sinon
.spy(client
, "_fail");
659 client
._rfb_version
= 3.7;
660 var auth_schemes
= [1, 32];
661 client
._sock
._websocket
._receive_data(auth_schemes
);
662 expect(client
._fail
).to
.have
.been
.calledOnce
;
665 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
666 client
._rfb_version
= 3.7;
667 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
668 sinon
.spy(client
, '_fail');
669 client
._sock
._websocket
._receive_data(failure_data
);
671 expect(client
._fail
).to
.have
.been
.calledOnce
;
672 expect(client
._fail
).to
.have
.been
.calledWith(
673 'Security negotiation failed on no security types (reason: whoops)');
676 it('should transition to the Authentication state and continue on successful negotiation', function () {
677 client
._rfb_version
= 3.7;
678 var auth_schemes
= [1, 1];
679 client
._negotiate_authentication
= sinon
.spy();
680 client
._sock
._websocket
._receive_data(auth_schemes
);
681 expect(client
._rfb_init_state
).to
.equal('Authentication');
682 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
686 describe('Authentication', function () {
687 beforeEach(function () {
688 client
._rfb_init_state
= 'Security';
691 function send_security(type
, cl
) {
692 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
695 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
696 client
._rfb_version
= 3.6;
697 var err_msg
= "Whoopsies";
698 var data
= [0, 0, 0, 0];
699 var err_len
= err_msg
.length
;
700 push32(data
, err_len
);
701 for (var i
= 0; i
< err_len
; i
++) {
702 data
.push(err_msg
.charCodeAt(i
));
705 sinon
.spy(client
, '_fail');
706 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
707 expect(client
._fail
).to
.have
.been
.calledWith(
708 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
711 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
712 client
._rfb_version
= 3.8;
713 send_security(1, client
);
714 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
717 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
718 client
._rfb_version
= 3.7;
719 send_security(1, client
);
720 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
723 it('should fail on an unknown auth scheme', function () {
724 sinon
.spy(client
, "_fail");
725 client
._rfb_version
= 3.8;
726 send_security(57, client
);
727 expect(client
._fail
).to
.have
.been
.calledOnce
;
730 describe('VNC Authentication (type 2) Handler', function () {
731 beforeEach(function () {
732 client
._rfb_init_state
= 'Security';
733 client
._rfb_version
= 3.8;
736 it('should fire the credentialsrequired event if missing a password', function () {
737 var spy
= sinon
.spy();
738 client
.addEventListener("credentialsrequired", spy
);
739 send_security(2, client
);
742 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
743 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
745 expect(client
._rfb_credentials
).to
.be
.empty
;
746 expect(spy
).to
.have
.been
.calledOnce
;
747 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
750 it('should encrypt the password with DES and then send it back', function () {
751 client
._rfb_credentials
= { password
: 'passwd' };
752 send_security(2, client
);
753 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
756 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
757 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
759 var des_pass
= RFB
.genDES('passwd', challenge
);
760 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
763 it('should transition to SecurityResult immediately after sending the password', function () {
764 client
._rfb_credentials
= { password
: 'passwd' };
765 send_security(2, client
);
768 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
769 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
771 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
775 describe('XVP Authentication (type 22) Handler', function () {
776 beforeEach(function () {
777 client
._rfb_init_state
= 'Security';
778 client
._rfb_version
= 3.8;
781 it('should fall through to standard VNC authentication upon completion', function () {
782 client
._rfb_credentials
= { username
: 'user',
784 password
: 'password' };
785 client
._negotiate_std_vnc_auth
= sinon
.spy();
786 send_security(22, client
);
787 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
790 it('should fire the credentialsrequired event if all credentials are missing', function() {
791 var spy
= sinon
.spy();
792 client
.addEventListener("credentialsrequired", spy
);
793 client
._rfb_credentials
= {};
794 send_security(22, client
);
796 expect(client
._rfb_credentials
).to
.be
.empty
;
797 expect(spy
).to
.have
.been
.calledOnce
;
798 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
801 it('should fire the credentialsrequired event if some credentials are missing', function() {
802 var spy
= sinon
.spy();
803 client
.addEventListener("credentialsrequired", spy
);
804 client
._rfb_credentials
= { username
: 'user',
806 send_security(22, client
);
808 expect(spy
).to
.have
.been
.calledOnce
;
809 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
812 it('should send user and target separately', function () {
813 client
._rfb_credentials
= { username
: 'user',
815 password
: 'password' };
816 client
._negotiate_std_vnc_auth
= sinon
.spy();
818 send_security(22, client
);
820 var expected
= [22, 4, 6]; // auth selection, len user, len target
821 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
823 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
827 describe('TightVNC Authentication (type 16) Handler', function () {
828 beforeEach(function () {
829 client
._rfb_init_state
= 'Security';
830 client
._rfb_version
= 3.8;
831 send_security(16, client
);
832 client
._sock
._websocket
._get_sent_data(); // skip the security reply
835 function send_num_str_pairs(pairs
, client
) {
836 var pairs_len
= pairs
.length
;
838 push32(data
, pairs_len
);
840 for (var i
= 0; i
< pairs_len
; i
++) {
841 push32(data
, pairs
[i
][0]);
843 for (j
= 0; j
< 4; j
++) {
844 data
.push(pairs
[i
][1].charCodeAt(j
));
846 for (j
= 0; j
< 8; j
++) {
847 data
.push(pairs
[i
][2].charCodeAt(j
));
851 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
854 it('should skip tunnel negotiation if no tunnels are requested', function () {
855 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
856 expect(client
._rfb_tightvnc
).to
.be
.true;
859 it('should fail if no supported tunnels are listed', function () {
860 sinon
.spy(client
, "_fail");
861 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
862 expect(client
._fail
).to
.have
.been
.calledOnce
;
865 it('should choose the notunnel tunnel type', function () {
866 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
867 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
870 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
871 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
872 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
873 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
874 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
875 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
878 /*it('should attempt to use VNC auth over no auth when possible', function () {
879 client._rfb_tightvnc = true;
880 client._negotiate_std_vnc_auth = sinon.spy();
881 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
882 expect(client._sock).to.have.sent([0, 0, 0, 1]);
883 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
884 expect(client._rfb_auth_scheme).to.equal(2);
885 });*/ // while this would make sense, the original code doesn't actually do this
887 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
888 client
._rfb_tightvnc
= true;
889 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
890 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
891 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
894 it('should accept VNC authentication and transition to that', function () {
895 client
._rfb_tightvnc
= true;
896 client
._negotiate_std_vnc_auth
= sinon
.spy();
897 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
898 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
899 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
900 expect(client
._rfb_auth_scheme
).to
.equal(2);
903 it('should fail if there are no supported auth types', function () {
904 sinon
.spy(client
, "_fail");
905 client
._rfb_tightvnc
= true;
906 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
907 expect(client
._fail
).to
.have
.been
.calledOnce
;
912 describe('SecurityResult', function () {
913 beforeEach(function () {
914 client
._rfb_init_state
= 'SecurityResult';
917 it('should fall through to ServerInitialisation on a response code of 0', function () {
918 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
919 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
922 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
923 client
._rfb_version
= 3.8;
924 sinon
.spy(client
, '_fail');
925 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
926 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
927 expect(client
._fail
).to
.have
.been
.calledWith(
928 'Security negotiation failed on security result (reason: whoops)');
931 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
932 sinon
.spy(client
, '_fail');
933 client
._rfb_version
= 3.7;
934 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
935 expect(client
._fail
).to
.have
.been
.calledWith(
936 'Security handshake failed');
939 it('should result in securityfailure event when receiving a non zero status', function () {
940 var spy
= sinon
.spy();
941 client
.addEventListener("securityfailure", spy
);
942 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
943 expect(spy
).to
.have
.been
.calledOnce
;
944 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
947 it('should include reason when provided in securityfailure event', function () {
948 client
._rfb_version
= 3.8;
949 var spy
= sinon
.spy();
950 client
.addEventListener("securityfailure", spy
);
951 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
952 32, 102, 97, 105, 108, 117, 114, 101];
953 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
954 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
955 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
958 it('should not include reason when length is zero in securityfailure event', function () {
959 client
._rfb_version
= 3.9;
960 var spy
= sinon
.spy();
961 client
.addEventListener("securityfailure", spy
);
962 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 0];
963 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
964 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
965 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
968 it('should not include reason in securityfailure event for version < 3.8', function () {
969 client
._rfb_version
= 3.6;
970 var spy
= sinon
.spy();
971 client
.addEventListener("securityfailure", spy
);
972 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
973 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
974 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
978 describe('ClientInitialisation', function () {
979 it('should transition to the ServerInitialisation state', function () {
980 var client
= make_rfb();
981 client
._rfb_connection_state
= 'connecting';
982 client
._rfb_init_state
= 'SecurityResult';
983 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
984 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
987 it('should send 1 if we are in shared mode', function () {
988 var client
= make_rfb('wss://host:8675', { shared
: true });
989 client
._rfb_connection_state
= 'connecting';
990 client
._rfb_init_state
= 'SecurityResult';
991 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
992 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
995 it('should send 0 if we are not in shared mode', function () {
996 var client
= make_rfb('wss://host:8675', { shared
: false });
997 client
._rfb_connection_state
= 'connecting';
998 client
._rfb_init_state
= 'SecurityResult';
999 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1000 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1004 describe('ServerInitialisation', function () {
1005 beforeEach(function () {
1006 client
._rfb_init_state
= 'ServerInitialisation';
1009 function send_server_init(opts
, client
) {
1010 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1011 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1012 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1013 for (var opt
in opts
) {
1014 full_opts
[opt
] = opts
[opt
];
1018 push16(data
, full_opts
.width
);
1019 push16(data
, full_opts
.height
);
1021 data
.push(full_opts
.bpp
);
1022 data
.push(full_opts
.depth
);
1023 data
.push(full_opts
.big_endian
);
1024 data
.push(full_opts
.true_color
);
1026 push16(data
, full_opts
.red_max
);
1027 push16(data
, full_opts
.green_max
);
1028 push16(data
, full_opts
.blue_max
);
1029 push8(data
, full_opts
.red_shift
);
1030 push8(data
, full_opts
.green_shift
);
1031 push8(data
, full_opts
.blue_shift
);
1038 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1041 push32(name_data
, full_opts
.name
.length
);
1042 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1043 name_data
.push(full_opts
.name
.charCodeAt(i
));
1045 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1048 it('should set the framebuffer width and height', function () {
1049 send_server_init({ width
: 32, height
: 84 }, client
);
1050 expect(client
._fb_width
).to
.equal(32);
1051 expect(client
._fb_height
).to
.equal(84);
1054 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1056 it('should set the framebuffer name and call the callback', function () {
1057 var spy
= sinon
.spy();
1058 client
.addEventListener("desktopname", spy
);
1059 send_server_init({ name
: 'some name' }, client
);
1061 expect(client
._fb_name
).to
.equal('some name');
1062 expect(spy
).to
.have
.been
.calledOnce
;
1063 expect(spy
.args
[0][0].detail
.name
).to
.equal('some name');
1066 it('should handle the extended init message of the tight encoding', function () {
1067 // NB(sross): we don't actually do anything with it, so just test that we can
1068 // read it w/o throwing an error
1069 client
._rfb_tightvnc
= true;
1070 send_server_init({}, client
);
1072 var tight_data
= [];
1073 push16(tight_data
, 1);
1074 push16(tight_data
, 2);
1075 push16(tight_data
, 3);
1076 push16(tight_data
, 0);
1077 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1080 client
._sock
._websocket
._receive_data(tight_data
);
1082 expect(client
._rfb_connection_state
).to
.equal('connected');
1085 it('should call the resize callback and resize the display', function () {
1086 var spy
= sinon
.spy();
1087 client
.addEventListener("fbresize", spy
);
1088 sinon
.spy(client
._display
, 'resize');
1089 send_server_init({ width
: 27, height
: 32 }, client
);
1091 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1092 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1093 expect(spy
).to
.have
.been
.calledOnce
;
1094 expect(spy
.args
[0][0].detail
.width
).to
.equal(27);
1095 expect(spy
.args
[0][0].detail
.height
).to
.equal(32);
1098 it('should grab the mouse and keyboard', function () {
1099 sinon
.spy(client
._keyboard
, 'grab');
1100 sinon
.spy(client
._mouse
, 'grab');
1101 send_server_init({}, client
);
1102 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1103 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1106 describe('Initial Update Request', function () {
1107 beforeEach(function () {
1108 sinon
.spy(RFB
.messages
, "pixelFormat");
1109 sinon
.spy(RFB
.messages
, "clientEncodings");
1110 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1113 afterEach(function () {
1114 RFB
.messages
.pixelFormat
.restore();
1115 RFB
.messages
.clientEncodings
.restore();
1116 RFB
.messages
.fbUpdateRequest
.restore();
1119 // TODO(directxman12): test the various options in this configuration matrix
1120 it('should reply with the pixel format, client encodings, and initial update request', function () {
1121 send_server_init({ width
: 27, height
: 32 }, client
);
1123 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1124 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1125 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1126 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1127 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1128 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1129 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1130 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1133 it('should reply with restricted settings for Intel AMT servers', function () {
1134 send_server_init({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1136 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1137 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1138 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1139 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1140 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1141 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1142 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1143 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1144 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1148 it('should transition to the "connected" state', function () {
1149 send_server_init({}, client
);
1150 expect(client
._rfb_connection_state
).to
.equal('connected');
1155 describe('Protocol Message Processing After Completing Initialization', function () {
1158 beforeEach(function () {
1159 client
= make_rfb();
1160 client
._fb_name
= 'some device';
1161 client
._fb_width
= 640;
1162 client
._fb_height
= 20;
1165 describe('Framebuffer Update Handling', function () {
1166 var target_data_arr
= [
1167 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1168 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1169 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1170 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1174 var target_data_check_arr
= [
1175 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1176 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1177 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1178 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1180 var target_data_check
;
1182 before(function () {
1183 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1184 target_data
= new Uint8Array(target_data_arr
);
1185 target_data_check
= new Uint8Array(target_data_check_arr
);
1188 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1191 if (!rect_cnt
|| rect_cnt
> -1) {
1193 data
.push(0); // msg type
1194 data
.push(0); // padding
1195 push16(data
, rect_cnt
|| rect_data
.length
);
1198 for (var i
= 0; i
< rect_data
.length
; i
++) {
1200 push16(data
, rect_info
[i
].x
);
1201 push16(data
, rect_info
[i
].y
);
1202 push16(data
, rect_info
[i
].width
);
1203 push16(data
, rect_info
[i
].height
);
1204 push32(data
, rect_info
[i
].encoding
);
1206 data
= data
.concat(rect_data
[i
]);
1209 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1212 it('should send an update request if there is sufficient data', function () {
1213 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1214 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1216 client
._framebufferUpdate = function () { return true; };
1217 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1219 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1222 it('should not send an update request if we need more data', function () {
1223 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1224 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1227 it('should resume receiving an update if we previously did not have enough data', function () {
1228 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1229 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1231 // just enough to set FBU.rects
1232 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1233 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1235 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1236 // 247 should *not* be used as the message type here
1237 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1238 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1241 it('should not send a request in continuous updates mode', function () {
1242 client
._enabledContinuousUpdates
= true;
1243 client
._framebufferUpdate = function () { return true; };
1244 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1246 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1249 it('should fail on an unsupported encoding', function () {
1250 sinon
.spy(client
, "_fail");
1251 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1252 send_fbu_msg([rect_info
], [[]], client
);
1253 expect(client
._fail
).to
.have
.been
.calledOnce
;
1256 it('should be able to pause and resume receiving rects if not enought data', function () {
1257 // seed some initial data to copy
1258 client
._fb_width
= 4;
1259 client
._fb_height
= 4;
1260 client
._display
.resize(4, 4);
1261 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1263 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1264 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1265 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1266 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1267 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1268 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1269 expect(client
._display
).to
.have
.displayed(target_data_check
);
1272 describe('Message Encoding Handlers', function () {
1273 beforeEach(function () {
1274 // a really small frame
1275 client
._fb_width
= 4;
1276 client
._fb_height
= 4;
1277 client
._fb_depth
= 24;
1278 client
._display
.resize(4, 4);
1281 it('should handle the RAW encoding', function () {
1282 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1283 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1284 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1285 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1288 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1289 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1290 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1291 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1292 send_fbu_msg(info
, rects
, client
);
1293 expect(client
._display
).to
.have
.displayed(target_data
);
1296 it('should handle the RAW encoding in low colour mode', function () {
1297 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1298 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1299 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1300 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1302 [0x03, 0x03, 0x03, 0x03],
1303 [0x0c, 0x0c, 0x0c, 0x0c],
1304 [0x0c, 0x0c, 0x03, 0x03],
1305 [0x0c, 0x0c, 0x03, 0x03]];
1306 client
._fb_depth
= 8;
1307 send_fbu_msg(info
, rects
, client
);
1308 expect(client
._display
).to
.have
.displayed(target_data_check
);
1311 it('should handle the COPYRECT encoding', function () {
1312 // seed some initial data to copy
1313 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1315 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1316 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1317 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1318 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1319 send_fbu_msg(info
, rects
, client
);
1320 expect(client
._display
).to
.have
.displayed(target_data_check
);
1323 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1324 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1326 it('should handle the RRE encoding', function () {
1327 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1329 push32(rect
, 2); // 2 subrects
1330 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1331 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1335 push16(rect
, 0); // x: 0
1336 push16(rect
, 0); // y: 0
1337 push16(rect
, 2); // width: 2
1338 push16(rect
, 2); // height: 2
1339 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1343 push16(rect
, 2); // x: 2
1344 push16(rect
, 2); // y: 2
1345 push16(rect
, 2); // width: 2
1346 push16(rect
, 2); // height: 2
1348 send_fbu_msg(info
, [rect
], client
);
1349 expect(client
._display
).to
.have
.displayed(target_data_check
);
1352 describe('the HEXTILE encoding handler', function () {
1353 it('should handle a tile with fg, bg specified, normal subrects', function () {
1354 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1356 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1357 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1358 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1362 rect
.push(2); // 2 subrects
1363 rect
.push(0); // x: 0, y: 0
1364 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1365 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1366 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1367 send_fbu_msg(info
, [rect
], client
);
1368 expect(client
._display
).to
.have
.displayed(target_data_check
);
1371 it('should handle a raw tile', function () {
1372 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1374 rect
.push(0x01); // raw
1375 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1376 rect
.push(target_data
[i
+ 2]);
1377 rect
.push(target_data
[i
+ 1]);
1378 rect
.push(target_data
[i
]);
1379 rect
.push(target_data
[i
+ 3]);
1381 send_fbu_msg(info
, [rect
], client
);
1382 expect(client
._display
).to
.have
.displayed(target_data
);
1385 it('should handle a tile with only bg specified (solid bg)', function () {
1386 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1389 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1390 send_fbu_msg(info
, [rect
], client
);
1393 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1394 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1397 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1398 // set the width so we can have two tiles
1399 client
._fb_width
= 8;
1400 client
._display
.resize(8, 4);
1402 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1408 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1410 // send an empty frame
1413 send_fbu_msg(info
, [rect
], client
);
1417 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1418 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1419 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1422 it('should handle a tile with bg and coloured subrects', function () {
1423 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1425 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1426 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1427 rect
.push(2); // 2 subrects
1428 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1432 rect
.push(0); // x: 0, y: 0
1433 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1434 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1438 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1439 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1440 send_fbu_msg(info
, [rect
], client
);
1441 expect(client
._display
).to
.have
.displayed(target_data_check
);
1444 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1445 client
._fb_width
= 4;
1446 client
._fb_height
= 17;
1447 client
._display
.resize(4, 17);
1449 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1451 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1452 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1453 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1457 rect
.push(8); // 8 subrects
1459 for (i
= 0; i
< 4; i
++) {
1460 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1461 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1462 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1463 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1465 rect
.push(0x08); // anysubrects
1466 rect
.push(1); // 1 subrect
1467 rect
.push(0); // x: 0, y: 0
1468 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1469 send_fbu_msg(info
, [rect
], client
);
1472 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1473 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1474 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1477 it('should fail on an invalid subencoding', function () {
1478 sinon
.spy(client
,"_fail");
1479 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1480 var rects
= [[45]]; // an invalid subencoding
1481 send_fbu_msg(info
, rects
, client
);
1482 expect(client
._fail
).to
.have
.been
.calledOnce
;
1486 it
.skip('should handle the TIGHT encoding', function () {
1487 // TODO(directxman12): test this
1490 it
.skip('should handle the TIGHT_PNG encoding', function () {
1491 // TODO(directxman12): test this
1494 it('should handle the DesktopSize pseduo-encoding', function () {
1495 var spy
= sinon
.spy();
1496 client
.addEventListener("fbresize", spy
);
1497 sinon
.spy(client
._display
, 'resize');
1498 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1500 expect(spy
).to
.have
.been
.calledOnce
;
1501 expect(spy
.args
[0][0].detail
.width
).to
.equal(20);
1502 expect(spy
.args
[0][0].detail
.height
).to
.equal(50);
1504 expect(client
._fb_width
).to
.equal(20);
1505 expect(client
._fb_height
).to
.equal(50);
1507 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1508 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1511 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1514 beforeEach(function () {
1515 client
._supportsSetDesktopSize
= false;
1516 // a really small frame
1517 client
._fb_width
= 4;
1518 client
._fb_height
= 4;
1519 client
._display
.resize(4, 4);
1520 sinon
.spy(client
._display
, 'resize');
1521 resizeSpy
= sinon
.spy();
1522 client
.addEventListener("fbresize", resizeSpy
);
1525 function make_screen_data (nr_of_screens
) {
1527 push8(data
, nr_of_screens
); // number-of-screens
1528 push8(data
, 0); // padding
1529 push16(data
, 0); // padding
1530 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1531 push32(data
, 0); // id
1532 push16(data
, 0); // x-position
1533 push16(data
, 0); // y-position
1534 push16(data
, 20); // width
1535 push16(data
, 50); // height
1536 push32(data
, 0); // flags
1541 it('should call callback when resize is supported', function () {
1542 var spy
= sinon
.spy();
1543 client
.addEventListener("capabilities", spy
);
1545 expect(client
._supportsSetDesktopSize
).to
.be
.false;
1546 expect(client
.capabilities
.resize
).to
.be
.false;
1548 var reason_for_change
= 0; // server initiated
1549 var status_code
= 0; // No error
1551 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1552 width
: 4, height
: 4, encoding
: -308 }],
1553 make_screen_data(1), client
);
1555 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1556 expect(spy
).to
.have
.been
.calledOnce
;
1557 expect(spy
.args
[0][0].detail
.capabilities
.resize
).to
.be
.true;
1558 expect(client
.capabilities
.resize
).to
.be
.true;
1561 it('should handle a resize requested by this client', function () {
1562 var reason_for_change
= 1; // requested by this client
1563 var status_code
= 0; // No error
1565 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1566 width
: 20, height
: 50, encoding
: -308 }],
1567 make_screen_data(1), client
);
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);
1575 expect(resizeSpy
).to
.have
.been
.calledOnce
;
1576 expect(resizeSpy
.args
[0][0].detail
.width
).to
.equal(20);
1577 expect(resizeSpy
.args
[0][0].detail
.height
).to
.equal(50);
1580 it('should handle a resize requested by another client', function () {
1581 var reason_for_change
= 2; // requested by another client
1582 var status_code
= 0; // No error
1584 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1585 width
: 20, height
: 50, encoding
: -308 }],
1586 make_screen_data(1), client
);
1588 expect(client
._fb_width
).to
.equal(20);
1589 expect(client
._fb_height
).to
.equal(50);
1591 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1592 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1594 expect(resizeSpy
).to
.have
.been
.calledOnce
;
1595 expect(resizeSpy
.args
[0][0].detail
.width
).to
.equal(20);
1596 expect(resizeSpy
.args
[0][0].detail
.height
).to
.equal(50);
1599 it('should be able to recieve requests which contain data for multiple screens', function () {
1600 var reason_for_change
= 2; // requested by another client
1601 var status_code
= 0; // No error
1603 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1604 width
: 60, height
: 50, encoding
: -308 }],
1605 make_screen_data(3), client
);
1607 expect(client
._fb_width
).to
.equal(60);
1608 expect(client
._fb_height
).to
.equal(50);
1610 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1611 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1613 expect(resizeSpy
).to
.have
.been
.calledOnce
;
1614 expect(resizeSpy
.args
[0][0].detail
.width
).to
.equal(60);
1615 expect(resizeSpy
.args
[0][0].detail
.height
).to
.equal(50);
1618 it('should not handle a failed request', function () {
1619 var reason_for_change
= 1; // requested by this client
1620 var status_code
= 1; // Resize is administratively prohibited
1622 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1623 width
: 20, height
: 50, encoding
: -308 }],
1624 make_screen_data(1), client
);
1626 expect(client
._fb_width
).to
.equal(4);
1627 expect(client
._fb_height
).to
.equal(4);
1629 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1631 expect(resizeSpy
).to
.not
.have
.been
.called
;
1635 it
.skip('should handle the Cursor pseudo-encoding', function () {
1636 // TODO(directxman12): test
1639 it('should handle the last_rect pseudo-encoding', function () {
1640 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1641 expect(client
._FBU
.rects
).to
.equal(0);
1646 describe('XVP Message Handling', function () {
1647 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1648 var spy
= sinon
.spy();
1649 client
.addEventListener("capabilities", spy
);
1650 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1651 expect(client
._rfb_xvp_ver
).to
.equal(10);
1652 expect(spy
).to
.have
.been
.calledOnce
;
1653 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
1654 expect(client
.capabilities
.power
).to
.be
.true;
1657 it('should fail on unknown XVP message types', function () {
1658 sinon
.spy(client
, "_fail");
1659 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1660 expect(client
._fail
).to
.have
.been
.calledOnce
;
1664 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1665 var expected_str
= 'cheese!';
1666 var data
= [3, 0, 0, 0];
1667 push32(data
, expected_str
.length
);
1668 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1669 var spy
= sinon
.spy();
1670 client
.addEventListener("clipboard", spy
);
1672 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1673 expect(spy
).to
.have
.been
.calledOnce
;
1674 expect(spy
.args
[0][0].detail
.text
).to
.equal(expected_str
);
1677 it('should fire the bell callback on Bell', function () {
1678 var spy
= sinon
.spy();
1679 client
.addEventListener("bell", spy
);
1680 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1681 expect(spy
).to
.have
.been
.calledOnce
;
1684 it('should respond correctly to ServerFence', function () {
1685 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1686 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1688 var payload
= "foo\x00ab9";
1690 // ClientFence and ServerFence are identical in structure
1691 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1692 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1694 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1696 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1698 expected_msg
._sQlen
= 0;
1699 incoming_msg
._sQlen
= 0;
1701 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1702 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1704 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1706 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1709 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1710 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1712 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1714 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1716 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1718 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1719 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1722 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1723 client
._enabledContinuousUpdates
= true;
1724 client
._supportsContinuousUpdates
= true;
1726 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1728 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1731 it('should update continuous updates on resize', function () {
1732 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1733 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1735 client
._resize(450, 160);
1737 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1739 client
._enabledContinuousUpdates
= true;
1741 client
._resize(90, 700);
1743 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1746 it('should fail on an unknown message type', function () {
1747 sinon
.spy(client
, "_fail");
1748 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1749 expect(client
._fail
).to
.have
.been
.calledOnce
;
1753 describe('Asynchronous Events', function () {
1755 beforeEach(function () {
1756 client
= make_rfb();
1759 describe('Mouse event handlers', function () {
1760 it('should not send button messages in view-only mode', function () {
1761 client
._viewOnly
= true;
1762 sinon
.spy(client
._sock
, 'flush');
1763 client
._handleMouseButton(0, 0, 1, 0x001);
1764 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1767 it('should not send movement messages in view-only mode', function () {
1768 client
._viewOnly
= true;
1769 sinon
.spy(client
._sock
, 'flush');
1770 client
._handleMouseMove(0, 0);
1771 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1774 it('should send a pointer event on mouse button presses', function () {
1775 client
._handleMouseButton(10, 12, 1, 0x001);
1776 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1777 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1778 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1781 it('should send a mask of 1 on mousedown', function () {
1782 client
._handleMouseButton(10, 12, 1, 0x001);
1783 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1784 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1785 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1788 it('should send a mask of 0 on mouseup', function () {
1789 client
._mouse_buttonMask
= 0x001;
1790 client
._handleMouseButton(10, 12, 0, 0x001);
1791 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1792 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1793 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1796 it('should send a pointer event on mouse movement', function () {
1797 client
._handleMouseMove(10, 12);
1798 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1799 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1800 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1803 it('should set the button mask so that future mouse movements use it', function () {
1804 client
._handleMouseButton(10, 12, 1, 0x010);
1805 client
._handleMouseMove(13, 9);
1806 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1807 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1808 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1809 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1812 // NB(directxman12): we don't need to test not sending messages in
1813 // non-normal modes, since we haven't grabbed input
1814 // yet (grabbing input should be checked in the lifecycle tests).
1816 it('should not send movement messages when viewport dragging', function () {
1817 client
._viewportDragging
= true;
1818 client
._display
.viewportChangePos
= sinon
.spy();
1819 sinon
.spy(client
._sock
, 'flush');
1820 client
._handleMouseMove(13, 9);
1821 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1824 it('should not send button messages when initiating viewport dragging', function () {
1825 client
.dragViewport
= true;
1826 sinon
.spy(client
._sock
, 'flush');
1827 client
._handleMouseButton(13, 9, 0x001);
1828 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1831 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1832 client
.dragViewport
= true;
1833 client
._handleMouseButton(13, 9, 0x001);
1834 expect(client
._viewportDragging
).to
.be
.true;
1835 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1838 it('should terminate viewport dragging on a button up event, if enabled', function () {
1839 client
.dragViewport
= true;
1840 client
._viewportDragging
= true;
1841 client
._handleMouseButton(13, 9, 0x000);
1842 expect(client
._viewportDragging
).to
.be
.false;
1845 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1848 var newX
= 123 + 11 * window
.devicePixelRatio
;
1849 var newY
= 109 + 4 * window
.devicePixelRatio
;
1851 client
.dragViewport
= true;
1852 client
._viewportDragging
= true;
1853 client
._viewportHasMoved
= false;
1854 client
._viewportDragPos
= { x
: oldX
, y
: oldY
};
1855 client
._display
.viewportChangePos
= sinon
.spy();
1857 client
._handleMouseMove(newX
, newY
);
1859 expect(client
._viewportDragging
).to
.be
.true;
1860 expect(client
._viewportHasMoved
).to
.be
.true;
1861 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: newX
, y
: newY
});
1862 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
1863 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(oldX
- newX
, oldY
- newY
);
1867 describe('Keyboard Event Handlers', function () {
1868 it('should send a key message on a key press', function () {
1870 client
._handleKeyEvent(0x41, 'KeyA', true);
1871 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
1872 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
1873 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
1876 it('should not send messages in view-only mode', function () {
1877 client
._viewOnly
= true;
1878 sinon
.spy(client
._sock
, 'flush');
1879 client
._handleKeyEvent('a', 'KeyA', true);
1880 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1884 describe('WebSocket event handlers', function () {
1886 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
1887 client
._normal_msg
= sinon
.spy();
1888 client
._sock
._websocket
._receive_data(new Uint8Array([]));
1889 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
1892 it('should handle a message in the connected state as a normal message', function () {
1893 client
._normal_msg
= sinon
.spy();
1894 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1895 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
1898 it('should handle a message in any non-disconnected/failed state like an init message', function () {
1899 client
._rfb_connection_state
= 'connecting';
1900 client
._rfb_init_state
= 'ProtocolVersion';
1901 client
._init_msg
= sinon
.spy();
1902 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1903 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
1906 it('should process all normal messages directly', function () {
1907 var spy
= sinon
.spy();
1908 client
.addEventListener("bell", spy
);
1909 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
1910 expect(spy
).to
.have
.been
.calledTwice
;
1914 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
1915 client
= new RFB(document
.createElement('canvas'), 'wss://host:8675');
1917 client
._sock
._websocket
._open();
1918 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
1921 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
1922 sinon
.spy(client
, "_fail");
1923 client
._rfb_connection_state
= 'connected';
1924 client
._sock
._websocket
._open();
1925 expect(client
._fail
).to
.have
.been
.calledOnce
;
1929 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
1930 var real
= client
._sock
._websocket
.close
;
1931 client
._sock
._websocket
.close = function () {};
1932 client
.disconnect();
1933 expect(client
._rfb_connection_state
).to
.equal('disconnecting');
1934 client
._sock
._websocket
.close
= real
;
1935 client
._sock
._websocket
.close();
1936 expect(client
._rfb_connection_state
).to
.equal('disconnected');
1939 it('should fail if we get a close event while connecting', function () {
1940 sinon
.spy(client
, "_fail");
1941 client
._rfb_connection_state
= 'connecting';
1942 client
._sock
._websocket
.close();
1943 expect(client
._fail
).to
.have
.been
.calledOnce
;
1946 it('should unregister close event handler', function () {
1947 sinon
.spy(client
._sock
, 'off');
1948 client
.disconnect();
1949 client
._sock
._websocket
.close();
1950 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
1953 // error events do nothing