1 var expect
= chai
.expect
;
3 import RFB
from '../core/rfb.js';
4 import Websock
from '../core/websock.js';
5 import { encodings
} from '../core/encodings.js';
7 import FakeWebSocket
from './fake.websocket.js';
8 import sinon
from '../vendor/sinon.js';
10 /* UIEvent constructor polyfill for IE */
12 if (typeof window
.UIEvent
=== "function") return;
14 function UIEvent ( event
, params
) {
15 params
= params
|| { bubbles
: false, cancelable
: false, view
: window
, detail
: undefined };
16 var evt
= document
.createEvent( 'UIEvent' );
17 evt
.initUIEvent( event
, params
.bubbles
, params
.cancelable
, params
.view
, params
.detail
);
21 UIEvent
.prototype = window
.UIEvent
.prototype;
23 window
.UIEvent
= UIEvent
;
26 var push8 = function (arr
, num
) {
31 var push16 = function (arr
, num
) {
33 arr
.push((num
>> 8) & 0xFF,
37 var push32 = function (arr
, num
) {
39 arr
.push((num
>> 24) & 0xFF,
45 describe('Remote Frame Buffer Protocol Client', function() {
49 before(FakeWebSocket
.replace
);
50 after(FakeWebSocket
.restore
);
53 this.clock
= clock
= sinon
.useFakeTimers();
54 // sinon doesn't support this yet
55 raf
= window
.requestAnimationFrame
;
56 window
.requestAnimationFrame
= setTimeout
;
57 // Use a single set of buffers instead of reallocating to
59 var sock
= new Websock();
60 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
61 var rQ
= new Uint8Array(sock
._rQbufferSize
);
63 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
64 Websock
.prototype._allocate_buffers = function () {
72 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
74 window
.requestAnimationFrame
= raf
;
80 beforeEach(function () {
81 // Create a container element for all RFB objects to attach to
82 container
= document
.createElement('div');
83 container
.style
.width
= "100%";
84 container
.style
.height
= "100%";
85 document
.body
.appendChild(container
);
87 // And track all created RFB objects
90 afterEach(function () {
91 // Make sure every created RFB object is properly cleaned up
92 // or they might affect subsequent tests
93 rfbs
.forEach(function (rfb
) {
95 expect(rfb
._disconnect
).to
.have
.been
.called
;
99 document
.body
.removeChild(container
);
103 function make_rfb (url
, options
) {
104 url
= url
|| 'wss://host:8675';
105 var rfb
= new RFB(container
, url
, options
);
107 rfb
._sock
._websocket
._open();
108 rfb
._rfb_connection_state
= 'connected';
109 sinon
.spy(rfb
, "_disconnect");
114 describe('Connecting/Disconnecting', function () {
115 describe('#RFB', function () {
116 it('should set the current state to "connecting"', function () {
117 var client
= new RFB(document
.createElement('div'), 'wss://host:8675');
118 client
._rfb_connection_state
= '';
120 expect(client
._rfb_connection_state
).to
.equal('connecting');
123 it('should actually connect to the websocket', function () {
124 var client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
125 sinon
.spy(client
._sock
, 'open');
127 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
128 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
132 describe('#disconnect', function () {
134 beforeEach(function () {
138 it('should go to state "disconnecting" before "disconnected"', function () {
139 sinon
.spy(client
, '_updateConnectionState');
141 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
142 expect(client
._updateConnectionState
.getCall(0).args
[0])
143 .to
.equal('disconnecting');
144 expect(client
._updateConnectionState
.getCall(1).args
[0])
145 .to
.equal('disconnected');
146 expect(client
._rfb_connection_state
).to
.equal('disconnected');
149 it('should unregister error event handler', function () {
150 sinon
.spy(client
._sock
, 'off');
152 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
155 it('should unregister message event handler', function () {
156 sinon
.spy(client
._sock
, 'off');
158 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
161 it('should unregister open event handler', function () {
162 sinon
.spy(client
._sock
, 'off');
164 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
168 describe('#sendCredentials', function () {
170 beforeEach(function () {
172 client
._rfb_connection_state
= 'connecting';
175 it('should set the rfb credentials properly"', function () {
176 client
.sendCredentials({ password
: 'pass' });
177 expect(client
._rfb_credentials
).to
.deep
.equal({ password
: 'pass' });
180 it('should call init_msg "soon"', function () {
181 client
._init_msg
= sinon
.spy();
182 client
.sendCredentials({ password
: 'pass' });
184 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
189 describe('Public API Basic Behavior', function () {
191 beforeEach(function () {
195 describe('#sendCtrlAlDel', function () {
196 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
197 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
198 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
199 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
200 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
201 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
202 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
203 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
205 client
.sendCtrlAltDel();
206 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
209 it('should not send the keys if we are not in a normal state', function () {
210 sinon
.spy(client
._sock
, 'flush');
211 client
._rfb_connection_state
= "connecting";
212 client
.sendCtrlAltDel();
213 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
216 it('should not send the keys if we are set as view_only', function () {
217 sinon
.spy(client
._sock
, 'flush');
218 client
._viewOnly
= true;
219 client
.sendCtrlAltDel();
220 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
224 describe('#sendKey', function () {
225 it('should send a single key with the given code and state (down = true)', function () {
226 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
227 RFB
.messages
.keyEvent(expected
, 123, 1);
228 client
.sendKey(123, 'Key123', true);
229 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
232 it('should send both a down and up event if the state is not specified', function () {
233 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
234 RFB
.messages
.keyEvent(expected
, 123, 1);
235 RFB
.messages
.keyEvent(expected
, 123, 0);
236 client
.sendKey(123, 'Key123');
237 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
240 it('should not send the key if we are not in a normal state', function () {
241 sinon
.spy(client
._sock
, 'flush');
242 client
._rfb_connection_state
= "connecting";
243 client
.sendKey(123, 'Key123');
244 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
247 it('should not send the key if we are set as view_only', function () {
248 sinon
.spy(client
._sock
, 'flush');
249 client
._viewOnly
= true;
250 client
.sendKey(123, 'Key123');
251 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
254 it('should send QEMU extended events if supported', function () {
255 client
._qemuExtKeyEventSupported
= true;
256 var expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
257 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
258 client
.sendKey(0x20, 'Space', true);
259 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
262 it('should not send QEMU extended events if unknown key code', function () {
263 client
._qemuExtKeyEventSupported
= true;
264 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
265 RFB
.messages
.keyEvent(expected
, 123, 1);
266 client
.sendKey(123, 'FooBar', true);
267 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
271 describe('#focus', function () {
272 it('should move focus to canvas object', function () {
273 client
._canvas
.focus
= sinon
.spy();
275 expect(client
._canvas
.focus
).to
.have
.been
.called
.once
;
279 describe('#blur', function () {
280 it('should remove focus from canvas object', function () {
281 client
._canvas
.blur
= sinon
.spy();
283 expect(client
._canvas
.blur
).to
.have
.been
.called
.once
;
287 describe('#clipboardPasteFrom', function () {
288 it('should send the given text in a paste event', function () {
289 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0,
290 _sQbufferSize
: 11, flush: function () {}};
291 RFB
.messages
.clientCutText(expected
, 'abc');
292 client
.clipboardPasteFrom('abc');
293 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
296 it('should flush multiple times for large clipboards', function () {
297 sinon
.spy(client
._sock
, 'flush');
299 for (let i
= 0; i
< client
._sock
._sQbufferSize
+ 100; i
++) {
302 client
.clipboardPasteFrom(long_text
);
303 expect(client
._sock
.flush
).to
.have
.been
.calledTwice
;
306 it('should not send the text if we are not in a normal state', function () {
307 sinon
.spy(client
._sock
, 'flush');
308 client
._rfb_connection_state
= "connecting";
309 client
.clipboardPasteFrom('abc');
310 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
314 describe("XVP operations", function () {
315 beforeEach(function () {
316 client
._rfb_xvp_ver
= 1;
319 it('should send the shutdown signal on #machineShutdown', function () {
320 client
.machineShutdown();
321 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
324 it('should send the reboot signal on #machineReboot', function () {
325 client
.machineReboot();
326 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
329 it('should send the reset signal on #machineReset', function () {
330 client
.machineReset();
331 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
334 it('should not send XVP operations with higher versions than we support', function () {
335 sinon
.spy(client
._sock
, 'flush');
337 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
342 describe('Clipping', function () {
344 beforeEach(function () {
346 container
.style
.width
= '70px';
347 container
.style
.height
= '80px';
348 client
.clipViewport
= true;
351 it('should update display clip state when changing the property', function () {
352 var spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
354 client
.clipViewport
= false;
355 expect(spy
.set).to
.have
.been
.calledOnce
;
356 expect(spy
.set).to
.have
.been
.calledWith(false);
359 client
.clipViewport
= true;
360 expect(spy
.set).to
.have
.been
.calledOnce
;
361 expect(spy
.set).to
.have
.been
.calledWith(true);
364 it('should update the viewport when the container size changes', function () {
365 sinon
.spy(client
._display
, "viewportChangeSize");
367 container
.style
.width
= '40px';
368 container
.style
.height
= '50px';
369 var event
= new UIEvent('resize');
370 window
.dispatchEvent(event
);
373 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledOnce
;
374 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(40, 50);
377 it('should update the viewport when the remote session resizes', function () {
378 // Simple ExtendedDesktopSize FBU message
379 var incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
380 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
381 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
383 0x00, 0x00, 0x00, 0x00 ];
385 sinon
.spy(client
._display
, "viewportChangeSize");
387 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
389 // FIXME: Display implicitly calls viewportChangeSize() when
390 // resizing the framebuffer, hence calledTwice.
391 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledTwice
;
392 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(70, 80);
395 it('should not update the viewport if not clipping', function () {
396 client
.clipViewport
= false;
397 sinon
.spy(client
._display
, "viewportChangeSize");
399 container
.style
.width
= '40px';
400 container
.style
.height
= '50px';
401 var event
= new UIEvent('resize');
402 window
.dispatchEvent(event
);
405 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
408 it('should not update the viewport if scaling', function () {
409 client
.scaleViewport
= true;
410 sinon
.spy(client
._display
, "viewportChangeSize");
412 container
.style
.width
= '40px';
413 container
.style
.height
= '50px';
414 var event
= new UIEvent('resize');
415 window
.dispatchEvent(event
);
418 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
421 describe('Dragging', function () {
422 beforeEach(function () {
423 client
.dragViewport
= true;
424 sinon
.spy(RFB
.messages
, "pointerEvent");
427 afterEach(function () {
428 RFB
.messages
.pointerEvent
.restore();
431 it('should not send button messages when initiating viewport dragging', function () {
432 client
._handleMouseButton(13, 9, 0x001);
433 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
436 it('should send button messages when release without movement', function () {
438 client
._handleMouseButton(13, 9, 0x001);
439 client
._handleMouseButton(13, 9, 0x000);
440 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
442 RFB
.messages
.pointerEvent
.reset();
445 client
._handleMouseButton(13, 9, 0x001);
446 client
._handleMouseMove(15, 14);
447 client
._handleMouseButton(15, 14, 0x000);
448 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
451 it('should send button message directly when drag is disabled', function () {
452 client
.dragViewport
= false;
453 client
._handleMouseButton(13, 9, 0x001);
454 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
457 it('should be initiate viewport dragging on sufficient movement', function () {
458 sinon
.spy(client
._display
, "viewportChangePos");
460 // Too small movement
462 client
._handleMouseButton(13, 9, 0x001);
463 client
._handleMouseMove(18, 9);
465 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
466 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
468 // Sufficient movement
470 client
._handleMouseMove(43, 9);
472 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
473 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
474 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(-30, 0);
476 client
._display
.viewportChangePos
.reset();
478 // Now a small movement should move right away
480 client
._handleMouseMove(43, 14);
482 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
483 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
484 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(0, -5);
487 it('should not send button messages when dragging ends', function () {
488 // First the movement
490 client
._handleMouseButton(13, 9, 0x001);
491 client
._handleMouseMove(43, 9);
492 client
._handleMouseButton(43, 9, 0x000);
494 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
497 it('should terminate viewport dragging on a button up event', function () {
498 // First the dragging movement
500 client
._handleMouseButton(13, 9, 0x001);
501 client
._handleMouseMove(43, 9);
502 client
._handleMouseButton(43, 9, 0x000);
504 // Another movement now should not move the viewport
506 sinon
.spy(client
._display
, "viewportChangePos");
508 client
._handleMouseMove(43, 59);
510 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
515 describe('Scaling', function () {
517 beforeEach(function () {
519 container
.style
.width
= '70px';
520 container
.style
.height
= '80px';
521 client
.scaleViewport
= true;
524 it('should update display scale factor when changing the property', function () {
525 var spy
= sinon
.spy(client
._display
, "scale", ["set"]);
526 sinon
.spy(client
._display
, "autoscale");
528 client
.scaleViewport
= false;
529 expect(spy
.set).to
.have
.been
.calledOnce
;
530 expect(spy
.set).to
.have
.been
.calledWith(1.0);
531 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
533 client
.scaleViewport
= true;
534 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
535 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
538 it('should update the clipping setting when changing the property', function () {
539 client
.clipViewport
= true;
541 var spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
543 client
.scaleViewport
= false;
544 expect(spy
.set).to
.have
.been
.calledOnce
;
545 expect(spy
.set).to
.have
.been
.calledWith(true);
549 client
.scaleViewport
= true;
550 expect(spy
.set).to
.have
.been
.calledOnce
;
551 expect(spy
.set).to
.have
.been
.calledWith(false);
554 it('should update the scaling when the container size changes', function () {
555 sinon
.spy(client
._display
, "autoscale");
557 container
.style
.width
= '40px';
558 container
.style
.height
= '50px';
559 var event
= new UIEvent('resize');
560 window
.dispatchEvent(event
);
563 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
564 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(40, 50);
567 it('should update the scaling when the remote session resizes', function () {
568 // Simple ExtendedDesktopSize FBU message
569 var incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
570 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
571 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
573 0x00, 0x00, 0x00, 0x00 ];
575 sinon
.spy(client
._display
, "autoscale");
577 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
579 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
580 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
583 it('should not update the display scale factor if not scaling', function () {
584 client
.scaleViewport
= false;
586 sinon
.spy(client
._display
, "autoscale");
588 container
.style
.width
= '40px';
589 container
.style
.height
= '50px';
590 var event
= new UIEvent('resize');
591 window
.dispatchEvent(event
);
594 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
598 describe('Remote resize', function () {
600 beforeEach(function () {
602 client
._supportsSetDesktopSize
= true;
603 client
.resizeSession
= true;
604 container
.style
.width
= '70px';
605 container
.style
.height
= '80px';
606 sinon
.spy(RFB
.messages
, "setDesktopSize");
609 afterEach(function () {
610 RFB
.messages
.setDesktopSize
.restore();
613 it('should only request a resize when turned on', function () {
614 client
.resizeSession
= false;
615 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
616 client
.resizeSession
= true;
617 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
620 it('should request a resize when initially connecting', function () {
621 // Simple ExtendedDesktopSize FBU message
622 var incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
623 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
624 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
625 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
626 0x00, 0x00, 0x00, 0x00 ];
628 // First message should trigger a resize
630 client
._supportsSetDesktopSize
= false;
632 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
634 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
635 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 70, 80, 0, 0);
637 RFB
.messages
.setDesktopSize
.reset();
639 // Second message should not trigger a resize
641 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
643 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
646 it('should request a resize when the container resizes', function () {
647 container
.style
.width
= '40px';
648 container
.style
.height
= '50px';
649 var event
= new UIEvent('resize');
650 window
.dispatchEvent(event
);
653 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
654 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
657 it('should not resize until the container size is stable', function () {
658 container
.style
.width
= '20px';
659 container
.style
.height
= '30px';
660 var event1
= new UIEvent('resize');
661 window
.dispatchEvent(event1
);
664 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
666 container
.style
.width
= '40px';
667 container
.style
.height
= '50px';
668 var event2
= new UIEvent('resize');
669 window
.dispatchEvent(event2
);
672 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
676 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
677 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
680 it('should not resize when resize is disabled', function () {
681 client
._resizeSession
= false;
683 container
.style
.width
= '40px';
684 container
.style
.height
= '50px';
685 var event
= new UIEvent('resize');
686 window
.dispatchEvent(event
);
689 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
692 it('should not resize when resize is not supported', function () {
693 client
._supportsSetDesktopSize
= false;
695 container
.style
.width
= '40px';
696 container
.style
.height
= '50px';
697 var event
= new UIEvent('resize');
698 window
.dispatchEvent(event
);
701 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
704 it('should not resize when in view only mode', function () {
705 client
._viewOnly
= true;
707 container
.style
.width
= '40px';
708 container
.style
.height
= '50px';
709 var event
= new UIEvent('resize');
710 window
.dispatchEvent(event
);
713 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
716 it('should not try to override a server resize', function () {
717 // Simple ExtendedDesktopSize FBU message
718 var incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
719 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
720 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
721 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
722 0x00, 0x00, 0x00, 0x00 ];
724 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
726 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
730 describe('Misc Internals', function () {
731 describe('#_updateConnectionState', function () {
733 beforeEach(function () {
737 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
738 var spy
= sinon
.spy();
739 client
._disconnTimer
= setTimeout(spy
, 50);
740 client
._rfb_connection_state
= 'connecting';
741 client
._updateConnectionState('connected');
743 expect(spy
).to
.not
.have
.been
.called
;
744 expect(client
._disconnTimer
).to
.be
.null;
747 it('should set the rfb_connection_state', function () {
748 client
._rfb_connection_state
= 'connecting';
749 client
._updateConnectionState('connected');
750 expect(client
._rfb_connection_state
).to
.equal('connected');
753 it('should not change the state when we are disconnected', function () {
755 expect(client
._rfb_connection_state
).to
.equal('disconnected');
756 client
._updateConnectionState('connecting');
757 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
760 it('should ignore state changes to the same state', function () {
761 var connectSpy
= sinon
.spy();
762 client
.addEventListener("connect", connectSpy
);
764 expect(client
._rfb_connection_state
).to
.equal('connected');
765 client
._updateConnectionState('connected');
766 expect(connectSpy
).to
.not
.have
.been
.called
;
770 var disconnectSpy
= sinon
.spy();
771 client
.addEventListener("disconnect", disconnectSpy
);
773 expect(client
._rfb_connection_state
).to
.equal('disconnected');
774 client
._updateConnectionState('disconnected');
775 expect(disconnectSpy
).to
.not
.have
.been
.called
;
778 it('should ignore illegal state changes', function () {
779 var spy
= sinon
.spy();
780 client
.addEventListener("disconnect", spy
);
781 client
._updateConnectionState('disconnected');
782 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
783 expect(spy
).to
.not
.have
.been
.called
;
787 describe('#_fail', function () {
789 beforeEach(function () {
793 it('should close the WebSocket connection', function () {
794 sinon
.spy(client
._sock
, 'close');
796 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
799 it('should transition to disconnected', function () {
800 sinon
.spy(client
, '_updateConnectionState');
802 this.clock
.tick(2000);
803 expect(client
._updateConnectionState
).to
.have
.been
.called
;
804 expect(client
._rfb_connection_state
).to
.equal('disconnected');
807 it('should set clean_disconnect variable', function () {
808 client
._rfb_clean_disconnect
= true;
809 client
._rfb_connection_state
= 'connected';
811 expect(client
._rfb_clean_disconnect
).to
.be
.false;
814 it('should result in disconnect event with clean set to false', function () {
815 client
._rfb_connection_state
= 'connected';
816 var spy
= sinon
.spy();
817 client
.addEventListener("disconnect", spy
);
819 this.clock
.tick(2000);
820 expect(spy
).to
.have
.been
.calledOnce
;
821 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
827 describe('Connection States', function () {
828 describe('connecting', function () {
829 it('should open the websocket connection', function () {
830 var client
= new RFB(document
.createElement('div'),
831 'ws://HOST:8675/PATH');
832 sinon
.spy(client
._sock
, 'open');
834 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
838 describe('connected', function () {
840 beforeEach(function () {
844 it('should result in a connect event if state becomes connected', function () {
845 var spy
= sinon
.spy();
846 client
.addEventListener("connect", spy
);
847 client
._rfb_connection_state
= 'connecting';
848 client
._updateConnectionState('connected');
849 expect(spy
).to
.have
.been
.calledOnce
;
852 it('should not result in a connect event if the state is not "connected"', function () {
853 var spy
= sinon
.spy();
854 client
.addEventListener("connect", spy
);
855 client
._sock
._websocket
.open = function () {}; // explicitly don't call onopen
856 client
._updateConnectionState('connecting');
857 expect(spy
).to
.not
.have
.been
.called
;
861 describe('disconnecting', function () {
863 beforeEach(function () {
867 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
868 sinon
.spy(client
, '_updateConnectionState');
869 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
870 client
._updateConnectionState('disconnecting');
871 this.clock
.tick(3 * 1000);
872 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
873 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
874 expect(client
._rfb_connection_state
).to
.equal("disconnected");
877 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
878 client
._updateConnectionState('disconnecting');
879 this.clock
.tick(3 * 1000 / 2);
880 client
._sock
._websocket
.close();
881 this.clock
.tick(3 * 1000 / 2 + 1);
882 expect(client
._rfb_connection_state
).to
.equal('disconnected');
885 it('should close the WebSocket connection', function () {
886 sinon
.spy(client
._sock
, 'close');
887 client
._updateConnectionState('disconnecting');
888 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
891 it('should not result in a disconnect event', function () {
892 var spy
= sinon
.spy();
893 client
.addEventListener("disconnect", spy
);
894 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
895 client
._updateConnectionState('disconnecting');
896 expect(spy
).to
.not
.have
.been
.called
;
900 describe('disconnected', function () {
902 beforeEach(function () {
903 client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
906 it('should result in a disconnect event if state becomes "disconnected"', function () {
907 var spy
= sinon
.spy();
908 client
.addEventListener("disconnect", spy
);
909 client
._rfb_connection_state
= 'disconnecting';
910 client
._updateConnectionState('disconnected');
911 expect(spy
).to
.have
.been
.calledOnce
;
912 expect(spy
.args
[0][0].detail
.clean
).to
.be
.true;
915 it('should result in a disconnect event without msg when no reason given', function () {
916 var spy
= sinon
.spy();
917 client
.addEventListener("disconnect", spy
);
918 client
._rfb_connection_state
= 'disconnecting';
919 client
._rfb_disconnect_reason
= "";
920 client
._updateConnectionState('disconnected');
921 expect(spy
).to
.have
.been
.calledOnce
;
922 expect(spy
.args
[0].length
).to
.equal(1);
927 describe('Protocol Initialization States', function () {
929 beforeEach(function () {
931 client
._rfb_connection_state
= 'connecting';
934 describe('ProtocolVersion', function () {
935 function send_ver (ver
, client
) {
936 var arr
= new Uint8Array(12);
937 for (var i
= 0; i
< ver
.length
; i
++) {
938 arr
[i
+4] = ver
.charCodeAt(i
);
940 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
942 client
._sock
._websocket
._receive_data(arr
);
945 describe('version parsing', function () {
946 it('should interpret version 003.003 as version 3.3', function () {
947 send_ver('003.003', client
);
948 expect(client
._rfb_version
).to
.equal(3.3);
951 it('should interpret version 003.006 as version 3.3', function () {
952 send_ver('003.006', client
);
953 expect(client
._rfb_version
).to
.equal(3.3);
956 it('should interpret version 003.889 as version 3.3', function () {
957 send_ver('003.889', client
);
958 expect(client
._rfb_version
).to
.equal(3.3);
961 it('should interpret version 003.007 as version 3.7', function () {
962 send_ver('003.007', client
);
963 expect(client
._rfb_version
).to
.equal(3.7);
966 it('should interpret version 003.008 as version 3.8', function () {
967 send_ver('003.008', client
);
968 expect(client
._rfb_version
).to
.equal(3.8);
971 it('should interpret version 004.000 as version 3.8', function () {
972 send_ver('004.000', client
);
973 expect(client
._rfb_version
).to
.equal(3.8);
976 it('should interpret version 004.001 as version 3.8', function () {
977 send_ver('004.001', client
);
978 expect(client
._rfb_version
).to
.equal(3.8);
981 it('should interpret version 005.000 as version 3.8', function () {
982 send_ver('005.000', client
);
983 expect(client
._rfb_version
).to
.equal(3.8);
986 it('should fail on an invalid version', function () {
987 sinon
.spy(client
, "_fail");
988 send_ver('002.000', client
);
989 expect(client
._fail
).to
.have
.been
.calledOnce
;
993 it('should send back the interpreted version', function () {
994 send_ver('004.000', client
);
996 var expected_str
= 'RFB 003.008\n';
998 for (var i
= 0; i
< expected_str
.length
; i
++) {
999 expected
[i
] = expected_str
.charCodeAt(i
);
1002 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1005 it('should transition to the Security state on successful negotiation', function () {
1006 send_ver('003.008', client
);
1007 expect(client
._rfb_init_state
).to
.equal('Security');
1010 describe('Repeater', function () {
1011 beforeEach(function () {
1012 client
= make_rfb('wss://host:8675', { repeaterID
: "12345" });
1013 client
._rfb_connection_state
= 'connecting';
1016 it('should interpret version 000.000 as a repeater', function () {
1017 send_ver('000.000', client
);
1018 expect(client
._rfb_version
).to
.equal(0);
1020 var sent_data
= client
._sock
._websocket
._get_sent_data();
1021 expect(new Uint8Array(sent_data
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
1022 expect(sent_data
).to
.have
.length(250);
1025 it('should handle two step repeater negotiation', function () {
1026 send_ver('000.000', client
);
1027 send_ver('003.008', client
);
1028 expect(client
._rfb_version
).to
.equal(3.8);
1033 describe('Security', function () {
1034 beforeEach(function () {
1035 client
._rfb_init_state
= 'Security';
1038 it('should simply receive the auth scheme when for versions < 3.7', function () {
1039 client
._rfb_version
= 3.6;
1040 var auth_scheme_raw
= [1, 2, 3, 4];
1041 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
1042 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
1043 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
1044 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
1047 it('should prefer no authentication is possible', function () {
1048 client
._rfb_version
= 3.7;
1049 var auth_schemes
= [2, 1, 3];
1050 client
._sock
._websocket
._receive_data(auth_schemes
);
1051 expect(client
._rfb_auth_scheme
).to
.equal(1);
1052 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
1055 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
1056 client
._rfb_version
= 3.7;
1057 var auth_schemes
= [2, 22, 16];
1058 client
._sock
._websocket
._receive_data(auth_schemes
);
1059 expect(client
._rfb_auth_scheme
).to
.equal(22);
1060 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
1063 it('should fail if there are no supported schemes for versions >= 3.7', function () {
1064 sinon
.spy(client
, "_fail");
1065 client
._rfb_version
= 3.7;
1066 var auth_schemes
= [1, 32];
1067 client
._sock
._websocket
._receive_data(auth_schemes
);
1068 expect(client
._fail
).to
.have
.been
.calledOnce
;
1071 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
1072 client
._rfb_version
= 3.7;
1073 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1074 sinon
.spy(client
, '_fail');
1075 client
._sock
._websocket
._receive_data(failure_data
);
1077 expect(client
._fail
).to
.have
.been
.calledOnce
;
1078 expect(client
._fail
).to
.have
.been
.calledWith(
1079 'Security negotiation failed on no security types (reason: whoops)');
1082 it('should transition to the Authentication state and continue on successful negotiation', function () {
1083 client
._rfb_version
= 3.7;
1084 var auth_schemes
= [1, 1];
1085 client
._negotiate_authentication
= sinon
.spy();
1086 client
._sock
._websocket
._receive_data(auth_schemes
);
1087 expect(client
._rfb_init_state
).to
.equal('Authentication');
1088 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
1092 describe('Authentication', function () {
1093 beforeEach(function () {
1094 client
._rfb_init_state
= 'Security';
1097 function send_security(type
, cl
) {
1098 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
1101 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
1102 client
._rfb_version
= 3.6;
1103 var err_msg
= "Whoopsies";
1104 var data
= [0, 0, 0, 0];
1105 var err_len
= err_msg
.length
;
1106 push32(data
, err_len
);
1107 for (var i
= 0; i
< err_len
; i
++) {
1108 data
.push(err_msg
.charCodeAt(i
));
1111 sinon
.spy(client
, '_fail');
1112 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1113 expect(client
._fail
).to
.have
.been
.calledWith(
1114 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
1117 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
1118 client
._rfb_version
= 3.8;
1119 send_security(1, client
);
1120 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1123 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
1124 client
._rfb_version
= 3.7;
1125 send_security(1, client
);
1126 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1129 it('should fail on an unknown auth scheme', function () {
1130 sinon
.spy(client
, "_fail");
1131 client
._rfb_version
= 3.8;
1132 send_security(57, client
);
1133 expect(client
._fail
).to
.have
.been
.calledOnce
;
1136 describe('VNC Authentication (type 2) Handler', function () {
1137 beforeEach(function () {
1138 client
._rfb_init_state
= 'Security';
1139 client
._rfb_version
= 3.8;
1142 it('should fire the credentialsrequired event if missing a password', function () {
1143 var spy
= sinon
.spy();
1144 client
.addEventListener("credentialsrequired", spy
);
1145 send_security(2, client
);
1148 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1149 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1151 expect(client
._rfb_credentials
).to
.be
.empty
;
1152 expect(spy
).to
.have
.been
.calledOnce
;
1153 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
1156 it('should encrypt the password with DES and then send it back', function () {
1157 client
._rfb_credentials
= { password
: 'passwd' };
1158 send_security(2, client
);
1159 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
1162 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1163 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1165 var des_pass
= RFB
.genDES('passwd', challenge
);
1166 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
1169 it('should transition to SecurityResult immediately after sending the password', function () {
1170 client
._rfb_credentials
= { password
: 'passwd' };
1171 send_security(2, client
);
1174 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1175 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1177 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1181 describe('XVP Authentication (type 22) Handler', function () {
1182 beforeEach(function () {
1183 client
._rfb_init_state
= 'Security';
1184 client
._rfb_version
= 3.8;
1187 it('should fall through to standard VNC authentication upon completion', function () {
1188 client
._rfb_credentials
= { username
: 'user',
1190 password
: 'password' };
1191 client
._negotiate_std_vnc_auth
= sinon
.spy();
1192 send_security(22, client
);
1193 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1196 it('should fire the credentialsrequired event if all credentials are missing', function() {
1197 var spy
= sinon
.spy();
1198 client
.addEventListener("credentialsrequired", spy
);
1199 client
._rfb_credentials
= {};
1200 send_security(22, client
);
1202 expect(client
._rfb_credentials
).to
.be
.empty
;
1203 expect(spy
).to
.have
.been
.calledOnce
;
1204 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1207 it('should fire the credentialsrequired event if some credentials are missing', function() {
1208 var spy
= sinon
.spy();
1209 client
.addEventListener("credentialsrequired", spy
);
1210 client
._rfb_credentials
= { username
: 'user',
1212 send_security(22, client
);
1214 expect(spy
).to
.have
.been
.calledOnce
;
1215 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1218 it('should send user and target separately', function () {
1219 client
._rfb_credentials
= { username
: 'user',
1221 password
: 'password' };
1222 client
._negotiate_std_vnc_auth
= sinon
.spy();
1224 send_security(22, client
);
1226 var expected
= [22, 4, 6]; // auth selection, len user, len target
1227 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
1229 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1233 describe('TightVNC Authentication (type 16) Handler', function () {
1234 beforeEach(function () {
1235 client
._rfb_init_state
= 'Security';
1236 client
._rfb_version
= 3.8;
1237 send_security(16, client
);
1238 client
._sock
._websocket
._get_sent_data(); // skip the security reply
1241 function send_num_str_pairs(pairs
, client
) {
1242 var pairs_len
= pairs
.length
;
1244 push32(data
, pairs_len
);
1246 for (var i
= 0; i
< pairs_len
; i
++) {
1247 push32(data
, pairs
[i
][0]);
1249 for (j
= 0; j
< 4; j
++) {
1250 data
.push(pairs
[i
][1].charCodeAt(j
));
1252 for (j
= 0; j
< 8; j
++) {
1253 data
.push(pairs
[i
][2].charCodeAt(j
));
1257 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1260 it('should skip tunnel negotiation if no tunnels are requested', function () {
1261 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1262 expect(client
._rfb_tightvnc
).to
.be
.true;
1265 it('should fail if no supported tunnels are listed', function () {
1266 sinon
.spy(client
, "_fail");
1267 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
1268 expect(client
._fail
).to
.have
.been
.calledOnce
;
1271 it('should choose the notunnel tunnel type', function () {
1272 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
1273 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1276 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
1277 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
1278 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
1279 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
1280 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1281 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1284 /*it('should attempt to use VNC auth over no auth when possible', function () {
1285 client._rfb_tightvnc = true;
1286 client._negotiate_std_vnc_auth = sinon.spy();
1287 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
1288 expect(client._sock).to.have.sent([0, 0, 0, 1]);
1289 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
1290 expect(client._rfb_auth_scheme).to.equal(2);
1291 });*/ // while this would make sense, the original code doesn't actually do this
1293 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
1294 client
._rfb_tightvnc
= true;
1295 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
1296 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1297 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1300 it('should accept VNC authentication and transition to that', function () {
1301 client
._rfb_tightvnc
= true;
1302 client
._negotiate_std_vnc_auth
= sinon
.spy();
1303 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
1304 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1305 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1306 expect(client
._rfb_auth_scheme
).to
.equal(2);
1309 it('should fail if there are no supported auth types', function () {
1310 sinon
.spy(client
, "_fail");
1311 client
._rfb_tightvnc
= true;
1312 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
1313 expect(client
._fail
).to
.have
.been
.calledOnce
;
1318 describe('SecurityResult', function () {
1319 beforeEach(function () {
1320 client
._rfb_init_state
= 'SecurityResult';
1323 it('should fall through to ServerInitialisation on a response code of 0', function () {
1324 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1325 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1328 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1329 client
._rfb_version
= 3.8;
1330 sinon
.spy(client
, '_fail');
1331 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1332 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1333 expect(client
._fail
).to
.have
.been
.calledWith(
1334 'Security negotiation failed on security result (reason: whoops)');
1337 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1338 sinon
.spy(client
, '_fail');
1339 client
._rfb_version
= 3.7;
1340 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
1341 expect(client
._fail
).to
.have
.been
.calledWith(
1342 'Security handshake failed');
1345 it('should result in securityfailure event when receiving a non zero status', function () {
1346 var spy
= sinon
.spy();
1347 client
.addEventListener("securityfailure", spy
);
1348 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
1349 expect(spy
).to
.have
.been
.calledOnce
;
1350 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1353 it('should include reason when provided in securityfailure event', function () {
1354 client
._rfb_version
= 3.8;
1355 var spy
= sinon
.spy();
1356 client
.addEventListener("securityfailure", spy
);
1357 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
1358 32, 102, 97, 105, 108, 117, 114, 101];
1359 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1360 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1361 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
1364 it('should not include reason when length is zero in securityfailure event', function () {
1365 client
._rfb_version
= 3.9;
1366 var spy
= sinon
.spy();
1367 client
.addEventListener("securityfailure", spy
);
1368 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 0];
1369 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1370 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1371 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1374 it('should not include reason in securityfailure event for version < 3.8', function () {
1375 client
._rfb_version
= 3.6;
1376 var spy
= sinon
.spy();
1377 client
.addEventListener("securityfailure", spy
);
1378 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
1379 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1380 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1384 describe('ClientInitialisation', function () {
1385 it('should transition to the ServerInitialisation state', function () {
1386 var client
= make_rfb();
1387 client
._rfb_connection_state
= 'connecting';
1388 client
._rfb_init_state
= 'SecurityResult';
1389 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1390 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1393 it('should send 1 if we are in shared mode', function () {
1394 var client
= make_rfb('wss://host:8675', { shared
: true });
1395 client
._rfb_connection_state
= 'connecting';
1396 client
._rfb_init_state
= 'SecurityResult';
1397 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1398 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1401 it('should send 0 if we are not in shared mode', function () {
1402 var client
= make_rfb('wss://host:8675', { shared
: false });
1403 client
._rfb_connection_state
= 'connecting';
1404 client
._rfb_init_state
= 'SecurityResult';
1405 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1406 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1410 describe('ServerInitialisation', function () {
1411 beforeEach(function () {
1412 client
._rfb_init_state
= 'ServerInitialisation';
1415 function send_server_init(opts
, client
) {
1416 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1417 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1418 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1419 for (var opt
in opts
) {
1420 full_opts
[opt
] = opts
[opt
];
1424 push16(data
, full_opts
.width
);
1425 push16(data
, full_opts
.height
);
1427 data
.push(full_opts
.bpp
);
1428 data
.push(full_opts
.depth
);
1429 data
.push(full_opts
.big_endian
);
1430 data
.push(full_opts
.true_color
);
1432 push16(data
, full_opts
.red_max
);
1433 push16(data
, full_opts
.green_max
);
1434 push16(data
, full_opts
.blue_max
);
1435 push8(data
, full_opts
.red_shift
);
1436 push8(data
, full_opts
.green_shift
);
1437 push8(data
, full_opts
.blue_shift
);
1444 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1447 push32(name_data
, full_opts
.name
.length
);
1448 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1449 name_data
.push(full_opts
.name
.charCodeAt(i
));
1451 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1454 it('should set the framebuffer width and height', function () {
1455 send_server_init({ width
: 32, height
: 84 }, client
);
1456 expect(client
._fb_width
).to
.equal(32);
1457 expect(client
._fb_height
).to
.equal(84);
1460 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1462 it('should set the framebuffer name and call the callback', function () {
1463 var spy
= sinon
.spy();
1464 client
.addEventListener("desktopname", spy
);
1465 send_server_init({ name
: 'some name' }, client
);
1467 expect(client
._fb_name
).to
.equal('some name');
1468 expect(spy
).to
.have
.been
.calledOnce
;
1469 expect(spy
.args
[0][0].detail
.name
).to
.equal('some name');
1472 it('should handle the extended init message of the tight encoding', function () {
1473 // NB(sross): we don't actually do anything with it, so just test that we can
1474 // read it w/o throwing an error
1475 client
._rfb_tightvnc
= true;
1476 send_server_init({}, client
);
1478 var tight_data
= [];
1479 push16(tight_data
, 1);
1480 push16(tight_data
, 2);
1481 push16(tight_data
, 3);
1482 push16(tight_data
, 0);
1483 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1486 client
._sock
._websocket
._receive_data(tight_data
);
1488 expect(client
._rfb_connection_state
).to
.equal('connected');
1491 it('should resize the display', function () {
1492 sinon
.spy(client
._display
, 'resize');
1493 send_server_init({ width
: 27, height
: 32 }, client
);
1495 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1496 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1499 it('should grab the mouse and keyboard', function () {
1500 sinon
.spy(client
._keyboard
, 'grab');
1501 sinon
.spy(client
._mouse
, 'grab');
1502 send_server_init({}, client
);
1503 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1504 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1507 describe('Initial Update Request', function () {
1508 beforeEach(function () {
1509 sinon
.spy(RFB
.messages
, "pixelFormat");
1510 sinon
.spy(RFB
.messages
, "clientEncodings");
1511 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1514 afterEach(function () {
1515 RFB
.messages
.pixelFormat
.restore();
1516 RFB
.messages
.clientEncodings
.restore();
1517 RFB
.messages
.fbUpdateRequest
.restore();
1520 // TODO(directxman12): test the various options in this configuration matrix
1521 it('should reply with the pixel format, client encodings, and initial update request', function () {
1522 send_server_init({ width
: 27, height
: 32 }, client
);
1524 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1525 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1526 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1527 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1528 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1529 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1530 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1531 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1534 it('should reply with restricted settings for Intel AMT servers', function () {
1535 send_server_init({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1537 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1538 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1539 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1540 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1541 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1542 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1543 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1544 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1545 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1549 it('should transition to the "connected" state', function () {
1550 send_server_init({}, client
);
1551 expect(client
._rfb_connection_state
).to
.equal('connected');
1556 describe('Protocol Message Processing After Completing Initialization', function () {
1559 beforeEach(function () {
1560 client
= make_rfb();
1561 client
._fb_name
= 'some device';
1562 client
._fb_width
= 640;
1563 client
._fb_height
= 20;
1566 describe('Framebuffer Update Handling', function () {
1567 var target_data_arr
= [
1568 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1569 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1570 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1571 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1575 var target_data_check_arr
= [
1576 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1577 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1578 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1579 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1581 var target_data_check
;
1583 before(function () {
1584 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1585 target_data
= new Uint8Array(target_data_arr
);
1586 target_data_check
= new Uint8Array(target_data_check_arr
);
1589 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1592 if (!rect_cnt
|| rect_cnt
> -1) {
1594 data
.push(0); // msg type
1595 data
.push(0); // padding
1596 push16(data
, rect_cnt
|| rect_data
.length
);
1599 for (var i
= 0; i
< rect_data
.length
; i
++) {
1601 push16(data
, rect_info
[i
].x
);
1602 push16(data
, rect_info
[i
].y
);
1603 push16(data
, rect_info
[i
].width
);
1604 push16(data
, rect_info
[i
].height
);
1605 push32(data
, rect_info
[i
].encoding
);
1607 data
= data
.concat(rect_data
[i
]);
1610 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1613 it('should send an update request if there is sufficient data', function () {
1614 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1615 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1617 client
._framebufferUpdate = function () { return true; };
1618 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1620 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1623 it('should not send an update request if we need more data', function () {
1624 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1625 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1628 it('should resume receiving an update if we previously did not have enough data', function () {
1629 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1630 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1632 // just enough to set FBU.rects
1633 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1634 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1636 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1637 // 247 should *not* be used as the message type here
1638 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1639 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1642 it('should not send a request in continuous updates mode', function () {
1643 client
._enabledContinuousUpdates
= true;
1644 client
._framebufferUpdate = function () { return true; };
1645 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1647 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1650 it('should fail on an unsupported encoding', function () {
1651 sinon
.spy(client
, "_fail");
1652 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1653 send_fbu_msg([rect_info
], [[]], client
);
1654 expect(client
._fail
).to
.have
.been
.calledOnce
;
1657 it('should be able to pause and resume receiving rects if not enought data', function () {
1658 // seed some initial data to copy
1659 client
._fb_width
= 4;
1660 client
._fb_height
= 4;
1661 client
._display
.resize(4, 4);
1662 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1664 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1665 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1666 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1667 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1668 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1669 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1670 expect(client
._display
).to
.have
.displayed(target_data_check
);
1673 describe('Message Encoding Handlers', function () {
1674 beforeEach(function () {
1675 // a really small frame
1676 client
._fb_width
= 4;
1677 client
._fb_height
= 4;
1678 client
._fb_depth
= 24;
1679 client
._display
.resize(4, 4);
1682 it('should handle the RAW encoding', function () {
1683 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1684 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1685 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1686 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1689 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1690 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1691 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1692 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1693 send_fbu_msg(info
, rects
, client
);
1694 expect(client
._display
).to
.have
.displayed(target_data
);
1697 it('should handle the RAW encoding in low colour mode', function () {
1698 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1699 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1700 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1701 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1703 [0x03, 0x03, 0x03, 0x03],
1704 [0x0c, 0x0c, 0x0c, 0x0c],
1705 [0x0c, 0x0c, 0x03, 0x03],
1706 [0x0c, 0x0c, 0x03, 0x03]];
1707 client
._fb_depth
= 8;
1708 send_fbu_msg(info
, rects
, client
);
1709 expect(client
._display
).to
.have
.displayed(target_data_check
);
1712 it('should handle the COPYRECT encoding', function () {
1713 // seed some initial data to copy
1714 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1716 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1717 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1718 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1719 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1720 send_fbu_msg(info
, rects
, client
);
1721 expect(client
._display
).to
.have
.displayed(target_data_check
);
1724 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1725 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1727 it('should handle the RRE encoding', function () {
1728 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1730 push32(rect
, 2); // 2 subrects
1731 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1732 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1736 push16(rect
, 0); // x: 0
1737 push16(rect
, 0); // y: 0
1738 push16(rect
, 2); // width: 2
1739 push16(rect
, 2); // height: 2
1740 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1744 push16(rect
, 2); // x: 2
1745 push16(rect
, 2); // y: 2
1746 push16(rect
, 2); // width: 2
1747 push16(rect
, 2); // height: 2
1749 send_fbu_msg(info
, [rect
], client
);
1750 expect(client
._display
).to
.have
.displayed(target_data_check
);
1753 describe('the HEXTILE encoding handler', function () {
1754 it('should handle a tile with fg, bg specified, normal subrects', function () {
1755 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1757 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1758 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1759 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1763 rect
.push(2); // 2 subrects
1764 rect
.push(0); // x: 0, y: 0
1765 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1766 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1767 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1768 send_fbu_msg(info
, [rect
], client
);
1769 expect(client
._display
).to
.have
.displayed(target_data_check
);
1772 it('should handle a raw tile', function () {
1773 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1775 rect
.push(0x01); // raw
1776 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1777 rect
.push(target_data
[i
+ 2]);
1778 rect
.push(target_data
[i
+ 1]);
1779 rect
.push(target_data
[i
]);
1780 rect
.push(target_data
[i
+ 3]);
1782 send_fbu_msg(info
, [rect
], client
);
1783 expect(client
._display
).to
.have
.displayed(target_data
);
1786 it('should handle a tile with only bg specified (solid bg)', function () {
1787 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1790 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1791 send_fbu_msg(info
, [rect
], client
);
1794 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1795 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1798 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1799 // set the width so we can have two tiles
1800 client
._fb_width
= 8;
1801 client
._display
.resize(8, 4);
1803 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1809 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1811 // send an empty frame
1814 send_fbu_msg(info
, [rect
], client
);
1818 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1819 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1820 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1823 it('should handle a tile with bg and coloured subrects', function () {
1824 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1826 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1827 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1828 rect
.push(2); // 2 subrects
1829 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1833 rect
.push(0); // x: 0, y: 0
1834 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1835 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1839 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1840 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1841 send_fbu_msg(info
, [rect
], client
);
1842 expect(client
._display
).to
.have
.displayed(target_data_check
);
1845 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1846 client
._fb_width
= 4;
1847 client
._fb_height
= 17;
1848 client
._display
.resize(4, 17);
1850 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1852 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1853 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1854 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1858 rect
.push(8); // 8 subrects
1860 for (i
= 0; i
< 4; i
++) {
1861 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1862 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1863 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1864 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1866 rect
.push(0x08); // anysubrects
1867 rect
.push(1); // 1 subrect
1868 rect
.push(0); // x: 0, y: 0
1869 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1870 send_fbu_msg(info
, [rect
], client
);
1873 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1874 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1875 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1878 it('should fail on an invalid subencoding', function () {
1879 sinon
.spy(client
,"_fail");
1880 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1881 var rects
= [[45]]; // an invalid subencoding
1882 send_fbu_msg(info
, rects
, client
);
1883 expect(client
._fail
).to
.have
.been
.calledOnce
;
1887 it
.skip('should handle the TIGHT encoding', function () {
1888 // TODO(directxman12): test this
1891 it
.skip('should handle the TIGHT_PNG encoding', function () {
1892 // TODO(directxman12): test this
1895 it('should handle the DesktopSize pseduo-encoding', function () {
1896 sinon
.spy(client
._display
, 'resize');
1897 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1899 expect(client
._fb_width
).to
.equal(20);
1900 expect(client
._fb_height
).to
.equal(50);
1902 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1903 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1906 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1907 beforeEach(function () {
1908 // a really small frame
1909 client
._fb_width
= 4;
1910 client
._fb_height
= 4;
1911 client
._display
.resize(4, 4);
1912 sinon
.spy(client
._display
, 'resize');
1915 function make_screen_data (nr_of_screens
) {
1917 push8(data
, nr_of_screens
); // number-of-screens
1918 push8(data
, 0); // padding
1919 push16(data
, 0); // padding
1920 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1921 push32(data
, 0); // id
1922 push16(data
, 0); // x-position
1923 push16(data
, 0); // y-position
1924 push16(data
, 20); // width
1925 push16(data
, 50); // height
1926 push32(data
, 0); // flags
1931 it('should handle a resize requested by this client', function () {
1932 var reason_for_change
= 1; // requested by this client
1933 var status_code
= 0; // No error
1935 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1936 width
: 20, height
: 50, encoding
: -308 }],
1937 make_screen_data(1), client
);
1939 expect(client
._fb_width
).to
.equal(20);
1940 expect(client
._fb_height
).to
.equal(50);
1942 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1943 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1946 it('should handle a resize requested by another client', function () {
1947 var reason_for_change
= 2; // requested by another client
1948 var status_code
= 0; // No error
1950 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1951 width
: 20, height
: 50, encoding
: -308 }],
1952 make_screen_data(1), client
);
1954 expect(client
._fb_width
).to
.equal(20);
1955 expect(client
._fb_height
).to
.equal(50);
1957 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1958 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1961 it('should be able to recieve requests which contain data for multiple screens', function () {
1962 var reason_for_change
= 2; // requested by another client
1963 var status_code
= 0; // No error
1965 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1966 width
: 60, height
: 50, encoding
: -308 }],
1967 make_screen_data(3), client
);
1969 expect(client
._fb_width
).to
.equal(60);
1970 expect(client
._fb_height
).to
.equal(50);
1972 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1973 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1976 it('should not handle a failed request', function () {
1977 var reason_for_change
= 1; // requested by this client
1978 var status_code
= 1; // Resize is administratively prohibited
1980 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1981 width
: 20, height
: 50, encoding
: -308 }],
1982 make_screen_data(1), client
);
1984 expect(client
._fb_width
).to
.equal(4);
1985 expect(client
._fb_height
).to
.equal(4);
1987 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1991 it
.skip('should handle the Cursor pseudo-encoding', function () {
1992 // TODO(directxman12): test
1995 it('should handle the last_rect pseudo-encoding', function () {
1996 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1997 expect(client
._FBU
.rects
).to
.equal(0);
2002 describe('XVP Message Handling', function () {
2003 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
2004 var spy
= sinon
.spy();
2005 client
.addEventListener("capabilities", spy
);
2006 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
2007 expect(client
._rfb_xvp_ver
).to
.equal(10);
2008 expect(spy
).to
.have
.been
.calledOnce
;
2009 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
2010 expect(client
.capabilities
.power
).to
.be
.true;
2013 it('should fail on unknown XVP message types', function () {
2014 sinon
.spy(client
, "_fail");
2015 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
2016 expect(client
._fail
).to
.have
.been
.calledOnce
;
2020 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
2021 var expected_str
= 'cheese!';
2022 var data
= [3, 0, 0, 0];
2023 push32(data
, expected_str
.length
);
2024 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
2025 var spy
= sinon
.spy();
2026 client
.addEventListener("clipboard", spy
);
2028 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
2029 expect(spy
).to
.have
.been
.calledOnce
;
2030 expect(spy
.args
[0][0].detail
.text
).to
.equal(expected_str
);
2033 it('should fire the bell callback on Bell', function () {
2034 var spy
= sinon
.spy();
2035 client
.addEventListener("bell", spy
);
2036 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
2037 expect(spy
).to
.have
.been
.calledOnce
;
2040 it('should respond correctly to ServerFence', function () {
2041 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
2042 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
2044 var payload
= "foo\x00ab9";
2046 // ClientFence and ServerFence are identical in structure
2047 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
2048 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
2050 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
2052 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2054 expected_msg
._sQlen
= 0;
2055 incoming_msg
._sQlen
= 0;
2057 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
2058 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
2060 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
2062 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2065 it('should enable continuous updates on first EndOfContinousUpdates', function () {
2066 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
2068 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
2070 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2072 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
2074 expect(client
._enabledContinuousUpdates
).to
.be
.true;
2075 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2078 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
2079 client
._enabledContinuousUpdates
= true;
2080 client
._supportsContinuousUpdates
= true;
2082 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
2084 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2087 it('should update continuous updates on resize', function () {
2088 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
2089 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
2091 client
._resize(450, 160);
2093 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
2095 client
._enabledContinuousUpdates
= true;
2097 client
._resize(90, 700);
2099 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2102 it('should fail on an unknown message type', function () {
2103 sinon
.spy(client
, "_fail");
2104 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
2105 expect(client
._fail
).to
.have
.been
.calledOnce
;
2109 describe('Asynchronous Events', function () {
2111 beforeEach(function () {
2112 client
= make_rfb();
2115 describe('Mouse event handlers', function () {
2116 it('should not send button messages in view-only mode', function () {
2117 client
._viewOnly
= true;
2118 sinon
.spy(client
._sock
, 'flush');
2119 client
._handleMouseButton(0, 0, 1, 0x001);
2120 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2123 it('should not send movement messages in view-only mode', function () {
2124 client
._viewOnly
= true;
2125 sinon
.spy(client
._sock
, 'flush');
2126 client
._handleMouseMove(0, 0);
2127 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2130 it('should send a pointer event on mouse button presses', function () {
2131 client
._handleMouseButton(10, 12, 1, 0x001);
2132 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
2133 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
2134 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2137 it('should send a mask of 1 on mousedown', function () {
2138 client
._handleMouseButton(10, 12, 1, 0x001);
2139 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
2140 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
2141 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2144 it('should send a mask of 0 on mouseup', function () {
2145 client
._mouse_buttonMask
= 0x001;
2146 client
._handleMouseButton(10, 12, 0, 0x001);
2147 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
2148 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
2149 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2152 it('should send a pointer event on mouse movement', function () {
2153 client
._handleMouseMove(10, 12);
2154 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
2155 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
2156 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2159 it('should set the button mask so that future mouse movements use it', function () {
2160 client
._handleMouseButton(10, 12, 1, 0x010);
2161 client
._handleMouseMove(13, 9);
2162 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
2163 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
2164 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
2165 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2169 describe('Keyboard Event Handlers', function () {
2170 it('should send a key message on a key press', function () {
2171 client
._handleKeyEvent(0x41, 'KeyA', true);
2172 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
2173 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
2174 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
2177 it('should not send messages in view-only mode', function () {
2178 client
._viewOnly
= true;
2179 sinon
.spy(client
._sock
, 'flush');
2180 client
._handleKeyEvent('a', 'KeyA', true);
2181 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2185 describe('WebSocket event handlers', function () {
2187 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
2188 client
._normal_msg
= sinon
.spy();
2189 client
._sock
._websocket
._receive_data(new Uint8Array([]));
2190 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
2193 it('should handle a message in the connected state as a normal message', function () {
2194 client
._normal_msg
= sinon
.spy();
2195 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2196 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
2199 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2200 client
._rfb_connection_state
= 'connecting';
2201 client
._rfb_init_state
= 'ProtocolVersion';
2202 client
._init_msg
= sinon
.spy();
2203 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2204 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
2207 it('should process all normal messages directly', function () {
2208 var spy
= sinon
.spy();
2209 client
.addEventListener("bell", spy
);
2210 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2211 expect(spy
).to
.have
.been
.calledTwice
;
2215 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2216 client
= new RFB(document
.createElement('div'), 'wss://host:8675');
2218 client
._sock
._websocket
._open();
2219 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2222 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2223 sinon
.spy(client
, "_fail");
2224 client
._rfb_connection_state
= 'connected';
2225 client
._sock
._websocket
._open();
2226 expect(client
._fail
).to
.have
.been
.calledOnce
;
2230 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2231 var real
= client
._sock
._websocket
.close
;
2232 client
._sock
._websocket
.close = function () {};
2233 client
.disconnect();
2234 expect(client
._rfb_connection_state
).to
.equal('disconnecting');
2235 client
._sock
._websocket
.close
= real
;
2236 client
._sock
._websocket
.close();
2237 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2240 it('should fail if we get a close event while connecting', function () {
2241 sinon
.spy(client
, "_fail");
2242 client
._rfb_connection_state
= 'connecting';
2243 client
._sock
._websocket
.close();
2244 expect(client
._fail
).to
.have
.been
.calledOnce
;
2247 it('should unregister close event handler', function () {
2248 sinon
.spy(client
._sock
, 'off');
2249 client
.disconnect();
2250 client
._sock
._websocket
.close();
2251 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2254 // error events do nothing