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 /* UIEvent constructor polyfill for IE */
14 if (typeof window
.UIEvent
=== "function") return;
16 function UIEvent ( event
, params
) {
17 params
= params
|| { bubbles
: false, cancelable
: false, view
: window
, detail
: undefined };
18 var evt
= document
.createEvent( 'UIEvent' );
19 evt
.initUIEvent( event
, params
.bubbles
, params
.cancelable
, params
.view
, params
.detail
);
23 UIEvent
.prototype = window
.UIEvent
.prototype;
25 window
.UIEvent
= UIEvent
;
28 var push8 = function (arr
, num
) {
33 var push16 = function (arr
, num
) {
35 arr
.push((num
>> 8) & 0xFF,
39 var push32 = function (arr
, num
) {
41 arr
.push((num
>> 24) & 0xFF,
47 describe('Remote Frame Buffer Protocol Client', function() {
51 before(FakeWebSocket
.replace
);
52 after(FakeWebSocket
.restore
);
55 this.clock
= clock
= sinon
.useFakeTimers();
56 // sinon doesn't support this yet
57 raf
= window
.requestAnimationFrame
;
58 window
.requestAnimationFrame
= setTimeout
;
59 // Use a single set of buffers instead of reallocating to
61 var sock
= new Websock();
62 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
63 var rQ
= new Uint8Array(sock
._rQbufferSize
);
65 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
66 Websock
.prototype._allocate_buffers = function () {
74 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
76 window
.requestAnimationFrame
= raf
;
82 beforeEach(function () {
83 // Create a container element for all RFB objects to attach to
84 container
= document
.createElement('div');
85 container
.style
.width
= "100%";
86 container
.style
.height
= "100%";
87 document
.body
.appendChild(container
);
89 // And track all created RFB objects
92 afterEach(function () {
93 // Make sure every created RFB object is properly cleaned up
94 // or they might affect subsequent tests
95 rfbs
.forEach(function (rfb
) {
97 expect(rfb
._disconnect
).to
.have
.been
.called
;
101 document
.body
.removeChild(container
);
105 function make_rfb (url
, options
) {
106 url
= url
|| 'wss://host:8675';
107 var rfb
= new RFB(container
, url
, options
);
109 rfb
._sock
._websocket
._open();
110 rfb
._rfb_connection_state
= 'connected';
111 sinon
.spy(rfb
, "_disconnect");
116 describe('Connecting/Disconnecting', function () {
117 describe('#RFB', function () {
118 it('should set the current state to "connecting"', function () {
119 var client
= new RFB(document
.createElement('div'), 'wss://host:8675');
120 client
._rfb_connection_state
= '';
122 expect(client
._rfb_connection_state
).to
.equal('connecting');
125 it('should actually connect to the websocket', function () {
126 var client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
127 sinon
.spy(client
._sock
, 'open');
129 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
130 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
134 describe('#disconnect', function () {
136 beforeEach(function () {
140 it('should go to state "disconnecting" before "disconnected"', function () {
141 sinon
.spy(client
, '_updateConnectionState');
143 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
144 expect(client
._updateConnectionState
.getCall(0).args
[0])
145 .to
.equal('disconnecting');
146 expect(client
._updateConnectionState
.getCall(1).args
[0])
147 .to
.equal('disconnected');
148 expect(client
._rfb_connection_state
).to
.equal('disconnected');
151 it('should unregister error event handler', function () {
152 sinon
.spy(client
._sock
, 'off');
154 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
157 it('should unregister message event handler', function () {
158 sinon
.spy(client
._sock
, 'off');
160 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
163 it('should unregister open event handler', function () {
164 sinon
.spy(client
._sock
, 'off');
166 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
170 describe('#sendCredentials', function () {
172 beforeEach(function () {
174 client
._rfb_connection_state
= 'connecting';
177 it('should set the rfb credentials properly"', function () {
178 client
.sendCredentials({ password
: 'pass' });
179 expect(client
._rfb_credentials
).to
.deep
.equal({ password
: 'pass' });
182 it('should call init_msg "soon"', function () {
183 client
._init_msg
= sinon
.spy();
184 client
.sendCredentials({ password
: 'pass' });
186 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
191 describe('Public API Basic Behavior', function () {
193 beforeEach(function () {
197 describe('#sendCtrlAlDel', function () {
198 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
199 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
200 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
201 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
202 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
203 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
204 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
205 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
207 client
.sendCtrlAltDel();
208 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
211 it('should not send the keys if we are not in a normal state', function () {
212 sinon
.spy(client
._sock
, 'flush');
213 client
._rfb_connection_state
= "connecting";
214 client
.sendCtrlAltDel();
215 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
218 it('should not send the keys if we are set as view_only', function () {
219 sinon
.spy(client
._sock
, 'flush');
220 client
._viewOnly
= true;
221 client
.sendCtrlAltDel();
222 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
226 describe('#sendKey', function () {
227 it('should send a single key with the given code and state (down = true)', function () {
228 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
229 RFB
.messages
.keyEvent(expected
, 123, 1);
230 client
.sendKey(123, 'Key123', true);
231 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
234 it('should send both a down and up event if the state is not specified', function () {
235 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
236 RFB
.messages
.keyEvent(expected
, 123, 1);
237 RFB
.messages
.keyEvent(expected
, 123, 0);
238 client
.sendKey(123, 'Key123');
239 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
242 it('should not send the key if we are not in a normal state', function () {
243 sinon
.spy(client
._sock
, 'flush');
244 client
._rfb_connection_state
= "connecting";
245 client
.sendKey(123, 'Key123');
246 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
249 it('should not send the key if we are set as view_only', function () {
250 sinon
.spy(client
._sock
, 'flush');
251 client
._viewOnly
= true;
252 client
.sendKey(123, 'Key123');
253 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
256 it('should send QEMU extended events if supported', function () {
257 client
._qemuExtKeyEventSupported
= true;
258 var expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
259 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
260 client
.sendKey(0x20, 'Space', true);
261 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
264 it('should not send QEMU extended events if unknown key code', function () {
265 client
._qemuExtKeyEventSupported
= true;
266 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
267 RFB
.messages
.keyEvent(expected
, 123, 1);
268 client
.sendKey(123, 'FooBar', true);
269 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
273 describe('#focus', function () {
274 it('should move focus to canvas object', function () {
275 client
._canvas
.focus
= sinon
.spy();
277 expect(client
._canvas
.focus
).to
.have
.been
.called
.once
;
281 describe('#blur', function () {
282 it('should remove focus from canvas object', function () {
283 client
._canvas
.blur
= sinon
.spy();
285 expect(client
._canvas
.blur
).to
.have
.been
.called
.once
;
289 describe('#clipboardPasteFrom', function () {
290 it('should send the given text in a paste event', function () {
291 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
292 RFB
.messages
.clientCutText(expected
, 'abc');
293 client
.clipboardPasteFrom('abc');
294 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
297 it('should not send the text if we are not in a normal state', function () {
298 sinon
.spy(client
._sock
, 'flush');
299 client
._rfb_connection_state
= "connecting";
300 client
.clipboardPasteFrom('abc');
301 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
305 describe("XVP operations", function () {
306 beforeEach(function () {
307 client
._rfb_xvp_ver
= 1;
310 it('should send the shutdown signal on #machineShutdown', function () {
311 client
.machineShutdown();
312 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
315 it('should send the reboot signal on #machineReboot', function () {
316 client
.machineReboot();
317 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
320 it('should send the reset signal on #machineReset', function () {
321 client
.machineReset();
322 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
325 it('should not send XVP operations with higher versions than we support', function () {
326 sinon
.spy(client
._sock
, 'flush');
328 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
333 describe('Clipping', function () {
335 beforeEach(function () {
337 container
.style
.width
= '70px';
338 container
.style
.height
= '80px';
339 client
.clipViewport
= true;
342 it('should update display clip state when changing the property', function () {
343 var spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
345 client
.clipViewport
= false;
346 expect(spy
.set).to
.have
.been
.calledOnce
;
347 expect(spy
.set).to
.have
.been
.calledWith(false);
350 client
.clipViewport
= true;
351 expect(spy
.set).to
.have
.been
.calledOnce
;
352 expect(spy
.set).to
.have
.been
.calledWith(true);
355 it('should update the viewport when the container size changes', function () {
356 sinon
.spy(client
._display
, "viewportChangeSize");
358 container
.style
.width
= '40px';
359 container
.style
.height
= '50px';
360 var event
= new UIEvent('resize');
361 window
.dispatchEvent(event
);
364 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledOnce
;
365 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(40, 50);
368 it('should update the viewport when the remote session resizes', function () {
369 // Simple ExtendedDesktopSize FBU message
370 var incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
371 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
372 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
373 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
374 0x00, 0x00, 0x00, 0x00 ];
376 sinon
.spy(client
._display
, "viewportChangeSize");
378 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
380 // FIXME: Display implicitly calls viewportChangeSize() when
381 // resizing the framebuffer, hence calledTwice.
382 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledTwice
;
383 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(70, 80);
386 it('should not update the viewport if not clipping', function () {
387 client
.clipViewport
= false;
388 sinon
.spy(client
._display
, "viewportChangeSize");
390 container
.style
.width
= '40px';
391 container
.style
.height
= '50px';
392 var event
= new UIEvent('resize');
393 window
.dispatchEvent(event
);
396 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
399 it('should not update the viewport if scaling', function () {
400 client
.scaleViewport
= true;
401 sinon
.spy(client
._display
, "viewportChangeSize");
403 container
.style
.width
= '40px';
404 container
.style
.height
= '50px';
405 var event
= new UIEvent('resize');
406 window
.dispatchEvent(event
);
409 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
412 describe('Dragging', function () {
413 beforeEach(function () {
414 client
.dragViewport
= true;
415 sinon
.spy(RFB
.messages
, "pointerEvent");
418 afterEach(function () {
419 RFB
.messages
.pointerEvent
.restore();
422 it('should not send button messages when initiating viewport dragging', function () {
423 client
._handleMouseButton(13, 9, 0x001);
424 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
427 it('should send button messages when release without movement', function () {
429 client
._handleMouseButton(13, 9, 0x001);
430 client
._handleMouseButton(13, 9, 0x000);
431 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
433 RFB
.messages
.pointerEvent
.reset();
436 client
._handleMouseButton(13, 9, 0x001);
437 client
._handleMouseMove(15, 14);
438 client
._handleMouseButton(15, 14, 0x000);
439 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
442 it('should send button message directly when drag is disabled', function () {
443 client
.dragViewport
= false;
444 client
._handleMouseButton(13, 9, 0x001);
445 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
448 it('should be initiate viewport dragging on sufficient movement', function () {
449 sinon
.spy(client
._display
, "viewportChangePos");
451 // Too small movement
453 client
._handleMouseButton(13, 9, 0x001);
454 client
._handleMouseMove(18, 9);
456 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
457 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
459 // Sufficient movement
461 client
._handleMouseMove(43, 9);
463 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
464 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
465 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(-30, 0);
467 client
._display
.viewportChangePos
.reset();
469 // Now a small movement should move right away
471 client
._handleMouseMove(43, 14);
473 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
474 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
475 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(0, -5);
478 it('should not send button messages when dragging ends', function () {
479 // First the movement
481 client
._handleMouseButton(13, 9, 0x001);
482 client
._handleMouseMove(43, 9);
483 client
._handleMouseButton(43, 9, 0x000);
485 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
488 it('should terminate viewport dragging on a button up event', function () {
489 // First the dragging movement
491 client
._handleMouseButton(13, 9, 0x001);
492 client
._handleMouseMove(43, 9);
493 client
._handleMouseButton(43, 9, 0x000);
495 // Another movement now should not move the viewport
497 sinon
.spy(client
._display
, "viewportChangePos");
499 client
._handleMouseMove(43, 59);
501 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
506 describe('Scaling', function () {
508 beforeEach(function () {
510 container
.style
.width
= '70px';
511 container
.style
.height
= '80px';
512 client
.scaleViewport
= true;
515 it('should update display scale factor when changing the property', function () {
516 var spy
= sinon
.spy(client
._display
, "scale", ["set"]);
517 sinon
.spy(client
._display
, "autoscale");
519 client
.scaleViewport
= false;
520 expect(spy
.set).to
.have
.been
.calledOnce
;
521 expect(spy
.set).to
.have
.been
.calledWith(1.0);
522 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
524 client
.scaleViewport
= true;
525 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
526 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
529 it('should update the clipping setting when changing the property', function () {
530 client
.clipViewport
= true;
532 var spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
534 client
.scaleViewport
= false;
535 expect(spy
.set).to
.have
.been
.calledOnce
;
536 expect(spy
.set).to
.have
.been
.calledWith(true);
540 client
.scaleViewport
= true;
541 expect(spy
.set).to
.have
.been
.calledOnce
;
542 expect(spy
.set).to
.have
.been
.calledWith(false);
545 it('should update the scaling when the container size changes', function () {
546 sinon
.spy(client
._display
, "autoscale");
548 container
.style
.width
= '40px';
549 container
.style
.height
= '50px';
550 var event
= new UIEvent('resize');
551 window
.dispatchEvent(event
);
554 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
555 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(40, 50);
558 it('should update the scaling when the remote session resizes', function () {
559 // Simple ExtendedDesktopSize FBU message
560 var incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
561 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
562 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
563 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
564 0x00, 0x00, 0x00, 0x00 ];
566 sinon
.spy(client
._display
, "autoscale");
568 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
570 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
571 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
574 it('should not update the display scale factor if not scaling', function () {
575 client
.scaleViewport
= false;
577 sinon
.spy(client
._display
, "autoscale");
579 container
.style
.width
= '40px';
580 container
.style
.height
= '50px';
581 var event
= new UIEvent('resize');
582 window
.dispatchEvent(event
);
585 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
589 describe('Remote resize', function () {
591 beforeEach(function () {
593 client
._supportsSetDesktopSize
= true;
594 client
.resizeSession
= true;
595 container
.style
.width
= '70px';
596 container
.style
.height
= '80px';
597 sinon
.spy(RFB
.messages
, "setDesktopSize");
600 afterEach(function () {
601 RFB
.messages
.setDesktopSize
.restore();
604 it('should only request a resize when turned on', function () {
605 client
.resizeSession
= false;
606 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
607 client
.resizeSession
= true;
608 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
611 it('should request a resize when initially connecting', function () {
612 // Simple ExtendedDesktopSize FBU message
613 var incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
614 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
615 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
616 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
617 0x00, 0x00, 0x00, 0x00 ];
619 // First message should trigger a resize
621 client
._supportsSetDesktopSize
= false;
623 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
625 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
626 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 70, 80, 0, 0);
628 RFB
.messages
.setDesktopSize
.reset();
630 // Second message should not trigger a resize
632 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
634 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
637 it('should request a resize when the container resizes', function () {
638 container
.style
.width
= '40px';
639 container
.style
.height
= '50px';
640 var event
= new UIEvent('resize');
641 window
.dispatchEvent(event
);
644 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
645 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
648 it('should not resize until the container size is stable', function () {
649 container
.style
.width
= '20px';
650 container
.style
.height
= '30px';
651 var event
= new UIEvent('resize');
652 window
.dispatchEvent(event
);
655 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
657 container
.style
.width
= '40px';
658 container
.style
.height
= '50px';
659 var event
= new UIEvent('resize');
660 window
.dispatchEvent(event
);
663 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
667 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
668 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
671 it('should not resize when resize is disabled', function () {
672 client
._resizeSession
= false;
674 container
.style
.width
= '40px';
675 container
.style
.height
= '50px';
676 var event
= new UIEvent('resize');
677 window
.dispatchEvent(event
);
680 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
683 it('should not resize when resize is not supported', function () {
684 client
._supportsSetDesktopSize
= false;
686 container
.style
.width
= '40px';
687 container
.style
.height
= '50px';
688 var event
= new UIEvent('resize');
689 window
.dispatchEvent(event
);
692 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
695 it('should not resize when in view only mode', function () {
696 client
._viewOnly
= true;
698 container
.style
.width
= '40px';
699 container
.style
.height
= '50px';
700 var event
= new UIEvent('resize');
701 window
.dispatchEvent(event
);
704 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
707 it('should not try to override a server resize', function () {
708 // Simple ExtendedDesktopSize FBU message
709 var incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
710 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
711 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
712 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
713 0x00, 0x00, 0x00, 0x00 ];
715 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
717 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
721 describe('Misc Internals', function () {
722 describe('#_updateConnectionState', function () {
724 beforeEach(function () {
728 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
729 var spy
= sinon
.spy();
730 client
._disconnTimer
= setTimeout(spy
, 50);
731 client
._rfb_connection_state
= 'connecting';
732 client
._updateConnectionState('connected');
734 expect(spy
).to
.not
.have
.been
.called
;
735 expect(client
._disconnTimer
).to
.be
.null;
738 it('should set the rfb_connection_state', function () {
739 client
._rfb_connection_state
= 'connecting';
740 client
._updateConnectionState('connected');
741 expect(client
._rfb_connection_state
).to
.equal('connected');
744 it('should not change the state when we are disconnected', function () {
746 expect(client
._rfb_connection_state
).to
.equal('disconnected');
747 client
._updateConnectionState('connecting');
748 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
751 it('should ignore state changes to the same state', function () {
752 var connectSpy
= sinon
.spy();
753 client
.addEventListener("connect", connectSpy
);
755 expect(client
._rfb_connection_state
).to
.equal('connected');
756 client
._updateConnectionState('connected');
757 expect(connectSpy
).to
.not
.have
.been
.called
;
761 var disconnectSpy
= sinon
.spy();
762 client
.addEventListener("disconnect", disconnectSpy
);
764 expect(client
._rfb_connection_state
).to
.equal('disconnected');
765 client
._updateConnectionState('disconnected');
766 expect(disconnectSpy
).to
.not
.have
.been
.called
;
769 it('should ignore illegal state changes', function () {
770 var spy
= sinon
.spy();
771 client
.addEventListener("disconnect", spy
);
772 client
._updateConnectionState('disconnected');
773 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
774 expect(spy
).to
.not
.have
.been
.called
;
778 describe('#_fail', function () {
780 beforeEach(function () {
784 it('should close the WebSocket connection', function () {
785 sinon
.spy(client
._sock
, 'close');
787 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
790 it('should transition to disconnected', function () {
791 sinon
.spy(client
, '_updateConnectionState');
793 this.clock
.tick(2000);
794 expect(client
._updateConnectionState
).to
.have
.been
.called
;
795 expect(client
._rfb_connection_state
).to
.equal('disconnected');
798 it('should set clean_disconnect variable', function () {
799 client
._rfb_clean_disconnect
= true;
800 client
._rfb_connection_state
= 'connected';
802 expect(client
._rfb_clean_disconnect
).to
.be
.false;
805 it('should result in disconnect event with clean set to false', function () {
806 client
._rfb_connection_state
= 'connected';
807 var spy
= sinon
.spy();
808 client
.addEventListener("disconnect", spy
);
810 this.clock
.tick(2000);
811 expect(spy
).to
.have
.been
.calledOnce
;
812 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
818 describe('Connection States', function () {
819 describe('connecting', function () {
820 it('should open the websocket connection', function () {
821 var client
= new RFB(document
.createElement('div'),
822 'ws://HOST:8675/PATH');
823 sinon
.spy(client
._sock
, 'open');
825 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
829 describe('connected', function () {
831 beforeEach(function () {
835 it('should result in a connect event if state becomes connected', function () {
836 var spy
= sinon
.spy();
837 client
.addEventListener("connect", spy
);
838 client
._rfb_connection_state
= 'connecting';
839 client
._updateConnectionState('connected');
840 expect(spy
).to
.have
.been
.calledOnce
;
843 it('should not result in a connect event if the state is not "connected"', function () {
844 var spy
= sinon
.spy();
845 client
.addEventListener("connect", spy
);
846 client
._sock
._websocket
.open = function () {}; // explicitly don't call onopen
847 client
._updateConnectionState('connecting');
848 expect(spy
).to
.not
.have
.been
.called
;
852 describe('disconnecting', function () {
854 beforeEach(function () {
858 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
859 sinon
.spy(client
, '_updateConnectionState');
860 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
861 client
._updateConnectionState('disconnecting');
862 this.clock
.tick(3 * 1000);
863 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
864 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
865 expect(client
._rfb_connection_state
).to
.equal("disconnected");
868 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
869 client
._updateConnectionState('disconnecting');
870 this.clock
.tick(3 * 1000 / 2);
871 client
._sock
._websocket
.close();
872 this.clock
.tick(3 * 1000 / 2 + 1);
873 expect(client
._rfb_connection_state
).to
.equal('disconnected');
876 it('should close the WebSocket connection', function () {
877 sinon
.spy(client
._sock
, 'close');
878 client
._updateConnectionState('disconnecting');
879 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
882 it('should not result in a disconnect event', function () {
883 var spy
= sinon
.spy();
884 client
.addEventListener("disconnect", spy
);
885 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
886 client
._updateConnectionState('disconnecting');
887 expect(spy
).to
.not
.have
.been
.called
;
891 describe('disconnected', function () {
893 beforeEach(function () {
894 client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
897 it('should result in a disconnect event if state becomes "disconnected"', function () {
898 var spy
= sinon
.spy();
899 client
.addEventListener("disconnect", spy
);
900 client
._rfb_connection_state
= 'disconnecting';
901 client
._updateConnectionState('disconnected');
902 expect(spy
).to
.have
.been
.calledOnce
;
903 expect(spy
.args
[0][0].detail
.clean
).to
.be
.true;
906 it('should result in a disconnect event without msg when no reason given', function () {
907 var spy
= sinon
.spy();
908 client
.addEventListener("disconnect", spy
);
909 client
._rfb_connection_state
= 'disconnecting';
910 client
._rfb_disconnect_reason
= "";
911 client
._updateConnectionState('disconnected');
912 expect(spy
).to
.have
.been
.calledOnce
;
913 expect(spy
.args
[0].length
).to
.equal(1);
918 describe('Protocol Initialization States', function () {
920 beforeEach(function () {
922 client
._rfb_connection_state
= 'connecting';
925 describe('ProtocolVersion', function () {
926 function send_ver (ver
, client
) {
927 var arr
= new Uint8Array(12);
928 for (var i
= 0; i
< ver
.length
; i
++) {
929 arr
[i
+4] = ver
.charCodeAt(i
);
931 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
933 client
._sock
._websocket
._receive_data(arr
);
936 describe('version parsing', function () {
937 it('should interpret version 003.003 as version 3.3', function () {
938 send_ver('003.003', client
);
939 expect(client
._rfb_version
).to
.equal(3.3);
942 it('should interpret version 003.006 as version 3.3', function () {
943 send_ver('003.006', client
);
944 expect(client
._rfb_version
).to
.equal(3.3);
947 it('should interpret version 003.889 as version 3.3', function () {
948 send_ver('003.889', client
);
949 expect(client
._rfb_version
).to
.equal(3.3);
952 it('should interpret version 003.007 as version 3.7', function () {
953 send_ver('003.007', client
);
954 expect(client
._rfb_version
).to
.equal(3.7);
957 it('should interpret version 003.008 as version 3.8', function () {
958 send_ver('003.008', client
);
959 expect(client
._rfb_version
).to
.equal(3.8);
962 it('should interpret version 004.000 as version 3.8', function () {
963 send_ver('004.000', client
);
964 expect(client
._rfb_version
).to
.equal(3.8);
967 it('should interpret version 004.001 as version 3.8', function () {
968 send_ver('004.001', client
);
969 expect(client
._rfb_version
).to
.equal(3.8);
972 it('should interpret version 005.000 as version 3.8', function () {
973 send_ver('005.000', client
);
974 expect(client
._rfb_version
).to
.equal(3.8);
977 it('should fail on an invalid version', function () {
978 sinon
.spy(client
, "_fail");
979 send_ver('002.000', client
);
980 expect(client
._fail
).to
.have
.been
.calledOnce
;
984 it('should send back the interpreted version', function () {
985 send_ver('004.000', client
);
987 var expected_str
= 'RFB 003.008\n';
989 for (var i
= 0; i
< expected_str
.length
; i
++) {
990 expected
[i
] = expected_str
.charCodeAt(i
);
993 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
996 it('should transition to the Security state on successful negotiation', function () {
997 send_ver('003.008', client
);
998 expect(client
._rfb_init_state
).to
.equal('Security');
1001 describe('Repeater', function () {
1002 beforeEach(function () {
1003 client
= make_rfb('wss://host:8675', { repeaterID
: "12345" });
1004 client
._rfb_connection_state
= 'connecting';
1007 it('should interpret version 000.000 as a repeater', function () {
1008 send_ver('000.000', client
);
1009 expect(client
._rfb_version
).to
.equal(0);
1011 var sent_data
= client
._sock
._websocket
._get_sent_data();
1012 expect(new Uint8Array(sent_data
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
1013 expect(sent_data
).to
.have
.length(250);
1016 it('should handle two step repeater negotiation', function () {
1017 send_ver('000.000', client
);
1018 send_ver('003.008', client
);
1019 expect(client
._rfb_version
).to
.equal(3.8);
1024 describe('Security', function () {
1025 beforeEach(function () {
1026 client
._rfb_init_state
= 'Security';
1029 it('should simply receive the auth scheme when for versions < 3.7', function () {
1030 client
._rfb_version
= 3.6;
1031 var auth_scheme_raw
= [1, 2, 3, 4];
1032 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
1033 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
1034 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
1035 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
1038 it('should prefer no authentication is possible', function () {
1039 client
._rfb_version
= 3.7;
1040 var auth_schemes
= [2, 1, 3];
1041 client
._sock
._websocket
._receive_data(auth_schemes
);
1042 expect(client
._rfb_auth_scheme
).to
.equal(1);
1043 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
1046 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
1047 client
._rfb_version
= 3.7;
1048 var auth_schemes
= [2, 22, 16];
1049 client
._sock
._websocket
._receive_data(auth_schemes
);
1050 expect(client
._rfb_auth_scheme
).to
.equal(22);
1051 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
1054 it('should fail if there are no supported schemes for versions >= 3.7', function () {
1055 sinon
.spy(client
, "_fail");
1056 client
._rfb_version
= 3.7;
1057 var auth_schemes
= [1, 32];
1058 client
._sock
._websocket
._receive_data(auth_schemes
);
1059 expect(client
._fail
).to
.have
.been
.calledOnce
;
1062 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
1063 client
._rfb_version
= 3.7;
1064 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1065 sinon
.spy(client
, '_fail');
1066 client
._sock
._websocket
._receive_data(failure_data
);
1068 expect(client
._fail
).to
.have
.been
.calledOnce
;
1069 expect(client
._fail
).to
.have
.been
.calledWith(
1070 'Security negotiation failed on no security types (reason: whoops)');
1073 it('should transition to the Authentication state and continue on successful negotiation', function () {
1074 client
._rfb_version
= 3.7;
1075 var auth_schemes
= [1, 1];
1076 client
._negotiate_authentication
= sinon
.spy();
1077 client
._sock
._websocket
._receive_data(auth_schemes
);
1078 expect(client
._rfb_init_state
).to
.equal('Authentication');
1079 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
1083 describe('Authentication', function () {
1084 beforeEach(function () {
1085 client
._rfb_init_state
= 'Security';
1088 function send_security(type
, cl
) {
1089 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
1092 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
1093 client
._rfb_version
= 3.6;
1094 var err_msg
= "Whoopsies";
1095 var data
= [0, 0, 0, 0];
1096 var err_len
= err_msg
.length
;
1097 push32(data
, err_len
);
1098 for (var i
= 0; i
< err_len
; i
++) {
1099 data
.push(err_msg
.charCodeAt(i
));
1102 sinon
.spy(client
, '_fail');
1103 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1104 expect(client
._fail
).to
.have
.been
.calledWith(
1105 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
1108 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
1109 client
._rfb_version
= 3.8;
1110 send_security(1, client
);
1111 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1114 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
1115 client
._rfb_version
= 3.7;
1116 send_security(1, client
);
1117 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1120 it('should fail on an unknown auth scheme', function () {
1121 sinon
.spy(client
, "_fail");
1122 client
._rfb_version
= 3.8;
1123 send_security(57, client
);
1124 expect(client
._fail
).to
.have
.been
.calledOnce
;
1127 describe('VNC Authentication (type 2) Handler', function () {
1128 beforeEach(function () {
1129 client
._rfb_init_state
= 'Security';
1130 client
._rfb_version
= 3.8;
1133 it('should fire the credentialsrequired event if missing a password', function () {
1134 var spy
= sinon
.spy();
1135 client
.addEventListener("credentialsrequired", spy
);
1136 send_security(2, client
);
1139 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1140 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1142 expect(client
._rfb_credentials
).to
.be
.empty
;
1143 expect(spy
).to
.have
.been
.calledOnce
;
1144 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
1147 it('should encrypt the password with DES and then send it back', function () {
1148 client
._rfb_credentials
= { password
: 'passwd' };
1149 send_security(2, client
);
1150 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
1153 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1154 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1156 var des_pass
= RFB
.genDES('passwd', challenge
);
1157 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
1160 it('should transition to SecurityResult immediately after sending the password', function () {
1161 client
._rfb_credentials
= { password
: 'passwd' };
1162 send_security(2, client
);
1165 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1166 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1168 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1172 describe('XVP Authentication (type 22) Handler', function () {
1173 beforeEach(function () {
1174 client
._rfb_init_state
= 'Security';
1175 client
._rfb_version
= 3.8;
1178 it('should fall through to standard VNC authentication upon completion', function () {
1179 client
._rfb_credentials
= { username
: 'user',
1181 password
: 'password' };
1182 client
._negotiate_std_vnc_auth
= sinon
.spy();
1183 send_security(22, client
);
1184 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1187 it('should fire the credentialsrequired event if all credentials are missing', function() {
1188 var spy
= sinon
.spy();
1189 client
.addEventListener("credentialsrequired", spy
);
1190 client
._rfb_credentials
= {};
1191 send_security(22, client
);
1193 expect(client
._rfb_credentials
).to
.be
.empty
;
1194 expect(spy
).to
.have
.been
.calledOnce
;
1195 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1198 it('should fire the credentialsrequired event if some credentials are missing', function() {
1199 var spy
= sinon
.spy();
1200 client
.addEventListener("credentialsrequired", spy
);
1201 client
._rfb_credentials
= { username
: 'user',
1203 send_security(22, client
);
1205 expect(spy
).to
.have
.been
.calledOnce
;
1206 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1209 it('should send user and target separately', function () {
1210 client
._rfb_credentials
= { username
: 'user',
1212 password
: 'password' };
1213 client
._negotiate_std_vnc_auth
= sinon
.spy();
1215 send_security(22, client
);
1217 var expected
= [22, 4, 6]; // auth selection, len user, len target
1218 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
1220 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1224 describe('TightVNC Authentication (type 16) Handler', function () {
1225 beforeEach(function () {
1226 client
._rfb_init_state
= 'Security';
1227 client
._rfb_version
= 3.8;
1228 send_security(16, client
);
1229 client
._sock
._websocket
._get_sent_data(); // skip the security reply
1232 function send_num_str_pairs(pairs
, client
) {
1233 var pairs_len
= pairs
.length
;
1235 push32(data
, pairs_len
);
1237 for (var i
= 0; i
< pairs_len
; i
++) {
1238 push32(data
, pairs
[i
][0]);
1240 for (j
= 0; j
< 4; j
++) {
1241 data
.push(pairs
[i
][1].charCodeAt(j
));
1243 for (j
= 0; j
< 8; j
++) {
1244 data
.push(pairs
[i
][2].charCodeAt(j
));
1248 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1251 it('should skip tunnel negotiation if no tunnels are requested', function () {
1252 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1253 expect(client
._rfb_tightvnc
).to
.be
.true;
1256 it('should fail if no supported tunnels are listed', function () {
1257 sinon
.spy(client
, "_fail");
1258 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
1259 expect(client
._fail
).to
.have
.been
.calledOnce
;
1262 it('should choose the notunnel tunnel type', function () {
1263 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
1264 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1267 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
1268 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
1269 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
1270 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
1271 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1272 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1275 /*it('should attempt to use VNC auth over no auth when possible', function () {
1276 client._rfb_tightvnc = true;
1277 client._negotiate_std_vnc_auth = sinon.spy();
1278 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
1279 expect(client._sock).to.have.sent([0, 0, 0, 1]);
1280 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
1281 expect(client._rfb_auth_scheme).to.equal(2);
1282 });*/ // while this would make sense, the original code doesn't actually do this
1284 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
1285 client
._rfb_tightvnc
= true;
1286 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
1287 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1288 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1291 it('should accept VNC authentication and transition to that', function () {
1292 client
._rfb_tightvnc
= true;
1293 client
._negotiate_std_vnc_auth
= sinon
.spy();
1294 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
1295 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1296 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1297 expect(client
._rfb_auth_scheme
).to
.equal(2);
1300 it('should fail if there are no supported auth types', function () {
1301 sinon
.spy(client
, "_fail");
1302 client
._rfb_tightvnc
= true;
1303 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
1304 expect(client
._fail
).to
.have
.been
.calledOnce
;
1309 describe('SecurityResult', function () {
1310 beforeEach(function () {
1311 client
._rfb_init_state
= 'SecurityResult';
1314 it('should fall through to ServerInitialisation on a response code of 0', function () {
1315 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1316 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1319 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1320 client
._rfb_version
= 3.8;
1321 sinon
.spy(client
, '_fail');
1322 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1323 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1324 expect(client
._fail
).to
.have
.been
.calledWith(
1325 'Security negotiation failed on security result (reason: whoops)');
1328 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1329 sinon
.spy(client
, '_fail');
1330 client
._rfb_version
= 3.7;
1331 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
1332 expect(client
._fail
).to
.have
.been
.calledWith(
1333 'Security handshake failed');
1336 it('should result in securityfailure event when receiving a non zero status', function () {
1337 var spy
= sinon
.spy();
1338 client
.addEventListener("securityfailure", spy
);
1339 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
1340 expect(spy
).to
.have
.been
.calledOnce
;
1341 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1344 it('should include reason when provided in securityfailure event', function () {
1345 client
._rfb_version
= 3.8;
1346 var spy
= sinon
.spy();
1347 client
.addEventListener("securityfailure", spy
);
1348 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
1349 32, 102, 97, 105, 108, 117, 114, 101];
1350 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1351 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1352 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
1355 it('should not include reason when length is zero in securityfailure event', function () {
1356 client
._rfb_version
= 3.9;
1357 var spy
= sinon
.spy();
1358 client
.addEventListener("securityfailure", spy
);
1359 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 0];
1360 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1361 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1362 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1365 it('should not include reason in securityfailure event for version < 3.8', function () {
1366 client
._rfb_version
= 3.6;
1367 var spy
= sinon
.spy();
1368 client
.addEventListener("securityfailure", spy
);
1369 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
1370 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1371 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1375 describe('ClientInitialisation', function () {
1376 it('should transition to the ServerInitialisation state', function () {
1377 var client
= make_rfb();
1378 client
._rfb_connection_state
= 'connecting';
1379 client
._rfb_init_state
= 'SecurityResult';
1380 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1381 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1384 it('should send 1 if we are in shared mode', function () {
1385 var client
= make_rfb('wss://host:8675', { shared
: true });
1386 client
._rfb_connection_state
= 'connecting';
1387 client
._rfb_init_state
= 'SecurityResult';
1388 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1389 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1392 it('should send 0 if we are not in shared mode', function () {
1393 var client
= make_rfb('wss://host:8675', { shared
: false });
1394 client
._rfb_connection_state
= 'connecting';
1395 client
._rfb_init_state
= 'SecurityResult';
1396 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1397 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1401 describe('ServerInitialisation', function () {
1402 beforeEach(function () {
1403 client
._rfb_init_state
= 'ServerInitialisation';
1406 function send_server_init(opts
, client
) {
1407 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1408 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1409 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1410 for (var opt
in opts
) {
1411 full_opts
[opt
] = opts
[opt
];
1415 push16(data
, full_opts
.width
);
1416 push16(data
, full_opts
.height
);
1418 data
.push(full_opts
.bpp
);
1419 data
.push(full_opts
.depth
);
1420 data
.push(full_opts
.big_endian
);
1421 data
.push(full_opts
.true_color
);
1423 push16(data
, full_opts
.red_max
);
1424 push16(data
, full_opts
.green_max
);
1425 push16(data
, full_opts
.blue_max
);
1426 push8(data
, full_opts
.red_shift
);
1427 push8(data
, full_opts
.green_shift
);
1428 push8(data
, full_opts
.blue_shift
);
1435 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1438 push32(name_data
, full_opts
.name
.length
);
1439 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1440 name_data
.push(full_opts
.name
.charCodeAt(i
));
1442 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1445 it('should set the framebuffer width and height', function () {
1446 send_server_init({ width
: 32, height
: 84 }, client
);
1447 expect(client
._fb_width
).to
.equal(32);
1448 expect(client
._fb_height
).to
.equal(84);
1451 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1453 it('should set the framebuffer name and call the callback', function () {
1454 var spy
= sinon
.spy();
1455 client
.addEventListener("desktopname", spy
);
1456 send_server_init({ name
: 'some name' }, client
);
1458 expect(client
._fb_name
).to
.equal('some name');
1459 expect(spy
).to
.have
.been
.calledOnce
;
1460 expect(spy
.args
[0][0].detail
.name
).to
.equal('some name');
1463 it('should handle the extended init message of the tight encoding', function () {
1464 // NB(sross): we don't actually do anything with it, so just test that we can
1465 // read it w/o throwing an error
1466 client
._rfb_tightvnc
= true;
1467 send_server_init({}, client
);
1469 var tight_data
= [];
1470 push16(tight_data
, 1);
1471 push16(tight_data
, 2);
1472 push16(tight_data
, 3);
1473 push16(tight_data
, 0);
1474 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1477 client
._sock
._websocket
._receive_data(tight_data
);
1479 expect(client
._rfb_connection_state
).to
.equal('connected');
1482 it('should resize the display', function () {
1483 sinon
.spy(client
._display
, 'resize');
1484 send_server_init({ width
: 27, height
: 32 }, client
);
1486 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1487 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1490 it('should grab the mouse and keyboard', function () {
1491 sinon
.spy(client
._keyboard
, 'grab');
1492 sinon
.spy(client
._mouse
, 'grab');
1493 send_server_init({}, client
);
1494 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1495 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1498 describe('Initial Update Request', function () {
1499 beforeEach(function () {
1500 sinon
.spy(RFB
.messages
, "pixelFormat");
1501 sinon
.spy(RFB
.messages
, "clientEncodings");
1502 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1505 afterEach(function () {
1506 RFB
.messages
.pixelFormat
.restore();
1507 RFB
.messages
.clientEncodings
.restore();
1508 RFB
.messages
.fbUpdateRequest
.restore();
1511 // TODO(directxman12): test the various options in this configuration matrix
1512 it('should reply with the pixel format, client encodings, and initial update request', function () {
1513 send_server_init({ width
: 27, height
: 32 }, client
);
1515 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1516 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1517 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1518 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1519 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1520 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1521 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1522 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1525 it('should reply with restricted settings for Intel AMT servers', function () {
1526 send_server_init({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1528 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1529 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1530 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1531 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1532 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1533 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1534 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1535 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1536 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1540 it('should transition to the "connected" state', function () {
1541 send_server_init({}, client
);
1542 expect(client
._rfb_connection_state
).to
.equal('connected');
1547 describe('Protocol Message Processing After Completing Initialization', function () {
1550 beforeEach(function () {
1551 client
= make_rfb();
1552 client
._fb_name
= 'some device';
1553 client
._fb_width
= 640;
1554 client
._fb_height
= 20;
1557 describe('Framebuffer Update Handling', function () {
1558 var target_data_arr
= [
1559 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1560 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1561 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1562 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1566 var target_data_check_arr
= [
1567 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1568 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1569 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1570 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1572 var target_data_check
;
1574 before(function () {
1575 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1576 target_data
= new Uint8Array(target_data_arr
);
1577 target_data_check
= new Uint8Array(target_data_check_arr
);
1580 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1583 if (!rect_cnt
|| rect_cnt
> -1) {
1585 data
.push(0); // msg type
1586 data
.push(0); // padding
1587 push16(data
, rect_cnt
|| rect_data
.length
);
1590 for (var i
= 0; i
< rect_data
.length
; i
++) {
1592 push16(data
, rect_info
[i
].x
);
1593 push16(data
, rect_info
[i
].y
);
1594 push16(data
, rect_info
[i
].width
);
1595 push16(data
, rect_info
[i
].height
);
1596 push32(data
, rect_info
[i
].encoding
);
1598 data
= data
.concat(rect_data
[i
]);
1601 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1604 it('should send an update request if there is sufficient data', function () {
1605 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1606 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1608 client
._framebufferUpdate = function () { return true; };
1609 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1611 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1614 it('should not send an update request if we need more data', function () {
1615 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1616 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1619 it('should resume receiving an update if we previously did not have enough data', function () {
1620 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1621 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1623 // just enough to set FBU.rects
1624 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1625 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1627 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1628 // 247 should *not* be used as the message type here
1629 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1630 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1633 it('should not send a request in continuous updates mode', function () {
1634 client
._enabledContinuousUpdates
= true;
1635 client
._framebufferUpdate = function () { return true; };
1636 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1638 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1641 it('should fail on an unsupported encoding', function () {
1642 sinon
.spy(client
, "_fail");
1643 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1644 send_fbu_msg([rect_info
], [[]], client
);
1645 expect(client
._fail
).to
.have
.been
.calledOnce
;
1648 it('should be able to pause and resume receiving rects if not enought data', function () {
1649 // seed some initial data to copy
1650 client
._fb_width
= 4;
1651 client
._fb_height
= 4;
1652 client
._display
.resize(4, 4);
1653 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1655 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1656 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1657 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1658 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1659 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1660 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1661 expect(client
._display
).to
.have
.displayed(target_data_check
);
1664 describe('Message Encoding Handlers', function () {
1665 beforeEach(function () {
1666 // a really small frame
1667 client
._fb_width
= 4;
1668 client
._fb_height
= 4;
1669 client
._fb_depth
= 24;
1670 client
._display
.resize(4, 4);
1673 it('should handle the RAW encoding', function () {
1674 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1675 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1676 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1677 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1680 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1681 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1682 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1683 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1684 send_fbu_msg(info
, rects
, client
);
1685 expect(client
._display
).to
.have
.displayed(target_data
);
1688 it('should handle the RAW encoding in low colour mode', function () {
1689 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1690 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1691 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1692 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1694 [0x03, 0x03, 0x03, 0x03],
1695 [0x0c, 0x0c, 0x0c, 0x0c],
1696 [0x0c, 0x0c, 0x03, 0x03],
1697 [0x0c, 0x0c, 0x03, 0x03]];
1698 client
._fb_depth
= 8;
1699 send_fbu_msg(info
, rects
, client
);
1700 expect(client
._display
).to
.have
.displayed(target_data_check
);
1703 it('should handle the COPYRECT encoding', function () {
1704 // seed some initial data to copy
1705 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1707 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1708 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1709 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1710 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1711 send_fbu_msg(info
, rects
, client
);
1712 expect(client
._display
).to
.have
.displayed(target_data_check
);
1715 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1716 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1718 it('should handle the RRE encoding', function () {
1719 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1721 push32(rect
, 2); // 2 subrects
1722 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1723 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1727 push16(rect
, 0); // x: 0
1728 push16(rect
, 0); // y: 0
1729 push16(rect
, 2); // width: 2
1730 push16(rect
, 2); // height: 2
1731 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1735 push16(rect
, 2); // x: 2
1736 push16(rect
, 2); // y: 2
1737 push16(rect
, 2); // width: 2
1738 push16(rect
, 2); // height: 2
1740 send_fbu_msg(info
, [rect
], client
);
1741 expect(client
._display
).to
.have
.displayed(target_data_check
);
1744 describe('the HEXTILE encoding handler', function () {
1745 it('should handle a tile with fg, bg specified, normal subrects', function () {
1746 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1748 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1749 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1750 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1754 rect
.push(2); // 2 subrects
1755 rect
.push(0); // x: 0, y: 0
1756 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1757 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1758 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1759 send_fbu_msg(info
, [rect
], client
);
1760 expect(client
._display
).to
.have
.displayed(target_data_check
);
1763 it('should handle a raw tile', function () {
1764 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1766 rect
.push(0x01); // raw
1767 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1768 rect
.push(target_data
[i
+ 2]);
1769 rect
.push(target_data
[i
+ 1]);
1770 rect
.push(target_data
[i
]);
1771 rect
.push(target_data
[i
+ 3]);
1773 send_fbu_msg(info
, [rect
], client
);
1774 expect(client
._display
).to
.have
.displayed(target_data
);
1777 it('should handle a tile with only bg specified (solid bg)', function () {
1778 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1781 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1782 send_fbu_msg(info
, [rect
], client
);
1785 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1786 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1789 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1790 // set the width so we can have two tiles
1791 client
._fb_width
= 8;
1792 client
._display
.resize(8, 4);
1794 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1800 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1802 // send an empty frame
1805 send_fbu_msg(info
, [rect
], client
);
1809 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1810 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1811 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1814 it('should handle a tile with bg and coloured subrects', function () {
1815 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1817 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1818 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1819 rect
.push(2); // 2 subrects
1820 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1824 rect
.push(0); // x: 0, y: 0
1825 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1826 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1830 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1831 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1832 send_fbu_msg(info
, [rect
], client
);
1833 expect(client
._display
).to
.have
.displayed(target_data_check
);
1836 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1837 client
._fb_width
= 4;
1838 client
._fb_height
= 17;
1839 client
._display
.resize(4, 17);
1841 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1843 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1844 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1845 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1849 rect
.push(8); // 8 subrects
1851 for (i
= 0; i
< 4; i
++) {
1852 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1853 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1854 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1855 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1857 rect
.push(0x08); // anysubrects
1858 rect
.push(1); // 1 subrect
1859 rect
.push(0); // x: 0, y: 0
1860 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1861 send_fbu_msg(info
, [rect
], client
);
1864 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1865 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1866 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1869 it('should fail on an invalid subencoding', function () {
1870 sinon
.spy(client
,"_fail");
1871 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1872 var rects
= [[45]]; // an invalid subencoding
1873 send_fbu_msg(info
, rects
, client
);
1874 expect(client
._fail
).to
.have
.been
.calledOnce
;
1878 it
.skip('should handle the TIGHT encoding', function () {
1879 // TODO(directxman12): test this
1882 it
.skip('should handle the TIGHT_PNG encoding', function () {
1883 // TODO(directxman12): test this
1886 it('should handle the DesktopSize pseduo-encoding', function () {
1887 var spy
= sinon
.spy();
1888 sinon
.spy(client
._display
, 'resize');
1889 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1891 expect(client
._fb_width
).to
.equal(20);
1892 expect(client
._fb_height
).to
.equal(50);
1894 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1895 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1898 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1901 beforeEach(function () {
1902 // a really small frame
1903 client
._fb_width
= 4;
1904 client
._fb_height
= 4;
1905 client
._display
.resize(4, 4);
1906 sinon
.spy(client
._display
, 'resize');
1907 resizeSpy
= sinon
.spy();
1910 function make_screen_data (nr_of_screens
) {
1912 push8(data
, nr_of_screens
); // number-of-screens
1913 push8(data
, 0); // padding
1914 push16(data
, 0); // padding
1915 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1916 push32(data
, 0); // id
1917 push16(data
, 0); // x-position
1918 push16(data
, 0); // y-position
1919 push16(data
, 20); // width
1920 push16(data
, 50); // height
1921 push32(data
, 0); // flags
1926 it('should handle a resize requested by this client', function () {
1927 var reason_for_change
= 1; // requested by this client
1928 var status_code
= 0; // No error
1930 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1931 width
: 20, height
: 50, encoding
: -308 }],
1932 make_screen_data(1), client
);
1934 expect(client
._fb_width
).to
.equal(20);
1935 expect(client
._fb_height
).to
.equal(50);
1937 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1938 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1941 it('should handle a resize requested by another client', function () {
1942 var reason_for_change
= 2; // requested by another client
1943 var status_code
= 0; // No error
1945 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1946 width
: 20, height
: 50, encoding
: -308 }],
1947 make_screen_data(1), client
);
1949 expect(client
._fb_width
).to
.equal(20);
1950 expect(client
._fb_height
).to
.equal(50);
1952 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1953 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1956 it('should be able to recieve requests which contain data for multiple screens', function () {
1957 var reason_for_change
= 2; // requested by another client
1958 var status_code
= 0; // No error
1960 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1961 width
: 60, height
: 50, encoding
: -308 }],
1962 make_screen_data(3), client
);
1964 expect(client
._fb_width
).to
.equal(60);
1965 expect(client
._fb_height
).to
.equal(50);
1967 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1968 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1971 it('should not handle a failed request', function () {
1972 var reason_for_change
= 1; // requested by this client
1973 var status_code
= 1; // Resize is administratively prohibited
1975 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1976 width
: 20, height
: 50, encoding
: -308 }],
1977 make_screen_data(1), client
);
1979 expect(client
._fb_width
).to
.equal(4);
1980 expect(client
._fb_height
).to
.equal(4);
1982 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1986 it
.skip('should handle the Cursor pseudo-encoding', function () {
1987 // TODO(directxman12): test
1990 it('should handle the last_rect pseudo-encoding', function () {
1991 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1992 expect(client
._FBU
.rects
).to
.equal(0);
1997 describe('XVP Message Handling', function () {
1998 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1999 var spy
= sinon
.spy();
2000 client
.addEventListener("capabilities", spy
);
2001 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
2002 expect(client
._rfb_xvp_ver
).to
.equal(10);
2003 expect(spy
).to
.have
.been
.calledOnce
;
2004 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
2005 expect(client
.capabilities
.power
).to
.be
.true;
2008 it('should fail on unknown XVP message types', function () {
2009 sinon
.spy(client
, "_fail");
2010 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
2011 expect(client
._fail
).to
.have
.been
.calledOnce
;
2015 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
2016 var expected_str
= 'cheese!';
2017 var data
= [3, 0, 0, 0];
2018 push32(data
, expected_str
.length
);
2019 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
2020 var spy
= sinon
.spy();
2021 client
.addEventListener("clipboard", spy
);
2023 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
2024 expect(spy
).to
.have
.been
.calledOnce
;
2025 expect(spy
.args
[0][0].detail
.text
).to
.equal(expected_str
);
2028 it('should fire the bell callback on Bell', function () {
2029 var spy
= sinon
.spy();
2030 client
.addEventListener("bell", spy
);
2031 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
2032 expect(spy
).to
.have
.been
.calledOnce
;
2035 it('should respond correctly to ServerFence', function () {
2036 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
2037 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
2039 var payload
= "foo\x00ab9";
2041 // ClientFence and ServerFence are identical in structure
2042 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
2043 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
2045 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
2047 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2049 expected_msg
._sQlen
= 0;
2050 incoming_msg
._sQlen
= 0;
2052 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
2053 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
2055 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
2057 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2060 it('should enable continuous updates on first EndOfContinousUpdates', function () {
2061 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
2063 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
2065 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2067 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
2069 expect(client
._enabledContinuousUpdates
).to
.be
.true;
2070 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2073 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
2074 client
._enabledContinuousUpdates
= true;
2075 client
._supportsContinuousUpdates
= true;
2077 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
2079 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2082 it('should update continuous updates on resize', function () {
2083 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
2084 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
2086 client
._resize(450, 160);
2088 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
2090 client
._enabledContinuousUpdates
= true;
2092 client
._resize(90, 700);
2094 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2097 it('should fail on an unknown message type', function () {
2098 sinon
.spy(client
, "_fail");
2099 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
2100 expect(client
._fail
).to
.have
.been
.calledOnce
;
2104 describe('Asynchronous Events', function () {
2106 beforeEach(function () {
2107 client
= make_rfb();
2110 describe('Mouse event handlers', function () {
2111 it('should not send button messages in view-only mode', function () {
2112 client
._viewOnly
= true;
2113 sinon
.spy(client
._sock
, 'flush');
2114 client
._handleMouseButton(0, 0, 1, 0x001);
2115 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2118 it('should not send movement messages in view-only mode', function () {
2119 client
._viewOnly
= true;
2120 sinon
.spy(client
._sock
, 'flush');
2121 client
._handleMouseMove(0, 0);
2122 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2125 it('should send a pointer event on mouse button presses', function () {
2126 client
._handleMouseButton(10, 12, 1, 0x001);
2127 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
2128 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
2129 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2132 it('should send a mask of 1 on mousedown', function () {
2133 client
._handleMouseButton(10, 12, 1, 0x001);
2134 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
2135 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
2136 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2139 it('should send a mask of 0 on mouseup', function () {
2140 client
._mouse_buttonMask
= 0x001;
2141 client
._handleMouseButton(10, 12, 0, 0x001);
2142 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
2143 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
2144 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2147 it('should send a pointer event on mouse movement', function () {
2148 client
._handleMouseMove(10, 12);
2149 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
2150 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
2151 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2154 it('should set the button mask so that future mouse movements use it', function () {
2155 client
._handleMouseButton(10, 12, 1, 0x010);
2156 client
._handleMouseMove(13, 9);
2157 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
2158 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
2159 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
2160 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2164 describe('Keyboard Event Handlers', function () {
2165 it('should send a key message on a key press', function () {
2167 client
._handleKeyEvent(0x41, 'KeyA', true);
2168 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
2169 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
2170 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
2173 it('should not send messages in view-only mode', function () {
2174 client
._viewOnly
= true;
2175 sinon
.spy(client
._sock
, 'flush');
2176 client
._handleKeyEvent('a', 'KeyA', true);
2177 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2181 describe('WebSocket event handlers', function () {
2183 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
2184 client
._normal_msg
= sinon
.spy();
2185 client
._sock
._websocket
._receive_data(new Uint8Array([]));
2186 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
2189 it('should handle a message in the connected state as a normal message', function () {
2190 client
._normal_msg
= sinon
.spy();
2191 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2192 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
2195 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2196 client
._rfb_connection_state
= 'connecting';
2197 client
._rfb_init_state
= 'ProtocolVersion';
2198 client
._init_msg
= sinon
.spy();
2199 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2200 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
2203 it('should process all normal messages directly', function () {
2204 var spy
= sinon
.spy();
2205 client
.addEventListener("bell", spy
);
2206 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2207 expect(spy
).to
.have
.been
.calledTwice
;
2211 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2212 client
= new RFB(document
.createElement('div'), 'wss://host:8675');
2214 client
._sock
._websocket
._open();
2215 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2218 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2219 sinon
.spy(client
, "_fail");
2220 client
._rfb_connection_state
= 'connected';
2221 client
._sock
._websocket
._open();
2222 expect(client
._fail
).to
.have
.been
.calledOnce
;
2226 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2227 var real
= client
._sock
._websocket
.close
;
2228 client
._sock
._websocket
.close = function () {};
2229 client
.disconnect();
2230 expect(client
._rfb_connection_state
).to
.equal('disconnecting');
2231 client
._sock
._websocket
.close
= real
;
2232 client
._sock
._websocket
.close();
2233 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2236 it('should fail if we get a close event while connecting', function () {
2237 sinon
.spy(client
, "_fail");
2238 client
._rfb_connection_state
= 'connecting';
2239 client
._sock
._websocket
.close();
2240 expect(client
._fail
).to
.have
.been
.calledOnce
;
2243 it('should unregister close event handler', function () {
2244 sinon
.spy(client
._sock
, 'off');
2245 client
.disconnect();
2246 client
._sock
._websocket
.close();
2247 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2250 // error events do nothing