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
;
58 function make_rfb (url
, options
) {
59 url
= url
|| 'wss://host:8675';
60 var rfb
= new RFB(document
.createElement('canvas'), url
, options
);
62 rfb
._sock
._websocket
._open();
63 rfb
._rfb_connection_state
= 'connected';
67 describe('Connecting/Disconnecting', function () {
68 describe('#RFB', function () {
69 it('should set the current state to "connecting"', function () {
70 var client
= new RFB(document
.createElement('canvas'), 'wss://host:8675');
71 client
._rfb_connection_state
= '';
73 expect(client
._rfb_connection_state
).to
.equal('connecting');
76 it('should actually connect to the websocket', function () {
77 var client
= new RFB(document
.createElement('canvas'), 'ws://HOST:8675/PATH');
78 sinon
.spy(client
._sock
, 'open');
80 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
81 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
85 describe('#disconnect', function () {
87 beforeEach(function () {
91 it('should go to state "disconnecting" before "disconnected"', function () {
92 sinon
.spy(client
, '_updateConnectionState');
94 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
95 expect(client
._updateConnectionState
.getCall(0).args
[0])
96 .to
.equal('disconnecting');
97 expect(client
._updateConnectionState
.getCall(1).args
[0])
98 .to
.equal('disconnected');
99 expect(client
._rfb_connection_state
).to
.equal('disconnected');
102 it('should unregister error event handler', function () {
103 sinon
.spy(client
._sock
, 'off');
105 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
108 it('should unregister message event handler', function () {
109 sinon
.spy(client
._sock
, 'off');
111 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
114 it('should unregister open event handler', function () {
115 sinon
.spy(client
._sock
, 'off');
117 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
121 describe('#sendCredentials', function () {
123 beforeEach(function () {
125 client
._rfb_connection_state
= 'connecting';
128 it('should set the rfb credentials properly"', function () {
129 client
.sendCredentials({ password
: 'pass' });
130 expect(client
._rfb_credentials
).to
.deep
.equal({ password
: 'pass' });
133 it('should call init_msg "soon"', function () {
134 client
._init_msg
= sinon
.spy();
135 client
.sendCredentials({ password
: 'pass' });
137 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
142 describe('Public API Basic Behavior', function () {
144 beforeEach(function () {
148 describe('#sendCtrlAlDel', function () {
149 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
150 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
151 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
152 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
153 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
154 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
155 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
156 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
158 client
.sendCtrlAltDel();
159 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
162 it('should not send the keys if we are not in a normal state', function () {
163 sinon
.spy(client
._sock
, 'flush');
164 client
._rfb_connection_state
= "broken";
165 client
.sendCtrlAltDel();
166 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
169 it('should not send the keys if we are set as view_only', function () {
170 sinon
.spy(client
._sock
, 'flush');
171 client
._viewOnly
= true;
172 client
.sendCtrlAltDel();
173 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
177 describe('#sendKey', function () {
178 it('should send a single key with the given code and state (down = true)', function () {
179 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
180 RFB
.messages
.keyEvent(expected
, 123, 1);
181 client
.sendKey(123, 'Key123', true);
182 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
185 it('should send both a down and up event if the state is not specified', function () {
186 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
187 RFB
.messages
.keyEvent(expected
, 123, 1);
188 RFB
.messages
.keyEvent(expected
, 123, 0);
189 client
.sendKey(123, 'Key123');
190 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
193 it('should not send the key if we are not in a normal state', function () {
194 sinon
.spy(client
._sock
, 'flush');
195 client
._rfb_connection_state
= "broken";
196 client
.sendKey(123, 'Key123');
197 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
200 it('should not send the key if we are set as view_only', function () {
201 sinon
.spy(client
._sock
, 'flush');
202 client
._viewOnly
= true;
203 client
.sendKey(123, 'Key123');
204 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
207 it('should send QEMU extended events if supported', function () {
208 client
._qemuExtKeyEventSupported
= true;
209 var expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
210 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
211 client
.sendKey(0x20, 'Space', true);
212 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
215 it('should not send QEMU extended events if unknown key code', function () {
216 client
._qemuExtKeyEventSupported
= true;
217 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
218 RFB
.messages
.keyEvent(expected
, 123, 1);
219 client
.sendKey(123, 'FooBar', true);
220 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
224 describe('#clipboardPasteFrom', function () {
225 it('should send the given text in a paste event', function () {
226 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
227 RFB
.messages
.clientCutText(expected
, 'abc');
228 client
.clipboardPasteFrom('abc');
229 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
232 it('should not send the text if we are not in a normal state', function () {
233 sinon
.spy(client
._sock
, 'flush');
234 client
._rfb_connection_state
= "broken";
235 client
.clipboardPasteFrom('abc');
236 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
240 describe("#requestDesktopSize", function () {
241 beforeEach(function() {
242 client
._supportsSetDesktopSize
= true;
245 it('should send the request with the given width and height', function () {
246 var expected
= [251];
247 push8(expected
, 0); // padding
248 push16(expected
, 1); // width
249 push16(expected
, 2); // height
250 push8(expected
, 1); // number-of-screens
251 push8(expected
, 0); // padding before screen array
252 push32(expected
, 0); // id
253 push16(expected
, 0); // x-position
254 push16(expected
, 0); // y-position
255 push16(expected
, 1); // width
256 push16(expected
, 2); // height
257 push32(expected
, 0); // flags
259 client
.requestDesktopSize(1, 2);
260 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
263 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
264 sinon
.spy(client
._sock
, 'flush');
265 client
._supportsSetDesktopSize
= false;
266 client
.requestDesktopSize(1,2);
267 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
270 it('should not send the request if we are not in a normal state', function () {
271 sinon
.spy(client
._sock
, 'flush');
272 client
._rfb_connection_state
= "broken";
273 client
.requestDesktopSize(1,2);
274 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
278 describe("XVP operations", function () {
279 beforeEach(function () {
280 client
._rfb_xvp_ver
= 1;
283 it('should send the shutdown signal on #machineShutdown', function () {
284 client
.machineShutdown();
285 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
288 it('should send the reboot signal on #machineReboot', function () {
289 client
.machineReboot();
290 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
293 it('should send the reset signal on #machineReset', function () {
294 client
.machineReset();
295 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
298 it('should not send XVP operations with higher versions than we support', function () {
299 sinon
.spy(client
._sock
, 'flush');
301 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
306 describe('Misc Internals', function () {
307 describe('#_updateConnectionState', function () {
309 beforeEach(function () {
313 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
314 var spy
= sinon
.spy();
315 client
._disconnTimer
= setTimeout(spy
, 50);
316 client
._rfb_connection_state
= 'connecting';
317 client
._updateConnectionState('connected');
319 expect(spy
).to
.not
.have
.been
.called
;
320 expect(client
._disconnTimer
).to
.be
.null;
323 it('should set the rfb_connection_state', function () {
324 client
._rfb_connection_state
= 'disconnecting';
325 client
._updateConnectionState('disconnected');
326 expect(client
._rfb_connection_state
).to
.equal('disconnected');
329 it('should not change the state when we are disconnected', function () {
330 client
._rfb_connection_state
= 'disconnected';
331 client
._updateConnectionState('connecting');
332 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
335 it('should ignore state changes to the same state', function () {
336 var connectSpy
= sinon
.spy();
337 var disconnectSpy
= sinon
.spy();
338 client
.addEventListener("connect", connectSpy
);
339 client
.addEventListener("disconnect", disconnectSpy
);
341 client
._rfb_connection_state
= 'connected';
342 client
._updateConnectionState('connected');
343 expect(connectSpy
).to
.not
.have
.been
.called
;
345 client
._rfb_connection_state
= 'disconnected';
346 client
._updateConnectionState('disconnected');
347 expect(disconnectSpy
).to
.not
.have
.been
.called
;
350 it('should ignore illegal state changes', function () {
351 var spy
= sinon
.spy();
352 client
.addEventListener("disconnect", spy
);
353 client
._rfb_connection_state
= 'connected';
354 client
._updateConnectionState('disconnected');
355 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
356 expect(spy
).to
.not
.have
.been
.called
;
360 describe('#_fail', function () {
362 beforeEach(function () {
366 it('should close the WebSocket connection', function () {
367 sinon
.spy(client
._sock
, 'close');
369 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
372 it('should transition to disconnected', function () {
373 sinon
.spy(client
, '_updateConnectionState');
375 this.clock
.tick(2000);
376 expect(client
._updateConnectionState
).to
.have
.been
.called
;
377 expect(client
._rfb_connection_state
).to
.equal('disconnected');
380 it('should set clean_disconnect variable', function () {
381 client
._rfb_clean_disconnect
= true;
382 client
._rfb_connection_state
= 'connected';
384 expect(client
._rfb_clean_disconnect
).to
.be
.false;
387 it('should result in disconnect event with clean set to false', function () {
388 client
._rfb_connection_state
= 'connected';
389 var spy
= sinon
.spy();
390 client
.addEventListener("disconnect", spy
);
392 this.clock
.tick(2000);
393 expect(spy
).to
.have
.been
.calledOnce
;
394 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
400 describe('Connection States', function () {
401 describe('connecting', function () {
402 it('should open the websocket connection', function () {
403 var client
= new RFB(document
.createElement('canvas'),
404 'ws://HOST:8675/PATH');
405 sinon
.spy(client
._sock
, 'open');
407 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
411 describe('connected', function () {
413 beforeEach(function () {
417 it('should result in a connect event if state becomes connected', function () {
418 var spy
= sinon
.spy();
419 client
.addEventListener("connect", spy
);
420 client
._rfb_connection_state
= 'connecting';
421 client
._updateConnectionState('connected');
422 expect(spy
).to
.have
.been
.calledOnce
;
425 it('should not result in a connect event if the state is not "connected"', function () {
426 var spy
= sinon
.spy();
427 client
.addEventListener("connect", spy
);
428 client
._sock
._websocket
.open = function () {}; // explicitly don't call onopen
429 client
._updateConnectionState('connecting');
430 expect(spy
).to
.not
.have
.been
.called
;
434 describe('disconnecting', function () {
436 beforeEach(function () {
440 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
441 sinon
.spy(client
, '_updateConnectionState');
442 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
443 client
._updateConnectionState('disconnecting');
444 this.clock
.tick(3 * 1000);
445 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
446 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
447 expect(client
._rfb_connection_state
).to
.equal("disconnected");
450 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
451 client
._updateConnectionState('disconnecting');
452 this.clock
.tick(3 * 1000 / 2);
453 client
._sock
._websocket
.close();
454 this.clock
.tick(3 * 1000 / 2 + 1);
455 expect(client
._rfb_connection_state
).to
.equal('disconnected');
458 it('should close the WebSocket connection', function () {
459 sinon
.spy(client
._sock
, 'close');
460 client
._updateConnectionState('disconnecting');
461 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
465 describe('disconnected', function () {
467 beforeEach(function () { client
= make_rfb(); });
469 it('should result in a disconnect event if state becomes "disconnected"', function () {
470 var spy
= sinon
.spy();
471 client
.addEventListener("disconnect", spy
);
472 client
._rfb_connection_state
= 'disconnecting';
473 client
._updateConnectionState('disconnected');
474 expect(spy
).to
.have
.been
.calledOnce
;
475 expect(spy
.args
[0][0].detail
.clean
).to
.be
.true;
478 it('should not result in a disconnect event if the state is not "disconnected"', function () {
479 var spy
= sinon
.spy();
480 client
.addEventListener("disconnect", spy
);
481 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
482 client
._updateConnectionState('disconnecting');
483 expect(spy
).to
.not
.have
.been
.called
;
486 it('should result in a disconnect event without msg when no reason given', function () {
487 var spy
= sinon
.spy();
488 client
.addEventListener("disconnect", spy
);
489 client
._rfb_connection_state
= 'disconnecting';
490 client
._rfb_disconnect_reason
= "";
491 client
._updateConnectionState('disconnected');
492 expect(spy
).to
.have
.been
.calledOnce
;
493 expect(spy
.args
[0].length
).to
.equal(1);
498 describe('Protocol Initialization States', function () {
500 beforeEach(function () {
502 client
._rfb_connection_state
= 'connecting';
505 describe('ProtocolVersion', function () {
506 function send_ver (ver
, client
) {
507 var arr
= new Uint8Array(12);
508 for (var i
= 0; i
< ver
.length
; i
++) {
509 arr
[i
+4] = ver
.charCodeAt(i
);
511 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
513 client
._sock
._websocket
._receive_data(arr
);
516 describe('version parsing', function () {
517 it('should interpret version 003.003 as version 3.3', function () {
518 send_ver('003.003', client
);
519 expect(client
._rfb_version
).to
.equal(3.3);
522 it('should interpret version 003.006 as version 3.3', function () {
523 send_ver('003.006', client
);
524 expect(client
._rfb_version
).to
.equal(3.3);
527 it('should interpret version 003.889 as version 3.3', function () {
528 send_ver('003.889', client
);
529 expect(client
._rfb_version
).to
.equal(3.3);
532 it('should interpret version 003.007 as version 3.7', function () {
533 send_ver('003.007', client
);
534 expect(client
._rfb_version
).to
.equal(3.7);
537 it('should interpret version 003.008 as version 3.8', function () {
538 send_ver('003.008', client
);
539 expect(client
._rfb_version
).to
.equal(3.8);
542 it('should interpret version 004.000 as version 3.8', function () {
543 send_ver('004.000', client
);
544 expect(client
._rfb_version
).to
.equal(3.8);
547 it('should interpret version 004.001 as version 3.8', function () {
548 send_ver('004.001', client
);
549 expect(client
._rfb_version
).to
.equal(3.8);
552 it('should interpret version 005.000 as version 3.8', function () {
553 send_ver('005.000', client
);
554 expect(client
._rfb_version
).to
.equal(3.8);
557 it('should fail on an invalid version', function () {
558 sinon
.spy(client
, "_fail");
559 send_ver('002.000', client
);
560 expect(client
._fail
).to
.have
.been
.calledOnce
;
564 it('should send back the interpreted version', function () {
565 send_ver('004.000', client
);
567 var expected_str
= 'RFB 003.008\n';
569 for (var i
= 0; i
< expected_str
.length
; i
++) {
570 expected
[i
] = expected_str
.charCodeAt(i
);
573 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
576 it('should transition to the Security state on successful negotiation', function () {
577 send_ver('003.008', client
);
578 expect(client
._rfb_init_state
).to
.equal('Security');
581 describe('Repeater', function () {
582 beforeEach(function () {
583 client
= make_rfb('wss://host:8675', { repeaterID
: "12345" });
584 client
._rfb_connection_state
= 'connecting';
587 it('should interpret version 000.000 as a repeater', function () {
588 send_ver('000.000', client
);
589 expect(client
._rfb_version
).to
.equal(0);
591 var sent_data
= client
._sock
._websocket
._get_sent_data();
592 expect(new Uint8Array(sent_data
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
593 expect(sent_data
).to
.have
.length(250);
596 it('should handle two step repeater negotiation', function () {
597 send_ver('000.000', client
);
598 send_ver('003.008', client
);
599 expect(client
._rfb_version
).to
.equal(3.8);
604 describe('Security', function () {
605 beforeEach(function () {
606 client
._rfb_init_state
= 'Security';
609 it('should simply receive the auth scheme when for versions < 3.7', function () {
610 client
._rfb_version
= 3.6;
611 var auth_scheme_raw
= [1, 2, 3, 4];
612 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
613 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
614 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
615 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
618 it('should prefer no authentication is possible', function () {
619 client
._rfb_version
= 3.7;
620 var auth_schemes
= [2, 1, 3];
621 client
._sock
._websocket
._receive_data(auth_schemes
);
622 expect(client
._rfb_auth_scheme
).to
.equal(1);
623 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
626 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
627 client
._rfb_version
= 3.7;
628 var auth_schemes
= [2, 22, 16];
629 client
._sock
._websocket
._receive_data(auth_schemes
);
630 expect(client
._rfb_auth_scheme
).to
.equal(22);
631 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
634 it('should fail if there are no supported schemes for versions >= 3.7', function () {
635 sinon
.spy(client
, "_fail");
636 client
._rfb_version
= 3.7;
637 var auth_schemes
= [1, 32];
638 client
._sock
._websocket
._receive_data(auth_schemes
);
639 expect(client
._fail
).to
.have
.been
.calledOnce
;
642 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
643 client
._rfb_version
= 3.7;
644 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
645 sinon
.spy(client
, '_fail');
646 client
._sock
._websocket
._receive_data(failure_data
);
648 expect(client
._fail
).to
.have
.been
.calledOnce
;
649 expect(client
._fail
).to
.have
.been
.calledWith(
650 'Security negotiation failed on no security types (reason: whoops)');
653 it('should transition to the Authentication state and continue on successful negotiation', function () {
654 client
._rfb_version
= 3.7;
655 var auth_schemes
= [1, 1];
656 client
._negotiate_authentication
= sinon
.spy();
657 client
._sock
._websocket
._receive_data(auth_schemes
);
658 expect(client
._rfb_init_state
).to
.equal('Authentication');
659 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
663 describe('Authentication', function () {
664 beforeEach(function () {
665 client
._rfb_init_state
= 'Security';
668 function send_security(type
, cl
) {
669 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
672 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
673 client
._rfb_version
= 3.6;
674 var err_msg
= "Whoopsies";
675 var data
= [0, 0, 0, 0];
676 var err_len
= err_msg
.length
;
677 push32(data
, err_len
);
678 for (var i
= 0; i
< err_len
; i
++) {
679 data
.push(err_msg
.charCodeAt(i
));
682 sinon
.spy(client
, '_fail');
683 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
684 expect(client
._fail
).to
.have
.been
.calledWith(
685 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
688 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
689 client
._rfb_version
= 3.8;
690 send_security(1, client
);
691 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
694 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
695 client
._rfb_version
= 3.7;
696 send_security(1, client
);
697 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
700 it('should fail on an unknown auth scheme', function () {
701 sinon
.spy(client
, "_fail");
702 client
._rfb_version
= 3.8;
703 send_security(57, client
);
704 expect(client
._fail
).to
.have
.been
.calledOnce
;
707 describe('VNC Authentication (type 2) Handler', function () {
708 beforeEach(function () {
709 client
._rfb_init_state
= 'Security';
710 client
._rfb_version
= 3.8;
713 it('should fire the credentialsrequired event if missing a password', function () {
714 var spy
= sinon
.spy();
715 client
.addEventListener("credentialsrequired", spy
);
716 send_security(2, client
);
719 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
720 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
722 expect(client
._rfb_credentials
).to
.be
.empty
;
723 expect(spy
).to
.have
.been
.calledOnce
;
724 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
727 it('should encrypt the password with DES and then send it back', function () {
728 client
._rfb_credentials
= { password
: 'passwd' };
729 send_security(2, client
);
730 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
733 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
734 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
736 var des_pass
= RFB
.genDES('passwd', challenge
);
737 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
740 it('should transition to SecurityResult immediately after sending the password', function () {
741 client
._rfb_credentials
= { password
: 'passwd' };
742 send_security(2, client
);
745 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
746 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
748 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
752 describe('XVP Authentication (type 22) Handler', function () {
753 beforeEach(function () {
754 client
._rfb_init_state
= 'Security';
755 client
._rfb_version
= 3.8;
758 it('should fall through to standard VNC authentication upon completion', function () {
759 client
._rfb_credentials
= { username
: 'user',
761 password
: 'password' };
762 client
._negotiate_std_vnc_auth
= sinon
.spy();
763 send_security(22, client
);
764 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
767 it('should fire the credentialsrequired event if all credentials are missing', function() {
768 var spy
= sinon
.spy();
769 client
.addEventListener("credentialsrequired", spy
);
770 client
._rfb_credentials
= {};
771 send_security(22, client
);
773 expect(client
._rfb_credentials
).to
.be
.empty
;
774 expect(spy
).to
.have
.been
.calledOnce
;
775 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
778 it('should fire the credentialsrequired event if some credentials are missing', function() {
779 var spy
= sinon
.spy();
780 client
.addEventListener("credentialsrequired", spy
);
781 client
._rfb_credentials
= { username
: 'user',
783 send_security(22, client
);
785 expect(spy
).to
.have
.been
.calledOnce
;
786 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
789 it('should send user and target separately', function () {
790 client
._rfb_credentials
= { username
: 'user',
792 password
: 'password' };
793 client
._negotiate_std_vnc_auth
= sinon
.spy();
795 send_security(22, client
);
797 var expected
= [22, 4, 6]; // auth selection, len user, len target
798 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
800 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
804 describe('TightVNC Authentication (type 16) Handler', function () {
805 beforeEach(function () {
806 client
._rfb_init_state
= 'Security';
807 client
._rfb_version
= 3.8;
808 send_security(16, client
);
809 client
._sock
._websocket
._get_sent_data(); // skip the security reply
812 function send_num_str_pairs(pairs
, client
) {
813 var pairs_len
= pairs
.length
;
815 push32(data
, pairs_len
);
817 for (var i
= 0; i
< pairs_len
; i
++) {
818 push32(data
, pairs
[i
][0]);
820 for (j
= 0; j
< 4; j
++) {
821 data
.push(pairs
[i
][1].charCodeAt(j
));
823 for (j
= 0; j
< 8; j
++) {
824 data
.push(pairs
[i
][2].charCodeAt(j
));
828 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
831 it('should skip tunnel negotiation if no tunnels are requested', function () {
832 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
833 expect(client
._rfb_tightvnc
).to
.be
.true;
836 it('should fail if no supported tunnels are listed', function () {
837 sinon
.spy(client
, "_fail");
838 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
839 expect(client
._fail
).to
.have
.been
.calledOnce
;
842 it('should choose the notunnel tunnel type', function () {
843 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
844 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
847 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
848 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
849 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
850 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
851 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
852 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
855 /*it('should attempt to use VNC auth over no auth when possible', function () {
856 client._rfb_tightvnc = true;
857 client._negotiate_std_vnc_auth = sinon.spy();
858 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
859 expect(client._sock).to.have.sent([0, 0, 0, 1]);
860 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
861 expect(client._rfb_auth_scheme).to.equal(2);
862 });*/ // while this would make sense, the original code doesn't actually do this
864 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
865 client
._rfb_tightvnc
= true;
866 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
867 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
868 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
871 it('should accept VNC authentication and transition to that', function () {
872 client
._rfb_tightvnc
= true;
873 client
._negotiate_std_vnc_auth
= sinon
.spy();
874 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
875 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
876 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
877 expect(client
._rfb_auth_scheme
).to
.equal(2);
880 it('should fail if there are no supported auth types', function () {
881 sinon
.spy(client
, "_fail");
882 client
._rfb_tightvnc
= true;
883 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
884 expect(client
._fail
).to
.have
.been
.calledOnce
;
889 describe('SecurityResult', function () {
890 beforeEach(function () {
891 client
._rfb_init_state
= 'SecurityResult';
894 it('should fall through to ServerInitialisation on a response code of 0', function () {
895 client
._updateConnectionState
= sinon
.spy();
896 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
897 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
900 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
901 client
._rfb_version
= 3.8;
902 sinon
.spy(client
, '_fail');
903 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
904 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
905 expect(client
._fail
).to
.have
.been
.calledWith(
906 'Security negotiation failed on security result (reason: whoops)');
909 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
910 sinon
.spy(client
, '_fail');
911 client
._rfb_version
= 3.7;
912 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
913 expect(client
._fail
).to
.have
.been
.calledWith(
914 'Security handshake failed');
917 it('should result in securityfailure event when receiving a non zero status', function () {
918 var spy
= sinon
.spy();
919 client
.addEventListener("securityfailure", spy
);
920 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
921 expect(spy
).to
.have
.been
.calledOnce
;
922 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
925 it('should include reason when provided in securityfailure event', function () {
926 client
._rfb_version
= 3.8;
927 var spy
= sinon
.spy();
928 client
.addEventListener("securityfailure", spy
);
929 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
930 32, 102, 97, 105, 108, 117, 114, 101];
931 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
932 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
933 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
936 it('should not include reason when length is zero in securityfailure event', function () {
937 client
._rfb_version
= 3.9;
938 var spy
= sinon
.spy();
939 client
.addEventListener("securityfailure", spy
);
940 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 0];
941 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
942 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
943 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
946 it('should not include reason in securityfailure event for version < 3.8', function () {
947 client
._rfb_version
= 3.6;
948 var spy
= sinon
.spy();
949 client
.addEventListener("securityfailure", spy
);
950 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
951 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
952 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
956 describe('ClientInitialisation', function () {
957 it('should transition to the ServerInitialisation state', function () {
958 var client
= make_rfb();
959 client
._rfb_connection_state
= 'connecting';
960 client
._rfb_init_state
= 'SecurityResult';
961 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
962 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
965 it('should send 1 if we are in shared mode', function () {
966 var client
= make_rfb('wss://host:8675', { shared
: true });
967 client
._rfb_connection_state
= 'connecting';
968 client
._rfb_init_state
= 'SecurityResult';
969 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
970 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
973 it('should send 0 if we are not in shared mode', function () {
974 var client
= make_rfb('wss://host:8675', { shared
: false });
975 client
._rfb_connection_state
= 'connecting';
976 client
._rfb_init_state
= 'SecurityResult';
977 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
978 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
982 describe('ServerInitialisation', function () {
983 beforeEach(function () {
984 client
._rfb_init_state
= 'ServerInitialisation';
987 function send_server_init(opts
, client
) {
988 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
989 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
990 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
991 for (var opt
in opts
) {
992 full_opts
[opt
] = opts
[opt
];
996 push16(data
, full_opts
.width
);
997 push16(data
, full_opts
.height
);
999 data
.push(full_opts
.bpp
);
1000 data
.push(full_opts
.depth
);
1001 data
.push(full_opts
.big_endian
);
1002 data
.push(full_opts
.true_color
);
1004 push16(data
, full_opts
.red_max
);
1005 push16(data
, full_opts
.green_max
);
1006 push16(data
, full_opts
.blue_max
);
1007 push8(data
, full_opts
.red_shift
);
1008 push8(data
, full_opts
.green_shift
);
1009 push8(data
, full_opts
.blue_shift
);
1016 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1019 push32(name_data
, full_opts
.name
.length
);
1020 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1021 name_data
.push(full_opts
.name
.charCodeAt(i
));
1023 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1026 it('should set the framebuffer width and height', function () {
1027 send_server_init({ width
: 32, height
: 84 }, client
);
1028 expect(client
._fb_width
).to
.equal(32);
1029 expect(client
._fb_height
).to
.equal(84);
1032 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1034 it('should set the framebuffer name and call the callback', function () {
1035 var spy
= sinon
.spy();
1036 client
.addEventListener("desktopname", spy
);
1037 send_server_init({ name
: 'some name' }, client
);
1039 expect(client
._fb_name
).to
.equal('some name');
1040 expect(spy
).to
.have
.been
.calledOnce
;
1041 expect(spy
.args
[0][0].detail
.name
).to
.equal('some name');
1044 it('should handle the extended init message of the tight encoding', function () {
1045 // NB(sross): we don't actually do anything with it, so just test that we can
1046 // read it w/o throwing an error
1047 client
._rfb_tightvnc
= true;
1048 send_server_init({}, client
);
1050 var tight_data
= [];
1051 push16(tight_data
, 1);
1052 push16(tight_data
, 2);
1053 push16(tight_data
, 3);
1054 push16(tight_data
, 0);
1055 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1058 client
._sock
._websocket
._receive_data(tight_data
);
1060 expect(client
._rfb_connection_state
).to
.equal('connected');
1063 it('should call the resize callback and resize the display', function () {
1064 var spy
= sinon
.spy();
1065 client
.addEventListener("fbresize", spy
);
1066 sinon
.spy(client
._display
, 'resize');
1067 send_server_init({ width
: 27, height
: 32 }, client
);
1069 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1070 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1071 expect(spy
).to
.have
.been
.calledOnce
;
1072 expect(spy
.args
[0][0].detail
.width
).to
.equal(27);
1073 expect(spy
.args
[0][0].detail
.height
).to
.equal(32);
1076 it('should grab the mouse and keyboard', function () {
1077 sinon
.spy(client
._keyboard
, 'grab');
1078 sinon
.spy(client
._mouse
, 'grab');
1079 send_server_init({}, client
);
1080 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1081 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1084 describe('Initial Update Request', function () {
1085 beforeEach(function () {
1086 sinon
.spy(RFB
.messages
, "pixelFormat");
1087 sinon
.spy(RFB
.messages
, "clientEncodings");
1088 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1091 afterEach(function () {
1092 RFB
.messages
.pixelFormat
.restore();
1093 RFB
.messages
.clientEncodings
.restore();
1094 RFB
.messages
.fbUpdateRequest
.restore();
1097 // TODO(directxman12): test the various options in this configuration matrix
1098 it('should reply with the pixel format, client encodings, and initial update request', function () {
1099 send_server_init({ width
: 27, height
: 32 }, client
);
1101 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1102 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1103 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1104 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1105 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1106 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1107 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1108 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1111 it('should reply with restricted settings for Intel AMT servers', function () {
1112 send_server_init({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1114 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1115 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1116 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1117 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1118 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1119 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1120 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1121 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1122 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1126 it('should transition to the "connected" state', function () {
1127 send_server_init({}, client
);
1128 expect(client
._rfb_connection_state
).to
.equal('connected');
1133 describe('Protocol Message Processing After Completing Initialization', function () {
1136 beforeEach(function () {
1137 client
= make_rfb();
1138 client
._fb_name
= 'some device';
1139 client
._fb_width
= 640;
1140 client
._fb_height
= 20;
1143 describe('Framebuffer Update Handling', function () {
1144 var target_data_arr
= [
1145 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1146 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1147 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1148 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1152 var target_data_check_arr
= [
1153 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1154 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1155 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1156 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1158 var target_data_check
;
1160 before(function () {
1161 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1162 target_data
= new Uint8Array(target_data_arr
);
1163 target_data_check
= new Uint8Array(target_data_check_arr
);
1166 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1169 if (!rect_cnt
|| rect_cnt
> -1) {
1171 data
.push(0); // msg type
1172 data
.push(0); // padding
1173 push16(data
, rect_cnt
|| rect_data
.length
);
1176 for (var i
= 0; i
< rect_data
.length
; i
++) {
1178 push16(data
, rect_info
[i
].x
);
1179 push16(data
, rect_info
[i
].y
);
1180 push16(data
, rect_info
[i
].width
);
1181 push16(data
, rect_info
[i
].height
);
1182 push32(data
, rect_info
[i
].encoding
);
1184 data
= data
.concat(rect_data
[i
]);
1187 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1190 it('should send an update request if there is sufficient data', function () {
1191 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1192 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1194 client
._framebufferUpdate = function () { return true; };
1195 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1197 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1200 it('should not send an update request if we need more data', function () {
1201 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1202 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1205 it('should resume receiving an update if we previously did not have enough data', function () {
1206 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1207 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1209 // just enough to set FBU.rects
1210 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1211 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1213 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1214 // 247 should *not* be used as the message type here
1215 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1216 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1219 it('should not send a request in continuous updates mode', function () {
1220 client
._enabledContinuousUpdates
= true;
1221 client
._framebufferUpdate = function () { return true; };
1222 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1224 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1227 it('should fail on an unsupported encoding', function () {
1228 sinon
.spy(client
, "_fail");
1229 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1230 send_fbu_msg([rect_info
], [[]], client
);
1231 expect(client
._fail
).to
.have
.been
.calledOnce
;
1234 it('should be able to pause and resume receiving rects if not enought data', function () {
1235 // seed some initial data to copy
1236 client
._fb_width
= 4;
1237 client
._fb_height
= 4;
1238 client
._display
.resize(4, 4);
1239 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1241 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1242 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1243 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1244 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1245 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1246 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1247 expect(client
._display
).to
.have
.displayed(target_data_check
);
1250 describe('Message Encoding Handlers', function () {
1251 beforeEach(function () {
1252 // a really small frame
1253 client
._fb_width
= 4;
1254 client
._fb_height
= 4;
1255 client
._fb_depth
= 24;
1256 client
._display
.resize(4, 4);
1259 it('should handle the RAW encoding', function () {
1260 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1261 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1262 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1263 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1266 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1267 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1268 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1269 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1270 send_fbu_msg(info
, rects
, client
);
1271 expect(client
._display
).to
.have
.displayed(target_data
);
1274 it('should handle the RAW encoding in low colour mode', function () {
1275 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1276 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1277 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1278 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1280 [0x03, 0x03, 0x03, 0x03],
1281 [0x0c, 0x0c, 0x0c, 0x0c],
1282 [0x0c, 0x0c, 0x03, 0x03],
1283 [0x0c, 0x0c, 0x03, 0x03]];
1284 client
._fb_depth
= 8;
1285 send_fbu_msg(info
, rects
, client
);
1286 expect(client
._display
).to
.have
.displayed(target_data_check
);
1289 it('should handle the COPYRECT encoding', function () {
1290 // seed some initial data to copy
1291 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1293 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1294 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1295 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1296 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1297 send_fbu_msg(info
, rects
, client
);
1298 expect(client
._display
).to
.have
.displayed(target_data_check
);
1301 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1302 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1304 it('should handle the RRE encoding', function () {
1305 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1307 push32(rect
, 2); // 2 subrects
1308 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1309 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1313 push16(rect
, 0); // x: 0
1314 push16(rect
, 0); // y: 0
1315 push16(rect
, 2); // width: 2
1316 push16(rect
, 2); // height: 2
1317 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1321 push16(rect
, 2); // x: 2
1322 push16(rect
, 2); // y: 2
1323 push16(rect
, 2); // width: 2
1324 push16(rect
, 2); // height: 2
1326 send_fbu_msg(info
, [rect
], client
);
1327 expect(client
._display
).to
.have
.displayed(target_data_check
);
1330 describe('the HEXTILE encoding handler', function () {
1331 it('should handle a tile with fg, bg specified, normal subrects', function () {
1332 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1334 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1335 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1336 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1340 rect
.push(2); // 2 subrects
1341 rect
.push(0); // x: 0, y: 0
1342 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1343 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1344 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1345 send_fbu_msg(info
, [rect
], client
);
1346 expect(client
._display
).to
.have
.displayed(target_data_check
);
1349 it('should handle a raw tile', function () {
1350 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1352 rect
.push(0x01); // raw
1353 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1354 rect
.push(target_data
[i
+ 2]);
1355 rect
.push(target_data
[i
+ 1]);
1356 rect
.push(target_data
[i
]);
1357 rect
.push(target_data
[i
+ 3]);
1359 send_fbu_msg(info
, [rect
], client
);
1360 expect(client
._display
).to
.have
.displayed(target_data
);
1363 it('should handle a tile with only bg specified (solid bg)', function () {
1364 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1367 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1368 send_fbu_msg(info
, [rect
], client
);
1371 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1372 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1375 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1376 // set the width so we can have two tiles
1377 client
._fb_width
= 8;
1378 client
._display
.resize(8, 4);
1380 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1386 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1388 // send an empty frame
1391 send_fbu_msg(info
, [rect
], client
);
1395 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1396 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1397 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1400 it('should handle a tile with bg and coloured subrects', function () {
1401 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1403 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1404 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1405 rect
.push(2); // 2 subrects
1406 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1410 rect
.push(0); // x: 0, y: 0
1411 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1412 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1416 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1417 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1418 send_fbu_msg(info
, [rect
], client
);
1419 expect(client
._display
).to
.have
.displayed(target_data_check
);
1422 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1423 client
._fb_width
= 4;
1424 client
._fb_height
= 17;
1425 client
._display
.resize(4, 17);
1427 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1429 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1430 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1431 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1435 rect
.push(8); // 8 subrects
1437 for (i
= 0; i
< 4; i
++) {
1438 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1439 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1440 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1441 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1443 rect
.push(0x08); // anysubrects
1444 rect
.push(1); // 1 subrect
1445 rect
.push(0); // x: 0, y: 0
1446 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1447 send_fbu_msg(info
, [rect
], client
);
1450 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1451 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1452 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1455 it('should fail on an invalid subencoding', function () {
1456 sinon
.spy(client
,"_fail");
1457 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1458 var rects
= [[45]]; // an invalid subencoding
1459 send_fbu_msg(info
, rects
, client
);
1460 expect(client
._fail
).to
.have
.been
.calledOnce
;
1464 it
.skip('should handle the TIGHT encoding', function () {
1465 // TODO(directxman12): test this
1468 it
.skip('should handle the TIGHT_PNG encoding', function () {
1469 // TODO(directxman12): test this
1472 it('should handle the DesktopSize pseduo-encoding', function () {
1473 var spy
= sinon
.spy();
1474 client
.addEventListener("fbresize", spy
);
1475 sinon
.spy(client
._display
, 'resize');
1476 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1478 expect(spy
).to
.have
.been
.calledOnce
;
1479 expect(spy
.args
[0][0].detail
.width
).to
.equal(20);
1480 expect(spy
.args
[0][0].detail
.height
).to
.equal(50);
1482 expect(client
._fb_width
).to
.equal(20);
1483 expect(client
._fb_height
).to
.equal(50);
1485 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1486 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1489 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1492 beforeEach(function () {
1493 client
._supportsSetDesktopSize
= false;
1494 // a really small frame
1495 client
._fb_width
= 4;
1496 client
._fb_height
= 4;
1497 client
._display
.resize(4, 4);
1498 sinon
.spy(client
._display
, 'resize');
1499 resizeSpy
= sinon
.spy();
1500 client
.addEventListener("fbresize", resizeSpy
);
1503 function make_screen_data (nr_of_screens
) {
1505 push8(data
, nr_of_screens
); // number-of-screens
1506 push8(data
, 0); // padding
1507 push16(data
, 0); // padding
1508 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1509 push32(data
, 0); // id
1510 push16(data
, 0); // x-position
1511 push16(data
, 0); // y-position
1512 push16(data
, 20); // width
1513 push16(data
, 50); // height
1514 push32(data
, 0); // flags
1519 it('should call callback when resize is supported', function () {
1520 var spy
= sinon
.spy();
1521 client
.addEventListener("capabilities", spy
);
1523 expect(client
._supportsSetDesktopSize
).to
.be
.false;
1524 expect(client
.capabilities
.resize
).to
.be
.false;
1526 var reason_for_change
= 0; // server initiated
1527 var status_code
= 0; // No error
1529 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1530 width
: 4, height
: 4, encoding
: -308 }],
1531 make_screen_data(1), client
);
1533 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1534 expect(spy
).to
.have
.been
.calledOnce
;
1535 expect(spy
.args
[0][0].detail
.capabilities
.resize
).to
.be
.true;
1536 expect(client
.capabilities
.resize
).to
.be
.true;
1539 it('should handle a resize requested by this client', function () {
1540 var reason_for_change
= 1; // requested by this client
1541 var status_code
= 0; // No error
1543 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1544 width
: 20, height
: 50, encoding
: -308 }],
1545 make_screen_data(1), client
);
1547 expect(client
._fb_width
).to
.equal(20);
1548 expect(client
._fb_height
).to
.equal(50);
1550 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1551 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1553 expect(resizeSpy
).to
.have
.been
.calledOnce
;
1554 expect(resizeSpy
.args
[0][0].detail
.width
).to
.equal(20);
1555 expect(resizeSpy
.args
[0][0].detail
.height
).to
.equal(50);
1558 it('should handle a resize requested by another client', function () {
1559 var reason_for_change
= 2; // requested by another client
1560 var status_code
= 0; // No error
1562 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1563 width
: 20, height
: 50, encoding
: -308 }],
1564 make_screen_data(1), client
);
1566 expect(client
._fb_width
).to
.equal(20);
1567 expect(client
._fb_height
).to
.equal(50);
1569 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1570 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1572 expect(resizeSpy
).to
.have
.been
.calledOnce
;
1573 expect(resizeSpy
.args
[0][0].detail
.width
).to
.equal(20);
1574 expect(resizeSpy
.args
[0][0].detail
.height
).to
.equal(50);
1577 it('should be able to recieve requests which contain data for multiple screens', function () {
1578 var reason_for_change
= 2; // requested by another client
1579 var status_code
= 0; // No error
1581 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1582 width
: 60, height
: 50, encoding
: -308 }],
1583 make_screen_data(3), client
);
1585 expect(client
._fb_width
).to
.equal(60);
1586 expect(client
._fb_height
).to
.equal(50);
1588 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1589 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1591 expect(resizeSpy
).to
.have
.been
.calledOnce
;
1592 expect(resizeSpy
.args
[0][0].detail
.width
).to
.equal(60);
1593 expect(resizeSpy
.args
[0][0].detail
.height
).to
.equal(50);
1596 it('should not handle a failed request', function () {
1597 var reason_for_change
= 1; // requested by this client
1598 var status_code
= 1; // Resize is administratively prohibited
1600 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1601 width
: 20, height
: 50, encoding
: -308 }],
1602 make_screen_data(1), client
);
1604 expect(client
._fb_width
).to
.equal(4);
1605 expect(client
._fb_height
).to
.equal(4);
1607 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1609 expect(resizeSpy
).to
.not
.have
.been
.called
;
1613 it
.skip('should handle the Cursor pseudo-encoding', function () {
1614 // TODO(directxman12): test
1617 it('should handle the last_rect pseudo-encoding', function () {
1618 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1619 expect(client
._FBU
.rects
).to
.equal(0);
1624 describe('XVP Message Handling', function () {
1625 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1626 var spy
= sinon
.spy();
1627 client
.addEventListener("capabilities", spy
);
1628 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1629 expect(client
._rfb_xvp_ver
).to
.equal(10);
1630 expect(spy
).to
.have
.been
.calledOnce
;
1631 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
1632 expect(client
.capabilities
.power
).to
.be
.true;
1635 it('should fail on unknown XVP message types', function () {
1636 sinon
.spy(client
, "_fail");
1637 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1638 expect(client
._fail
).to
.have
.been
.calledOnce
;
1642 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1643 var expected_str
= 'cheese!';
1644 var data
= [3, 0, 0, 0];
1645 push32(data
, expected_str
.length
);
1646 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1647 var spy
= sinon
.spy();
1648 client
.addEventListener("clipboard", spy
);
1650 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1651 expect(spy
).to
.have
.been
.calledOnce
;
1652 expect(spy
.args
[0][0].detail
.text
).to
.equal(expected_str
);
1655 it('should fire the bell callback on Bell', function () {
1656 var spy
= sinon
.spy();
1657 client
.addEventListener("bell", spy
);
1658 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1659 expect(spy
).to
.have
.been
.calledOnce
;
1662 it('should respond correctly to ServerFence', function () {
1663 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1664 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1666 var payload
= "foo\x00ab9";
1668 // ClientFence and ServerFence are identical in structure
1669 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1670 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1672 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1674 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1676 expected_msg
._sQlen
= 0;
1677 incoming_msg
._sQlen
= 0;
1679 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1680 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1682 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1684 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1687 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1688 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1690 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1692 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1694 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1696 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1697 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1700 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1701 client
._enabledContinuousUpdates
= true;
1702 client
._supportsContinuousUpdates
= true;
1704 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1706 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1709 it('should update continuous updates on resize', function () {
1710 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1711 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1713 client
._resize(450, 160);
1715 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1717 client
._enabledContinuousUpdates
= true;
1719 client
._resize(90, 700);
1721 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1724 it('should fail on an unknown message type', function () {
1725 sinon
.spy(client
, "_fail");
1726 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1727 expect(client
._fail
).to
.have
.been
.calledOnce
;
1731 describe('Asynchronous Events', function () {
1733 beforeEach(function () {
1734 client
= make_rfb();
1737 describe('Mouse event handlers', function () {
1738 it('should not send button messages in view-only mode', function () {
1739 client
._viewOnly
= true;
1740 sinon
.spy(client
._sock
, 'flush');
1741 client
._handleMouseButton(0, 0, 1, 0x001);
1742 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1745 it('should not send movement messages in view-only mode', function () {
1746 client
._viewOnly
= true;
1747 sinon
.spy(client
._sock
, 'flush');
1748 client
._handleMouseMove(0, 0);
1749 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1752 it('should send a pointer event on mouse button presses', function () {
1753 client
._handleMouseButton(10, 12, 1, 0x001);
1754 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1755 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1756 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1759 it('should send a mask of 1 on mousedown', function () {
1760 client
._handleMouseButton(10, 12, 1, 0x001);
1761 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1762 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1763 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1766 it('should send a mask of 0 on mouseup', function () {
1767 client
._mouse_buttonMask
= 0x001;
1768 client
._handleMouseButton(10, 12, 0, 0x001);
1769 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1770 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1771 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1774 it('should send a pointer event on mouse movement', function () {
1775 client
._handleMouseMove(10, 12);
1776 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1777 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1778 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1781 it('should set the button mask so that future mouse movements use it', function () {
1782 client
._handleMouseButton(10, 12, 1, 0x010);
1783 client
._handleMouseMove(13, 9);
1784 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1785 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1786 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1787 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1790 // NB(directxman12): we don't need to test not sending messages in
1791 // non-normal modes, since we haven't grabbed input
1792 // yet (grabbing input should be checked in the lifecycle tests).
1794 it('should not send movement messages when viewport dragging', function () {
1795 client
._viewportDragging
= true;
1796 client
._display
.viewportChangePos
= sinon
.spy();
1797 sinon
.spy(client
._sock
, 'flush');
1798 client
._handleMouseMove(13, 9);
1799 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1802 it('should not send button messages when initiating viewport dragging', function () {
1803 client
.dragViewport
= true;
1804 sinon
.spy(client
._sock
, 'flush');
1805 client
._handleMouseButton(13, 9, 0x001);
1806 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1809 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1810 client
.dragViewport
= true;
1811 client
._handleMouseButton(13, 9, 0x001);
1812 expect(client
._viewportDragging
).to
.be
.true;
1813 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1816 it('should terminate viewport dragging on a button up event, if enabled', function () {
1817 client
.dragViewport
= true;
1818 client
._viewportDragging
= true;
1819 client
._handleMouseButton(13, 9, 0x000);
1820 expect(client
._viewportDragging
).to
.be
.false;
1823 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1826 var newX
= 123 + 11 * window
.devicePixelRatio
;
1827 var newY
= 109 + 4 * window
.devicePixelRatio
;
1829 client
.dragViewport
= true;
1830 client
._viewportDragging
= true;
1831 client
._viewportHasMoved
= false;
1832 client
._viewportDragPos
= { x
: oldX
, y
: oldY
};
1833 client
._display
.viewportChangePos
= sinon
.spy();
1835 client
._handleMouseMove(newX
, newY
);
1837 expect(client
._viewportDragging
).to
.be
.true;
1838 expect(client
._viewportHasMoved
).to
.be
.true;
1839 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: newX
, y
: newY
});
1840 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
1841 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(oldX
- newX
, oldY
- newY
);
1845 describe('Keyboard Event Handlers', function () {
1846 it('should send a key message on a key press', function () {
1848 client
._handleKeyEvent(0x41, 'KeyA', true);
1849 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
1850 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
1851 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
1854 it('should not send messages in view-only mode', function () {
1855 client
._viewOnly
= true;
1856 sinon
.spy(client
._sock
, 'flush');
1857 client
._handleKeyEvent('a', 'KeyA', true);
1858 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1862 describe('WebSocket event handlers', function () {
1864 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
1865 client
._normal_msg
= sinon
.spy();
1866 client
._sock
._websocket
._receive_data(new Uint8Array([]));
1867 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
1870 it('should handle a message in the connected state as a normal message', function () {
1871 client
._normal_msg
= sinon
.spy();
1872 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1873 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
1876 it('should handle a message in any non-disconnected/failed state like an init message', function () {
1877 client
._rfb_connection_state
= 'connecting';
1878 client
._rfb_init_state
= 'ProtocolVersion';
1879 client
._init_msg
= sinon
.spy();
1880 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
1881 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
1884 it('should process all normal messages directly', function () {
1885 var spy
= sinon
.spy();
1886 client
.addEventListener("bell", spy
);
1887 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
1888 expect(spy
).to
.have
.been
.calledTwice
;
1892 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
1893 client
= new RFB(document
.createElement('canvas'), 'wss://host:8675');
1895 client
._sock
._websocket
._open();
1896 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
1899 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
1900 sinon
.spy(client
, "_fail");
1901 client
._rfb_connection_state
= 'some_other_state';
1902 client
._sock
._websocket
._open();
1903 expect(client
._fail
).to
.have
.been
.calledOnce
;
1907 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
1908 client
._rfb_connection_state
= 'disconnecting';
1909 client
._sock
._websocket
.close();
1910 expect(client
._rfb_connection_state
).to
.equal('disconnected');
1913 it('should fail if we get a close event while connecting', function () {
1914 sinon
.spy(client
, "_fail");
1915 client
._rfb_connection_state
= 'connecting';
1916 client
._sock
._websocket
.close();
1917 expect(client
._fail
).to
.have
.been
.calledOnce
;
1920 it('should fail if we get a close event while disconnected', function () {
1921 sinon
.spy(client
, "_fail");
1922 client
._rfb_connection_state
= 'disconnected';
1923 client
._sock
._websocket
.close();
1924 expect(client
._fail
).to
.have
.been
.calledOnce
;
1927 it('should unregister close event handler', function () {
1928 sinon
.spy(client
._sock
, 'off');
1929 client
._rfb_connection_state
= 'disconnecting';
1930 client
._sock
._websocket
.close();
1931 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
1934 // error events do nothing