1 const 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';
9 /* UIEvent constructor polyfill for IE */
11 if (typeof window
.UIEvent
=== "function") return;
13 function UIEvent( event
, params
) {
14 params
= params
|| { bubbles
: false, cancelable
: false, view
: window
, detail
: undefined };
15 const evt
= document
.createEvent( 'UIEvent' );
16 evt
.initUIEvent( event
, params
.bubbles
, params
.cancelable
, params
.view
, params
.detail
);
20 UIEvent
.prototype = window
.UIEvent
.prototype;
22 window
.UIEvent
= UIEvent
;
25 function push8(arr
, num
) {
30 function push16(arr
, num
) {
32 arr
.push((num
>> 8) & 0xFF,
36 function push32(arr
, num
) {
38 arr
.push((num
>> 24) & 0xFF,
44 function pushString(arr
, string
) {
45 let utf8
= unescape(encodeURIComponent(string
));
46 for (let i
= 0; i
< utf8
.length
; i
++) {
47 arr
.push(utf8
.charCodeAt(i
));
51 describe('Remote Frame Buffer Protocol Client', function () {
55 before(FakeWebSocket
.replace
);
56 after(FakeWebSocket
.restore
);
59 this.clock
= clock
= sinon
.useFakeTimers();
60 // sinon doesn't support this yet
61 raf
= window
.requestAnimationFrame
;
62 window
.requestAnimationFrame
= setTimeout
;
63 // Use a single set of buffers instead of reallocating to
65 const sock
= new Websock();
66 const _sQ
= new Uint8Array(sock
._sQbufferSize
);
67 const rQ
= new Uint8Array(sock
._rQbufferSize
);
69 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
70 Websock
.prototype._allocate_buffers = function () {
78 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
80 window
.requestAnimationFrame
= raf
;
86 beforeEach(function () {
87 // Create a container element for all RFB objects to attach to
88 container
= document
.createElement('div');
89 container
.style
.width
= "100%";
90 container
.style
.height
= "100%";
91 document
.body
.appendChild(container
);
93 // And track all created RFB objects
96 afterEach(function () {
97 // Make sure every created RFB object is properly cleaned up
98 // or they might affect subsequent tests
99 rfbs
.forEach(function (rfb
) {
101 expect(rfb
._disconnect
).to
.have
.been
.called
;
105 document
.body
.removeChild(container
);
109 function make_rfb(url
, options
) {
110 url
= url
|| 'wss://host:8675';
111 const rfb
= new RFB(container
, url
, options
);
113 rfb
._sock
._websocket
._open();
114 rfb
._rfb_connection_state
= 'connected';
115 sinon
.spy(rfb
, "_disconnect");
120 describe('Connecting/Disconnecting', function () {
121 describe('#RFB', function () {
122 it('should set the current state to "connecting"', function () {
123 const client
= new RFB(document
.createElement('div'), 'wss://host:8675');
124 client
._rfb_connection_state
= '';
126 expect(client
._rfb_connection_state
).to
.equal('connecting');
129 it('should actually connect to the websocket', function () {
130 const client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
131 sinon
.spy(client
._sock
, 'open');
133 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
134 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
138 describe('#disconnect', function () {
140 beforeEach(function () {
144 it('should go to state "disconnecting" before "disconnected"', function () {
145 sinon
.spy(client
, '_updateConnectionState');
147 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
148 expect(client
._updateConnectionState
.getCall(0).args
[0])
149 .to
.equal('disconnecting');
150 expect(client
._updateConnectionState
.getCall(1).args
[0])
151 .to
.equal('disconnected');
152 expect(client
._rfb_connection_state
).to
.equal('disconnected');
155 it('should unregister error event handler', function () {
156 sinon
.spy(client
._sock
, 'off');
158 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
161 it('should unregister message event handler', function () {
162 sinon
.spy(client
._sock
, 'off');
164 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
167 it('should unregister open event handler', function () {
168 sinon
.spy(client
._sock
, 'off');
170 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
174 describe('#sendCredentials', function () {
176 beforeEach(function () {
178 client
._rfb_connection_state
= 'connecting';
181 it('should set the rfb credentials properly"', function () {
182 client
.sendCredentials({ password
: 'pass' });
183 expect(client
._rfb_credentials
).to
.deep
.equal({ password
: 'pass' });
186 it('should call init_msg "soon"', function () {
187 client
._init_msg
= sinon
.spy();
188 client
.sendCredentials({ password
: 'pass' });
190 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
195 describe('Public API Basic Behavior', function () {
197 beforeEach(function () {
201 describe('#sendCtrlAlDel', function () {
202 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
203 const expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush
: () => {}};
204 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
205 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
206 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
207 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
208 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
209 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
211 client
.sendCtrlAltDel();
212 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
215 it('should not send the keys if we are not in a normal state', function () {
216 sinon
.spy(client
._sock
, 'flush');
217 client
._rfb_connection_state
= "connecting";
218 client
.sendCtrlAltDel();
219 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
222 it('should not send the keys if we are set as view_only', function () {
223 sinon
.spy(client
._sock
, 'flush');
224 client
._viewOnly
= true;
225 client
.sendCtrlAltDel();
226 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
230 describe('#sendKey', function () {
231 it('should send a single key with the given code and state (down = true)', function () {
232 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
233 RFB
.messages
.keyEvent(expected
, 123, 1);
234 client
.sendKey(123, 'Key123', true);
235 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
238 it('should send both a down and up event if the state is not specified', function () {
239 const expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
240 RFB
.messages
.keyEvent(expected
, 123, 1);
241 RFB
.messages
.keyEvent(expected
, 123, 0);
242 client
.sendKey(123, 'Key123');
243 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
246 it('should not send the key if we are not in a normal state', function () {
247 sinon
.spy(client
._sock
, 'flush');
248 client
._rfb_connection_state
= "connecting";
249 client
.sendKey(123, 'Key123');
250 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
253 it('should not send the key if we are set as view_only', function () {
254 sinon
.spy(client
._sock
, 'flush');
255 client
._viewOnly
= true;
256 client
.sendKey(123, 'Key123');
257 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
260 it('should send QEMU extended events if supported', function () {
261 client
._qemuExtKeyEventSupported
= true;
262 const expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush
: () => {}};
263 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
264 client
.sendKey(0x20, 'Space', true);
265 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
268 it('should not send QEMU extended events if unknown key code', function () {
269 client
._qemuExtKeyEventSupported
= true;
270 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
271 RFB
.messages
.keyEvent(expected
, 123, 1);
272 client
.sendKey(123, 'FooBar', true);
273 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
277 describe('#focus', function () {
278 it('should move focus to canvas object', function () {
279 client
._canvas
.focus
= sinon
.spy();
281 expect(client
._canvas
.focus
).to
.have
.been
.calledOnce
;
285 describe('#blur', function () {
286 it('should remove focus from canvas object', function () {
287 client
._canvas
.blur
= sinon
.spy();
289 expect(client
._canvas
.blur
).to
.have
.been
.calledOnce
;
293 describe('#clipboardPasteFrom', function () {
294 it('should send the given text in a paste event', function () {
295 const expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0,
296 _sQbufferSize
: 11, flush
: () => {}};
297 RFB
.messages
.clientCutText(expected
, 'abc');
298 client
.clipboardPasteFrom('abc');
299 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
302 it('should flush multiple times for large clipboards', function () {
303 sinon
.spy(client
._sock
, 'flush');
305 for (let i
= 0; i
< client
._sock
._sQbufferSize
+ 100; i
++) {
308 client
.clipboardPasteFrom(long_text
);
309 expect(client
._sock
.flush
).to
.have
.been
.calledTwice
;
312 it('should not send the text if we are not in a normal state', function () {
313 sinon
.spy(client
._sock
, 'flush');
314 client
._rfb_connection_state
= "connecting";
315 client
.clipboardPasteFrom('abc');
316 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
320 describe("XVP operations", function () {
321 beforeEach(function () {
322 client
._rfb_xvp_ver
= 1;
325 it('should send the shutdown signal on #machineShutdown', function () {
326 client
.machineShutdown();
327 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
330 it('should send the reboot signal on #machineReboot', function () {
331 client
.machineReboot();
332 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
335 it('should send the reset signal on #machineReset', function () {
336 client
.machineReset();
337 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
340 it('should not send XVP operations with higher versions than we support', function () {
341 sinon
.spy(client
._sock
, 'flush');
343 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
348 describe('Clipping', function () {
350 beforeEach(function () {
352 container
.style
.width
= '70px';
353 container
.style
.height
= '80px';
354 client
.clipViewport
= true;
357 it('should update display clip state when changing the property', function () {
358 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
360 client
.clipViewport
= false;
361 expect(spy
.set).to
.have
.been
.calledOnce
;
362 expect(spy
.set).to
.have
.been
.calledWith(false);
363 spy
.set.resetHistory();
365 client
.clipViewport
= true;
366 expect(spy
.set).to
.have
.been
.calledOnce
;
367 expect(spy
.set).to
.have
.been
.calledWith(true);
370 it('should update the viewport when the container size changes', function () {
371 sinon
.spy(client
._display
, "viewportChangeSize");
373 container
.style
.width
= '40px';
374 container
.style
.height
= '50px';
375 const event
= new UIEvent('resize');
376 window
.dispatchEvent(event
);
379 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledOnce
;
380 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(40, 50);
383 it('should update the viewport when the remote session resizes', function () {
384 // Simple ExtendedDesktopSize FBU message
385 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
386 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
387 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
388 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
389 0x00, 0x00, 0x00, 0x00 ];
391 sinon
.spy(client
._display
, "viewportChangeSize");
393 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
395 // FIXME: Display implicitly calls viewportChangeSize() when
396 // resizing the framebuffer, hence calledTwice.
397 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledTwice
;
398 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(70, 80);
401 it('should not update the viewport if not clipping', function () {
402 client
.clipViewport
= false;
403 sinon
.spy(client
._display
, "viewportChangeSize");
405 container
.style
.width
= '40px';
406 container
.style
.height
= '50px';
407 const event
= new UIEvent('resize');
408 window
.dispatchEvent(event
);
411 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
414 it('should not update the viewport if scaling', function () {
415 client
.scaleViewport
= true;
416 sinon
.spy(client
._display
, "viewportChangeSize");
418 container
.style
.width
= '40px';
419 container
.style
.height
= '50px';
420 const event
= new UIEvent('resize');
421 window
.dispatchEvent(event
);
424 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
427 describe('Dragging', function () {
428 beforeEach(function () {
429 client
.dragViewport
= true;
430 sinon
.spy(RFB
.messages
, "pointerEvent");
433 afterEach(function () {
434 RFB
.messages
.pointerEvent
.restore();
437 it('should not send button messages when initiating viewport dragging', function () {
438 client
._handleMouseButton(13, 9, 0x001);
439 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
442 it('should send button messages when release without movement', function () {
444 client
._handleMouseButton(13, 9, 0x001);
445 client
._handleMouseButton(13, 9, 0x000);
446 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
448 RFB
.messages
.pointerEvent
.resetHistory();
451 client
._handleMouseButton(13, 9, 0x001);
452 client
._handleMouseMove(15, 14);
453 client
._handleMouseButton(15, 14, 0x000);
454 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
457 it('should send button message directly when drag is disabled', function () {
458 client
.dragViewport
= false;
459 client
._handleMouseButton(13, 9, 0x001);
460 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
463 it('should be initiate viewport dragging on sufficient movement', function () {
464 sinon
.spy(client
._display
, "viewportChangePos");
466 // Too small movement
468 client
._handleMouseButton(13, 9, 0x001);
469 client
._handleMouseMove(18, 9);
471 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
472 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
474 // Sufficient movement
476 client
._handleMouseMove(43, 9);
478 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
479 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
480 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(-30, 0);
482 client
._display
.viewportChangePos
.resetHistory();
484 // Now a small movement should move right away
486 client
._handleMouseMove(43, 14);
488 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
489 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
490 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(0, -5);
493 it('should not send button messages when dragging ends', function () {
494 // First the movement
496 client
._handleMouseButton(13, 9, 0x001);
497 client
._handleMouseMove(43, 9);
498 client
._handleMouseButton(43, 9, 0x000);
500 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
503 it('should terminate viewport dragging on a button up event', function () {
504 // First the dragging movement
506 client
._handleMouseButton(13, 9, 0x001);
507 client
._handleMouseMove(43, 9);
508 client
._handleMouseButton(43, 9, 0x000);
510 // Another movement now should not move the viewport
512 sinon
.spy(client
._display
, "viewportChangePos");
514 client
._handleMouseMove(43, 59);
516 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
521 describe('Scaling', function () {
523 beforeEach(function () {
525 container
.style
.width
= '70px';
526 container
.style
.height
= '80px';
527 client
.scaleViewport
= true;
530 it('should update display scale factor when changing the property', function () {
531 const spy
= sinon
.spy(client
._display
, "scale", ["set"]);
532 sinon
.spy(client
._display
, "autoscale");
534 client
.scaleViewport
= false;
535 expect(spy
.set).to
.have
.been
.calledOnce
;
536 expect(spy
.set).to
.have
.been
.calledWith(1.0);
537 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
539 client
.scaleViewport
= true;
540 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
541 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
544 it('should update the clipping setting when changing the property', function () {
545 client
.clipViewport
= true;
547 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
549 client
.scaleViewport
= false;
550 expect(spy
.set).to
.have
.been
.calledOnce
;
551 expect(spy
.set).to
.have
.been
.calledWith(true);
553 spy
.set.resetHistory();
555 client
.scaleViewport
= true;
556 expect(spy
.set).to
.have
.been
.calledOnce
;
557 expect(spy
.set).to
.have
.been
.calledWith(false);
560 it('should update the scaling when the container size changes', function () {
561 sinon
.spy(client
._display
, "autoscale");
563 container
.style
.width
= '40px';
564 container
.style
.height
= '50px';
565 const event
= new UIEvent('resize');
566 window
.dispatchEvent(event
);
569 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
570 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(40, 50);
573 it('should update the scaling when the remote session resizes', function () {
574 // Simple ExtendedDesktopSize FBU message
575 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
576 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
577 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
578 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
579 0x00, 0x00, 0x00, 0x00 ];
581 sinon
.spy(client
._display
, "autoscale");
583 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
585 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
586 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
589 it('should not update the display scale factor if not scaling', function () {
590 client
.scaleViewport
= false;
592 sinon
.spy(client
._display
, "autoscale");
594 container
.style
.width
= '40px';
595 container
.style
.height
= '50px';
596 const event
= new UIEvent('resize');
597 window
.dispatchEvent(event
);
600 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
604 describe('Remote resize', function () {
606 beforeEach(function () {
608 client
._supportsSetDesktopSize
= true;
609 client
.resizeSession
= true;
610 container
.style
.width
= '70px';
611 container
.style
.height
= '80px';
612 sinon
.spy(RFB
.messages
, "setDesktopSize");
615 afterEach(function () {
616 RFB
.messages
.setDesktopSize
.restore();
619 it('should only request a resize when turned on', function () {
620 client
.resizeSession
= false;
621 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
622 client
.resizeSession
= true;
623 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
626 it('should request a resize when initially connecting', function () {
627 // Simple ExtendedDesktopSize FBU message
628 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
629 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
630 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
631 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
632 0x00, 0x00, 0x00, 0x00 ];
634 // First message should trigger a resize
636 client
._supportsSetDesktopSize
= false;
638 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
640 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
641 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 70, 80, 0, 0);
643 RFB
.messages
.setDesktopSize
.resetHistory();
645 // Second message should not trigger a resize
647 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
649 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
652 it('should request a resize when the container resizes', function () {
653 container
.style
.width
= '40px';
654 container
.style
.height
= '50px';
655 const event
= new UIEvent('resize');
656 window
.dispatchEvent(event
);
659 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
660 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
663 it('should not resize until the container size is stable', function () {
664 container
.style
.width
= '20px';
665 container
.style
.height
= '30px';
666 const event1
= new UIEvent('resize');
667 window
.dispatchEvent(event1
);
670 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
672 container
.style
.width
= '40px';
673 container
.style
.height
= '50px';
674 const event2
= new UIEvent('resize');
675 window
.dispatchEvent(event2
);
678 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
682 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
683 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
686 it('should not resize when resize is disabled', function () {
687 client
._resizeSession
= false;
689 container
.style
.width
= '40px';
690 container
.style
.height
= '50px';
691 const event
= new UIEvent('resize');
692 window
.dispatchEvent(event
);
695 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
698 it('should not resize when resize is not supported', function () {
699 client
._supportsSetDesktopSize
= false;
701 container
.style
.width
= '40px';
702 container
.style
.height
= '50px';
703 const event
= new UIEvent('resize');
704 window
.dispatchEvent(event
);
707 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
710 it('should not resize when in view only mode', function () {
711 client
._viewOnly
= true;
713 container
.style
.width
= '40px';
714 container
.style
.height
= '50px';
715 const event
= new UIEvent('resize');
716 window
.dispatchEvent(event
);
719 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
722 it('should not try to override a server resize', function () {
723 // Simple ExtendedDesktopSize FBU message
724 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
725 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
726 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
727 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
728 0x00, 0x00, 0x00, 0x00 ];
730 client
._sock
._websocket
._receive_data(new Uint8Array(incoming
));
732 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
736 describe('Misc Internals', function () {
737 describe('#_updateConnectionState', function () {
739 beforeEach(function () {
743 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
744 const spy
= sinon
.spy();
745 client
._disconnTimer
= setTimeout(spy
, 50);
746 client
._rfb_connection_state
= 'connecting';
747 client
._updateConnectionState('connected');
749 expect(spy
).to
.not
.have
.been
.called
;
750 expect(client
._disconnTimer
).to
.be
.null;
753 it('should set the rfb_connection_state', function () {
754 client
._rfb_connection_state
= 'connecting';
755 client
._updateConnectionState('connected');
756 expect(client
._rfb_connection_state
).to
.equal('connected');
759 it('should not change the state when we are disconnected', function () {
761 expect(client
._rfb_connection_state
).to
.equal('disconnected');
762 client
._updateConnectionState('connecting');
763 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
766 it('should ignore state changes to the same state', function () {
767 const connectSpy
= sinon
.spy();
768 client
.addEventListener("connect", connectSpy
);
770 expect(client
._rfb_connection_state
).to
.equal('connected');
771 client
._updateConnectionState('connected');
772 expect(connectSpy
).to
.not
.have
.been
.called
;
776 const disconnectSpy
= sinon
.spy();
777 client
.addEventListener("disconnect", disconnectSpy
);
779 expect(client
._rfb_connection_state
).to
.equal('disconnected');
780 client
._updateConnectionState('disconnected');
781 expect(disconnectSpy
).to
.not
.have
.been
.called
;
784 it('should ignore illegal state changes', function () {
785 const spy
= sinon
.spy();
786 client
.addEventListener("disconnect", spy
);
787 client
._updateConnectionState('disconnected');
788 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
789 expect(spy
).to
.not
.have
.been
.called
;
793 describe('#_fail', function () {
795 beforeEach(function () {
799 it('should close the WebSocket connection', function () {
800 sinon
.spy(client
._sock
, 'close');
802 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
805 it('should transition to disconnected', function () {
806 sinon
.spy(client
, '_updateConnectionState');
808 this.clock
.tick(2000);
809 expect(client
._updateConnectionState
).to
.have
.been
.called
;
810 expect(client
._rfb_connection_state
).to
.equal('disconnected');
813 it('should set clean_disconnect variable', function () {
814 client
._rfb_clean_disconnect
= true;
815 client
._rfb_connection_state
= 'connected';
817 expect(client
._rfb_clean_disconnect
).to
.be
.false;
820 it('should result in disconnect event with clean set to false', function () {
821 client
._rfb_connection_state
= 'connected';
822 const spy
= sinon
.spy();
823 client
.addEventListener("disconnect", spy
);
825 this.clock
.tick(2000);
826 expect(spy
).to
.have
.been
.calledOnce
;
827 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
833 describe('Connection States', function () {
834 describe('connecting', function () {
835 it('should open the websocket connection', function () {
836 const client
= new RFB(document
.createElement('div'),
837 'ws://HOST:8675/PATH');
838 sinon
.spy(client
._sock
, 'open');
840 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
844 describe('connected', function () {
846 beforeEach(function () {
850 it('should result in a connect event if state becomes connected', function () {
851 const spy
= sinon
.spy();
852 client
.addEventListener("connect", spy
);
853 client
._rfb_connection_state
= 'connecting';
854 client
._updateConnectionState('connected');
855 expect(spy
).to
.have
.been
.calledOnce
;
858 it('should not result in a connect event if the state is not "connected"', function () {
859 const spy
= sinon
.spy();
860 client
.addEventListener("connect", spy
);
861 client
._sock
._websocket
.open
= () => {}; // explicitly don't call onopen
862 client
._updateConnectionState('connecting');
863 expect(spy
).to
.not
.have
.been
.called
;
867 describe('disconnecting', function () {
869 beforeEach(function () {
873 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
874 sinon
.spy(client
, '_updateConnectionState');
875 client
._sock
._websocket
.close
= () => {}; // explicitly don't call onclose
876 client
._updateConnectionState('disconnecting');
877 this.clock
.tick(3 * 1000);
878 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
879 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
880 expect(client
._rfb_connection_state
).to
.equal("disconnected");
883 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
884 client
._updateConnectionState('disconnecting');
885 this.clock
.tick(3 * 1000 / 2);
886 client
._sock
._websocket
.close();
887 this.clock
.tick(3 * 1000 / 2 + 1);
888 expect(client
._rfb_connection_state
).to
.equal('disconnected');
891 it('should close the WebSocket connection', function () {
892 sinon
.spy(client
._sock
, 'close');
893 client
._updateConnectionState('disconnecting');
894 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
897 it('should not result in a disconnect event', function () {
898 const spy
= sinon
.spy();
899 client
.addEventListener("disconnect", spy
);
900 client
._sock
._websocket
.close
= () => {}; // explicitly don't call onclose
901 client
._updateConnectionState('disconnecting');
902 expect(spy
).to
.not
.have
.been
.called
;
906 describe('disconnected', function () {
908 beforeEach(function () {
909 client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
912 it('should result in a disconnect event if state becomes "disconnected"', function () {
913 const spy
= sinon
.spy();
914 client
.addEventListener("disconnect", spy
);
915 client
._rfb_connection_state
= 'disconnecting';
916 client
._updateConnectionState('disconnected');
917 expect(spy
).to
.have
.been
.calledOnce
;
918 expect(spy
.args
[0][0].detail
.clean
).to
.be
.true;
921 it('should result in a disconnect event without msg when no reason given', function () {
922 const spy
= sinon
.spy();
923 client
.addEventListener("disconnect", spy
);
924 client
._rfb_connection_state
= 'disconnecting';
925 client
._rfb_disconnect_reason
= "";
926 client
._updateConnectionState('disconnected');
927 expect(spy
).to
.have
.been
.calledOnce
;
928 expect(spy
.args
[0].length
).to
.equal(1);
933 describe('Protocol Initialization States', function () {
935 beforeEach(function () {
937 client
._rfb_connection_state
= 'connecting';
940 describe('ProtocolVersion', function () {
941 function send_ver(ver
, client
) {
942 const arr
= new Uint8Array(12);
943 for (let i
= 0; i
< ver
.length
; i
++) {
944 arr
[i
+4] = ver
.charCodeAt(i
);
946 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
948 client
._sock
._websocket
._receive_data(arr
);
951 describe('version parsing', function () {
952 it('should interpret version 003.003 as version 3.3', function () {
953 send_ver('003.003', client
);
954 expect(client
._rfb_version
).to
.equal(3.3);
957 it('should interpret version 003.006 as version 3.3', function () {
958 send_ver('003.006', client
);
959 expect(client
._rfb_version
).to
.equal(3.3);
962 it('should interpret version 003.889 as version 3.3', function () {
963 send_ver('003.889', client
);
964 expect(client
._rfb_version
).to
.equal(3.3);
967 it('should interpret version 003.007 as version 3.7', function () {
968 send_ver('003.007', client
);
969 expect(client
._rfb_version
).to
.equal(3.7);
972 it('should interpret version 003.008 as version 3.8', function () {
973 send_ver('003.008', client
);
974 expect(client
._rfb_version
).to
.equal(3.8);
977 it('should interpret version 004.000 as version 3.8', function () {
978 send_ver('004.000', client
);
979 expect(client
._rfb_version
).to
.equal(3.8);
982 it('should interpret version 004.001 as version 3.8', function () {
983 send_ver('004.001', client
);
984 expect(client
._rfb_version
).to
.equal(3.8);
987 it('should interpret version 005.000 as version 3.8', function () {
988 send_ver('005.000', client
);
989 expect(client
._rfb_version
).to
.equal(3.8);
992 it('should fail on an invalid version', function () {
993 sinon
.spy(client
, "_fail");
994 send_ver('002.000', client
);
995 expect(client
._fail
).to
.have
.been
.calledOnce
;
999 it('should send back the interpreted version', function () {
1000 send_ver('004.000', client
);
1002 const expected_str
= 'RFB 003.008\n';
1003 const expected
= [];
1004 for (let i
= 0; i
< expected_str
.length
; i
++) {
1005 expected
[i
] = expected_str
.charCodeAt(i
);
1008 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1011 it('should transition to the Security state on successful negotiation', function () {
1012 send_ver('003.008', client
);
1013 expect(client
._rfb_init_state
).to
.equal('Security');
1016 describe('Repeater', function () {
1017 beforeEach(function () {
1018 client
= make_rfb('wss://host:8675', { repeaterID
: "12345" });
1019 client
._rfb_connection_state
= 'connecting';
1022 it('should interpret version 000.000 as a repeater', function () {
1023 send_ver('000.000', client
);
1024 expect(client
._rfb_version
).to
.equal(0);
1026 const sent_data
= client
._sock
._websocket
._get_sent_data();
1027 expect(new Uint8Array(sent_data
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
1028 expect(sent_data
).to
.have
.length(250);
1031 it('should handle two step repeater negotiation', function () {
1032 send_ver('000.000', client
);
1033 send_ver('003.008', client
);
1034 expect(client
._rfb_version
).to
.equal(3.8);
1039 describe('Security', function () {
1040 beforeEach(function () {
1041 client
._rfb_init_state
= 'Security';
1044 it('should simply receive the auth scheme when for versions < 3.7', function () {
1045 client
._rfb_version
= 3.6;
1046 const auth_scheme_raw
= [1, 2, 3, 4];
1047 const auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
1048 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
1049 client
._sock
._websocket
._receive_data(new Uint8Array(auth_scheme_raw
));
1050 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
1053 it('should prefer no authentication is possible', function () {
1054 client
._rfb_version
= 3.7;
1055 const auth_schemes
= [2, 1, 3];
1056 client
._sock
._websocket
._receive_data(new Uint8Array(auth_schemes
));
1057 expect(client
._rfb_auth_scheme
).to
.equal(1);
1058 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
1061 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
1062 client
._rfb_version
= 3.7;
1063 const auth_schemes
= [2, 22, 16];
1064 client
._sock
._websocket
._receive_data(new Uint8Array(auth_schemes
));
1065 expect(client
._rfb_auth_scheme
).to
.equal(22);
1066 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
1069 it('should fail if there are no supported schemes for versions >= 3.7', function () {
1070 sinon
.spy(client
, "_fail");
1071 client
._rfb_version
= 3.7;
1072 const auth_schemes
= [1, 32];
1073 client
._sock
._websocket
._receive_data(new Uint8Array(auth_schemes
));
1074 expect(client
._fail
).to
.have
.been
.calledOnce
;
1077 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
1078 client
._rfb_version
= 3.7;
1079 const failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1080 sinon
.spy(client
, '_fail');
1081 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1083 expect(client
._fail
).to
.have
.been
.calledOnce
;
1084 expect(client
._fail
).to
.have
.been
.calledWith(
1085 'Security negotiation failed on no security types (reason: whoops)');
1088 it('should transition to the Authentication state and continue on successful negotiation', function () {
1089 client
._rfb_version
= 3.7;
1090 const auth_schemes
= [1, 1];
1091 client
._negotiate_authentication
= sinon
.spy();
1092 client
._sock
._websocket
._receive_data(new Uint8Array(auth_schemes
));
1093 expect(client
._rfb_init_state
).to
.equal('Authentication');
1094 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
1098 describe('Authentication', function () {
1099 beforeEach(function () {
1100 client
._rfb_init_state
= 'Security';
1103 function send_security(type
, cl
) {
1104 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
1107 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
1108 client
._rfb_version
= 3.6;
1109 const err_msg
= "Whoopsies";
1110 const data
= [0, 0, 0, 0];
1111 const err_len
= err_msg
.length
;
1112 push32(data
, err_len
);
1113 for (let i
= 0; i
< err_len
; i
++) {
1114 data
.push(err_msg
.charCodeAt(i
));
1117 sinon
.spy(client
, '_fail');
1118 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1119 expect(client
._fail
).to
.have
.been
.calledWith(
1120 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
1123 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
1124 client
._rfb_version
= 3.8;
1125 send_security(1, client
);
1126 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1129 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
1130 client
._rfb_version
= 3.7;
1131 send_security(1, client
);
1132 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1135 it('should fail on an unknown auth scheme', function () {
1136 sinon
.spy(client
, "_fail");
1137 client
._rfb_version
= 3.8;
1138 send_security(57, client
);
1139 expect(client
._fail
).to
.have
.been
.calledOnce
;
1142 describe('VNC Authentication (type 2) Handler', function () {
1143 beforeEach(function () {
1144 client
._rfb_init_state
= 'Security';
1145 client
._rfb_version
= 3.8;
1148 it('should fire the credentialsrequired event if missing a password', function () {
1149 const spy
= sinon
.spy();
1150 client
.addEventListener("credentialsrequired", spy
);
1151 send_security(2, client
);
1153 const challenge
= [];
1154 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1155 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1157 expect(client
._rfb_credentials
).to
.be
.empty
;
1158 expect(spy
).to
.have
.been
.calledOnce
;
1159 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
1162 it('should encrypt the password with DES and then send it back', function () {
1163 client
._rfb_credentials
= { password
: 'passwd' };
1164 send_security(2, client
);
1165 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
1167 const challenge
= [];
1168 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1169 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1171 const des_pass
= RFB
.genDES('passwd', challenge
);
1172 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
1175 it('should transition to SecurityResult immediately after sending the password', function () {
1176 client
._rfb_credentials
= { password
: 'passwd' };
1177 send_security(2, client
);
1179 const challenge
= [];
1180 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1181 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
1183 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1187 describe('XVP Authentication (type 22) Handler', function () {
1188 beforeEach(function () {
1189 client
._rfb_init_state
= 'Security';
1190 client
._rfb_version
= 3.8;
1193 it('should fall through to standard VNC authentication upon completion', function () {
1194 client
._rfb_credentials
= { username
: 'user',
1196 password
: 'password' };
1197 client
._negotiate_std_vnc_auth
= sinon
.spy();
1198 send_security(22, client
);
1199 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1202 it('should fire the credentialsrequired event if all credentials are missing', function () {
1203 const spy
= sinon
.spy();
1204 client
.addEventListener("credentialsrequired", spy
);
1205 client
._rfb_credentials
= {};
1206 send_security(22, client
);
1208 expect(client
._rfb_credentials
).to
.be
.empty
;
1209 expect(spy
).to
.have
.been
.calledOnce
;
1210 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1213 it('should fire the credentialsrequired event if some credentials are missing', function () {
1214 const spy
= sinon
.spy();
1215 client
.addEventListener("credentialsrequired", spy
);
1216 client
._rfb_credentials
= { username
: 'user',
1218 send_security(22, client
);
1220 expect(spy
).to
.have
.been
.calledOnce
;
1221 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1224 it('should send user and target separately', function () {
1225 client
._rfb_credentials
= { username
: 'user',
1227 password
: 'password' };
1228 client
._negotiate_std_vnc_auth
= sinon
.spy();
1230 send_security(22, client
);
1232 const expected
= [22, 4, 6]; // auth selection, len user, len target
1233 for (let i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
1235 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1239 describe('TightVNC Authentication (type 16) Handler', function () {
1240 beforeEach(function () {
1241 client
._rfb_init_state
= 'Security';
1242 client
._rfb_version
= 3.8;
1243 send_security(16, client
);
1244 client
._sock
._websocket
._get_sent_data(); // skip the security reply
1247 function send_num_str_pairs(pairs
, client
) {
1249 push32(data
, pairs
.length
);
1251 for (let i
= 0; i
< pairs
.length
; i
++) {
1252 push32(data
, pairs
[i
][0]);
1253 for (let j
= 0; j
< 4; j
++) {
1254 data
.push(pairs
[i
][1].charCodeAt(j
));
1256 for (let j
= 0; j
< 8; j
++) {
1257 data
.push(pairs
[i
][2].charCodeAt(j
));
1261 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1264 it('should skip tunnel negotiation if no tunnels are requested', function () {
1265 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1266 expect(client
._rfb_tightvnc
).to
.be
.true;
1269 it('should fail if no supported tunnels are listed', function () {
1270 sinon
.spy(client
, "_fail");
1271 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
1272 expect(client
._fail
).to
.have
.been
.calledOnce
;
1275 it('should choose the notunnel tunnel type', function () {
1276 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
1277 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1280 it('should choose the notunnel tunnel type for Siemens devices', function () {
1281 send_num_str_pairs([[1, 'SICR', 'SCHANNEL'], [2, 'SICR', 'SCHANLPW']], client
);
1282 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1285 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
1286 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
1287 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
1288 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
1289 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1290 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1293 /*it('should attempt to use VNC auth over no auth when possible', function () {
1294 client._rfb_tightvnc = true;
1295 client._negotiate_std_vnc_auth = sinon.spy();
1296 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
1297 expect(client._sock).to.have.sent([0, 0, 0, 1]);
1298 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
1299 expect(client._rfb_auth_scheme).to.equal(2);
1300 });*/ // while this would make sense, the original code doesn't actually do this
1302 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
1303 client
._rfb_tightvnc
= true;
1304 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
1305 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1306 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
1309 it('should accept VNC authentication and transition to that', function () {
1310 client
._rfb_tightvnc
= true;
1311 client
._negotiate_std_vnc_auth
= sinon
.spy();
1312 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
1313 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1314 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1315 expect(client
._rfb_auth_scheme
).to
.equal(2);
1318 it('should fail if there are no supported auth types', function () {
1319 sinon
.spy(client
, "_fail");
1320 client
._rfb_tightvnc
= true;
1321 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
1322 expect(client
._fail
).to
.have
.been
.calledOnce
;
1327 describe('SecurityResult', function () {
1328 beforeEach(function () {
1329 client
._rfb_init_state
= 'SecurityResult';
1332 it('should fall through to ServerInitialisation on a response code of 0', function () {
1333 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1334 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1337 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1338 client
._rfb_version
= 3.8;
1339 sinon
.spy(client
, '_fail');
1340 const failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1341 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1342 expect(client
._fail
).to
.have
.been
.calledWith(
1343 'Security negotiation failed on security result (reason: whoops)');
1346 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1347 sinon
.spy(client
, '_fail');
1348 client
._rfb_version
= 3.7;
1349 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
1350 expect(client
._fail
).to
.have
.been
.calledWith(
1351 'Security handshake failed');
1354 it('should result in securityfailure event when receiving a non zero status', function () {
1355 const spy
= sinon
.spy();
1356 client
.addEventListener("securityfailure", spy
);
1357 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
1358 expect(spy
).to
.have
.been
.calledOnce
;
1359 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1362 it('should include reason when provided in securityfailure event', function () {
1363 client
._rfb_version
= 3.8;
1364 const spy
= sinon
.spy();
1365 client
.addEventListener("securityfailure", spy
);
1366 const failure_data
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
1367 32, 102, 97, 105, 108, 117, 114, 101];
1368 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1369 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1370 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
1373 it('should not include reason when length is zero in securityfailure event', function () {
1374 client
._rfb_version
= 3.9;
1375 const spy
= sinon
.spy();
1376 client
.addEventListener("securityfailure", spy
);
1377 const failure_data
= [0, 0, 0, 1, 0, 0, 0, 0];
1378 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1379 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1380 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1383 it('should not include reason in securityfailure event for version < 3.8', function () {
1384 client
._rfb_version
= 3.6;
1385 const spy
= sinon
.spy();
1386 client
.addEventListener("securityfailure", spy
);
1387 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 2]));
1388 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1389 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1393 describe('ClientInitialisation', function () {
1394 it('should transition to the ServerInitialisation state', function () {
1395 const client
= make_rfb();
1396 client
._rfb_connection_state
= 'connecting';
1397 client
._rfb_init_state
= 'SecurityResult';
1398 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1399 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1402 it('should send 1 if we are in shared mode', function () {
1403 const client
= make_rfb('wss://host:8675', { shared
: true });
1404 client
._rfb_connection_state
= 'connecting';
1405 client
._rfb_init_state
= 'SecurityResult';
1406 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1407 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1410 it('should send 0 if we are not in shared mode', function () {
1411 const client
= make_rfb('wss://host:8675', { shared
: false });
1412 client
._rfb_connection_state
= 'connecting';
1413 client
._rfb_init_state
= 'SecurityResult';
1414 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1415 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1419 describe('ServerInitialisation', function () {
1420 beforeEach(function () {
1421 client
._rfb_init_state
= 'ServerInitialisation';
1424 function send_server_init(opts
, client
) {
1425 const full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1426 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1427 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1428 for (let opt
in opts
) {
1429 full_opts
[opt
] = opts
[opt
];
1433 push16(data
, full_opts
.width
);
1434 push16(data
, full_opts
.height
);
1436 data
.push(full_opts
.bpp
);
1437 data
.push(full_opts
.depth
);
1438 data
.push(full_opts
.big_endian
);
1439 data
.push(full_opts
.true_color
);
1441 push16(data
, full_opts
.red_max
);
1442 push16(data
, full_opts
.green_max
);
1443 push16(data
, full_opts
.blue_max
);
1444 push8(data
, full_opts
.red_shift
);
1445 push8(data
, full_opts
.green_shift
);
1446 push8(data
, full_opts
.blue_shift
);
1453 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1455 const name_data
= [];
1457 pushString(name_data
, full_opts
.name
);
1458 push32(name_len
, name_data
.length
);
1460 client
._sock
._websocket
._receive_data(new Uint8Array(name_len
));
1461 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1464 it('should set the framebuffer width and height', function () {
1465 send_server_init({ width
: 32, height
: 84 }, client
);
1466 expect(client
._fb_width
).to
.equal(32);
1467 expect(client
._fb_height
).to
.equal(84);
1470 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1472 it('should set the framebuffer name and call the callback', function () {
1473 const spy
= sinon
.spy();
1474 client
.addEventListener("desktopname", spy
);
1475 send_server_init({ name
: 'som€ nam€' }, client
);
1477 expect(client
._fb_name
).to
.equal('som€ nam€');
1478 expect(spy
).to
.have
.been
.calledOnce
;
1479 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
1482 it('should handle the extended init message of the tight encoding', function () {
1483 // NB(sross): we don't actually do anything with it, so just test that we can
1484 // read it w/o throwing an error
1485 client
._rfb_tightvnc
= true;
1486 send_server_init({}, client
);
1488 const tight_data
= [];
1489 push16(tight_data
, 1);
1490 push16(tight_data
, 2);
1491 push16(tight_data
, 3);
1492 push16(tight_data
, 0);
1493 for (let i
= 0; i
< 16 + 32 + 48; i
++) {
1496 client
._sock
._websocket
._receive_data(new Uint8Array(tight_data
));
1498 expect(client
._rfb_connection_state
).to
.equal('connected');
1501 it('should resize the display', function () {
1502 sinon
.spy(client
._display
, 'resize');
1503 send_server_init({ width
: 27, height
: 32 }, client
);
1505 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1506 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1509 it('should grab the mouse and keyboard', function () {
1510 sinon
.spy(client
._keyboard
, 'grab');
1511 sinon
.spy(client
._mouse
, 'grab');
1512 send_server_init({}, client
);
1513 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1514 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1517 describe('Initial Update Request', function () {
1518 beforeEach(function () {
1519 sinon
.spy(RFB
.messages
, "pixelFormat");
1520 sinon
.spy(RFB
.messages
, "clientEncodings");
1521 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1524 afterEach(function () {
1525 RFB
.messages
.pixelFormat
.restore();
1526 RFB
.messages
.clientEncodings
.restore();
1527 RFB
.messages
.fbUpdateRequest
.restore();
1530 // TODO(directxman12): test the various options in this configuration matrix
1531 it('should reply with the pixel format, client encodings, and initial update request', function () {
1532 send_server_init({ width
: 27, height
: 32 }, client
);
1534 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1535 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1536 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1537 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1538 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1539 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1540 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1541 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1544 it('should reply with restricted settings for Intel AMT servers', function () {
1545 send_server_init({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1547 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1548 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1549 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1550 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1551 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1552 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1553 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1554 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1555 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1559 it('should transition to the "connected" state', function () {
1560 send_server_init({}, client
);
1561 expect(client
._rfb_connection_state
).to
.equal('connected');
1566 describe('Protocol Message Processing After Completing Initialization', function () {
1569 beforeEach(function () {
1570 client
= make_rfb();
1571 client
._fb_name
= 'some device';
1572 client
._fb_width
= 640;
1573 client
._fb_height
= 20;
1576 describe('Framebuffer Update Handling', function () {
1577 const target_data_arr
= [
1578 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1579 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1580 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1581 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1585 const target_data_check_arr
= [
1586 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1587 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1588 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1589 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1591 let target_data_check
;
1593 before(function () {
1594 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1595 target_data
= new Uint8Array(target_data_arr
);
1596 target_data_check
= new Uint8Array(target_data_check_arr
);
1599 function send_fbu_msg(rect_info
, rect_data
, client
, rect_cnt
) {
1602 if (!rect_cnt
|| rect_cnt
> -1) {
1604 data
.push(0); // msg type
1605 data
.push(0); // padding
1606 push16(data
, rect_cnt
|| rect_data
.length
);
1609 for (let i
= 0; i
< rect_data
.length
; i
++) {
1611 push16(data
, rect_info
[i
].x
);
1612 push16(data
, rect_info
[i
].y
);
1613 push16(data
, rect_info
[i
].width
);
1614 push16(data
, rect_info
[i
].height
);
1615 push32(data
, rect_info
[i
].encoding
);
1617 data
= data
.concat(rect_data
[i
]);
1620 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1623 it('should send an update request if there is sufficient data', function () {
1624 const expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1625 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1627 client
._framebufferUpdate
= () => true;
1628 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1630 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1633 it('should not send an update request if we need more data', function () {
1634 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1635 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1638 it('should resume receiving an update if we previously did not have enough data', function () {
1639 const expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1640 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1642 // just enough to set FBU.rects
1643 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1644 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1646 client
._framebufferUpdate = function () { this._sock
.rQskipBytes(1); return true; }; // we magically have enough data
1647 // 247 should *not* be used as the message type here
1648 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1649 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1652 it('should not send a request in continuous updates mode', function () {
1653 client
._enabledContinuousUpdates
= true;
1654 client
._framebufferUpdate
= () => true;
1655 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1657 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1660 it('should fail on an unsupported encoding', function () {
1661 sinon
.spy(client
, "_fail");
1662 const rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1663 send_fbu_msg([rect_info
], [[]], client
);
1664 expect(client
._fail
).to
.have
.been
.calledOnce
;
1667 it('should be able to pause and resume receiving rects if not enought data', function () {
1668 // seed some initial data to copy
1669 client
._fb_width
= 4;
1670 client
._fb_height
= 4;
1671 client
._display
.resize(4, 4);
1672 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1674 const info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1675 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1676 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1677 const rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1678 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1679 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1680 expect(client
._display
).to
.have
.displayed(target_data_check
);
1683 describe('Message Encoding Handlers', function () {
1684 beforeEach(function () {
1685 // a really small frame
1686 client
._fb_width
= 4;
1687 client
._fb_height
= 4;
1688 client
._fb_depth
= 24;
1689 client
._display
.resize(4, 4);
1692 it('should handle the RAW encoding', function () {
1693 const info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1694 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1695 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1696 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1699 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1700 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1701 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1702 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1703 send_fbu_msg(info
, rects
, client
);
1704 expect(client
._display
).to
.have
.displayed(target_data
);
1707 it('should handle the RAW encoding in low colour mode', function () {
1708 const info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1709 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1710 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1711 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1713 [0x03, 0x03, 0x03, 0x03],
1714 [0x0c, 0x0c, 0x0c, 0x0c],
1715 [0x0c, 0x0c, 0x03, 0x03],
1716 [0x0c, 0x0c, 0x03, 0x03]];
1717 client
._fb_depth
= 8;
1718 send_fbu_msg(info
, rects
, client
);
1719 expect(client
._display
).to
.have
.displayed(target_data_check
);
1722 it('should handle the COPYRECT encoding', function () {
1723 // seed some initial data to copy
1724 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1726 const info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1727 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1728 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1729 const rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1730 send_fbu_msg(info
, rects
, client
);
1731 expect(client
._display
).to
.have
.displayed(target_data_check
);
1734 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1735 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1737 it('should handle the RRE encoding', function () {
1738 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1740 push32(rect
, 2); // 2 subrects
1741 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1742 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1746 push16(rect
, 0); // x: 0
1747 push16(rect
, 0); // y: 0
1748 push16(rect
, 2); // width: 2
1749 push16(rect
, 2); // height: 2
1750 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1754 push16(rect
, 2); // x: 2
1755 push16(rect
, 2); // y: 2
1756 push16(rect
, 2); // width: 2
1757 push16(rect
, 2); // height: 2
1759 send_fbu_msg(info
, [rect
], client
);
1760 expect(client
._display
).to
.have
.displayed(target_data_check
);
1763 describe('the HEXTILE encoding handler', function () {
1764 it('should handle a tile with fg, bg specified, normal subrects', function () {
1765 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1767 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1768 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1769 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1773 rect
.push(2); // 2 subrects
1774 rect
.push(0); // x: 0, y: 0
1775 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1776 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1777 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1778 send_fbu_msg(info
, [rect
], client
);
1779 expect(client
._display
).to
.have
.displayed(target_data_check
);
1782 it('should handle a raw tile', function () {
1783 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1785 rect
.push(0x01); // raw
1786 for (let i
= 0; i
< target_data
.length
; i
+= 4) {
1787 rect
.push(target_data
[i
+ 2]);
1788 rect
.push(target_data
[i
+ 1]);
1789 rect
.push(target_data
[i
]);
1790 rect
.push(target_data
[i
+ 3]);
1792 send_fbu_msg(info
, [rect
], client
);
1793 expect(client
._display
).to
.have
.displayed(target_data
);
1796 it('should handle a tile with only bg specified (solid bg)', function () {
1797 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1800 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1801 send_fbu_msg(info
, [rect
], client
);
1803 const expected
= [];
1804 for (let i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1805 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1808 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1809 // set the width so we can have two tiles
1810 client
._fb_width
= 8;
1811 client
._display
.resize(8, 4);
1813 const info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1819 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1821 // send an empty frame
1824 send_fbu_msg(info
, [rect
], client
);
1826 const expected
= [];
1827 for (let i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1828 for (let i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1829 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1832 it('should handle a tile with bg and coloured subrects', function () {
1833 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1835 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1836 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1837 rect
.push(2); // 2 subrects
1838 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1842 rect
.push(0); // x: 0, y: 0
1843 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1844 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1848 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1849 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1850 send_fbu_msg(info
, [rect
], client
);
1851 expect(client
._display
).to
.have
.displayed(target_data_check
);
1854 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1855 client
._fb_width
= 4;
1856 client
._fb_height
= 17;
1857 client
._display
.resize(4, 17);
1859 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1861 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1862 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1863 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1867 rect
.push(8); // 8 subrects
1868 for (let i
= 0; i
< 4; i
++) {
1869 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1870 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1871 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1872 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1874 rect
.push(0x08); // anysubrects
1875 rect
.push(1); // 1 subrect
1876 rect
.push(0); // x: 0, y: 0
1877 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1878 send_fbu_msg(info
, [rect
], client
);
1881 for (let i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1882 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1883 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1886 it('should fail on an invalid subencoding', function () {
1887 sinon
.spy(client
, "_fail");
1888 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1889 const rects
= [[45]]; // an invalid subencoding
1890 send_fbu_msg(info
, rects
, client
);
1891 expect(client
._fail
).to
.have
.been
.calledOnce
;
1895 it
.skip('should handle the TIGHT encoding', function () {
1896 // TODO(directxman12): test this
1899 it
.skip('should handle the TIGHT_PNG encoding', function () {
1900 // TODO(directxman12): test this
1903 it('should handle the DesktopSize pseduo-encoding', function () {
1904 sinon
.spy(client
._display
, 'resize');
1905 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1907 expect(client
._fb_width
).to
.equal(20);
1908 expect(client
._fb_height
).to
.equal(50);
1910 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1911 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1914 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1915 beforeEach(function () {
1916 // a really small frame
1917 client
._fb_width
= 4;
1918 client
._fb_height
= 4;
1919 client
._display
.resize(4, 4);
1920 sinon
.spy(client
._display
, 'resize');
1923 function make_screen_data(nr_of_screens
) {
1925 push8(data
, nr_of_screens
); // number-of-screens
1926 push8(data
, 0); // padding
1927 push16(data
, 0); // padding
1928 for (let i
=0; i
<nr_of_screens
; i
+= 1) {
1929 push32(data
, 0); // id
1930 push16(data
, 0); // x-position
1931 push16(data
, 0); // y-position
1932 push16(data
, 20); // width
1933 push16(data
, 50); // height
1934 push32(data
, 0); // flags
1939 it('should handle a resize requested by this client', function () {
1940 const reason_for_change
= 1; // requested by this client
1941 const status_code
= 0; // No error
1943 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1944 width
: 20, height
: 50, encoding
: -308 }],
1945 make_screen_data(1), client
);
1947 expect(client
._fb_width
).to
.equal(20);
1948 expect(client
._fb_height
).to
.equal(50);
1950 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1951 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1954 it('should handle a resize requested by another client', function () {
1955 const reason_for_change
= 2; // requested by another client
1956 const status_code
= 0; // No error
1958 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1959 width
: 20, height
: 50, encoding
: -308 }],
1960 make_screen_data(1), client
);
1962 expect(client
._fb_width
).to
.equal(20);
1963 expect(client
._fb_height
).to
.equal(50);
1965 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1966 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1969 it('should be able to recieve requests which contain data for multiple screens', function () {
1970 const reason_for_change
= 2; // requested by another client
1971 const status_code
= 0; // No error
1973 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1974 width
: 60, height
: 50, encoding
: -308 }],
1975 make_screen_data(3), client
);
1977 expect(client
._fb_width
).to
.equal(60);
1978 expect(client
._fb_height
).to
.equal(50);
1980 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1981 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1984 it('should not handle a failed request', function () {
1985 const reason_for_change
= 1; // requested by this client
1986 const status_code
= 1; // Resize is administratively prohibited
1988 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1989 width
: 20, height
: 50, encoding
: -308 }],
1990 make_screen_data(1), client
);
1992 expect(client
._fb_width
).to
.equal(4);
1993 expect(client
._fb_height
).to
.equal(4);
1995 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1999 describe('the Cursor pseudo-encoding handler', function () {
2000 beforeEach(function () {
2001 sinon
.spy(client
._cursor
, 'change');
2004 it('should handle a standard cursor', function () {
2005 const info
= { x
: 5, y
: 7,
2006 width
: 4, height
: 4,
2011 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2012 push32(rect
, 0x11223300);
2014 push32(rect
, 0xa0a0a0a0);
2016 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
2017 push32(expected
, 0x332211ff);
2018 push32(expected
, 0x33221100);
2020 expected
= new Uint8Array(expected
);
2022 send_fbu_msg([info
], [rect
], client
);
2024 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2025 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
2028 it('should handle an empty cursor', function () {
2029 const info
= { x
: 0, y
: 0,
2030 width
: 0, height
: 0,
2034 send_fbu_msg([info
], [rect
], client
);
2036 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2037 expect(client
._cursor
.change
).to
.have
.been
.calledWith(new Uint8Array
, 0, 0, 0, 0);
2040 it('should handle a transparent cursor', function () {
2041 const info
= { x
: 5, y
: 7,
2042 width
: 4, height
: 4,
2047 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2048 push32(rect
, 0x11223300);
2050 push32(rect
, 0x00000000);
2052 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2053 push32(expected
, 0x33221100);
2055 expected
= new Uint8Array(expected
);
2057 send_fbu_msg([info
], [rect
], client
);
2059 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2060 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
2063 describe('dot for empty cursor', function () {
2064 beforeEach(function () {
2065 client
.showDotCursor
= true;
2066 // Was called when we enabled dot cursor
2067 client
._cursor
.change
.resetHistory();
2070 it('should show a standard cursor', function () {
2071 const info
= { x
: 5, y
: 7,
2072 width
: 4, height
: 4,
2077 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2078 push32(rect
, 0x11223300);
2080 push32(rect
, 0xa0a0a0a0);
2082 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
2083 push32(expected
, 0x332211ff);
2084 push32(expected
, 0x33221100);
2086 expected
= new Uint8Array(expected
);
2088 send_fbu_msg([info
], [rect
], client
);
2090 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2091 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
2094 it('should handle an empty cursor', function () {
2095 const info
= { x
: 0, y
: 0,
2096 width
: 0, height
: 0,
2099 const dot
= RFB
.cursors
.dot
;
2101 send_fbu_msg([info
], [rect
], client
);
2103 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2104 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
2111 it('should handle a transparent cursor', function () {
2112 const info
= { x
: 5, y
: 7,
2113 width
: 4, height
: 4,
2116 const dot
= RFB
.cursors
.dot
;
2118 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2119 push32(rect
, 0x11223300);
2121 push32(rect
, 0x00000000);
2123 send_fbu_msg([info
], [rect
], client
);
2125 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2126 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
2135 describe('the VMware Cursor pseudo-encoding handler', function () {
2136 beforeEach(function () {
2137 sinon
.spy(client
._cursor
, 'change');
2139 afterEach(function () {
2140 client
._cursor
.change
.resetHistory();
2143 it('should handle the VMware cursor pseudo-encoding', function () {
2144 let data
= [0x00, 0x00, 0xff, 0,
2145 0x00, 0xff, 0x00, 0,
2146 0x00, 0xff, 0x00, 0,
2147 0x00, 0x00, 0xff, 0];
2153 for (let i
= 0; i
< data
.length
; i
++) {
2154 push8(rect
, data
[i
]);
2157 for (let i
= 0; i
< data
.length
; i
++) {
2158 push8(rect
, data
[i
]);
2161 send_fbu_msg([{ x
: 0, y
: 0, width
: 2, height
: 2,
2162 encoding
: 0x574d5664}],
2164 expect(client
._FBU
.rects
).to
.equal(0);
2167 it('should handle insufficient cursor pixel data', function () {
2169 // Specified 14x23 pixels for the cursor,
2170 // but only send 2x2 pixels worth of data
2173 let data
= [0x00, 0x00, 0xff, 0,
2174 0x00, 0xff, 0x00, 0];
2181 for (let i
= 0; i
< data
.length
; i
++) {
2182 push8(rect
, data
[i
]);
2185 for (let i
= 0; i
< data
.length
; i
++) {
2186 push8(rect
, data
[i
]);
2189 send_fbu_msg([{ x
: 0, y
: 0, width
: w
, height
: h
,
2190 encoding
: 0x574d5664}],
2193 // expect one FBU to remain unhandled
2194 expect(client
._FBU
.rects
).to
.equal(1);
2197 it('should update the cursor when type is classic', function () {
2199 [0xff, 0xff, 0xff, 0xff, //Transparent
2200 0xff, 0xff, 0xff, 0xff, //Transparent
2201 0x00, 0x00, 0x00, 0x00, //Opaque
2202 0xff, 0xff, 0xff, 0xff]; //Inverted
2205 [0x00, 0x00, 0x00, 0x00, //Transparent
2206 0x00, 0x00, 0x00, 0x00, //Transparent
2207 0x11, 0x22, 0x33, 0x44, //Opaque
2208 0xff, 0xff, 0xff, 0x44]; //Inverted
2211 push8(rect
, 0); //cursor_type
2212 push8(rect
, 0); //padding
2219 for (let i
= 0; i
< and_mask
.length
; i
++) {
2220 push8(rect
, and_mask
[i
]);
2223 for (let i
= 0; i
< xor_mask
.length
; i
++) {
2224 push8(rect
, xor_mask
[i
]);
2227 let expected_rgba
= [0x00, 0x00, 0x00, 0x00,
2228 0x00, 0x00, 0x00, 0x00,
2229 0x33, 0x22, 0x11, 0xff,
2230 0x00, 0x00, 0x00, 0xff];
2232 send_fbu_msg([{ x
: hotx
, y
: hoty
,
2233 width
: w
, height
: h
,
2234 encoding
: 0x574d5664}],
2237 expect(client
._cursor
.change
)
2238 .to
.have
.been
.calledOnce
;
2239 expect(client
._cursor
.change
)
2240 .to
.have
.been
.calledWith(expected_rgba
,
2245 it('should update the cursor when type is alpha', function () {
2246 let data
= [0xee, 0x55, 0xff, 0x00, // bgra
2247 0x00, 0xff, 0x00, 0xff,
2248 0x00, 0xff, 0x00, 0x22,
2249 0x00, 0xff, 0x00, 0x22,
2250 0x00, 0xff, 0x00, 0x22,
2251 0x00, 0x00, 0xff, 0xee];
2253 push8(rect
, 1); //cursor_type
2254 push8(rect
, 0); //padding
2260 for (let i
= 0; i
< data
.length
; i
++) {
2261 push8(rect
, data
[i
]);
2264 let expected_rgba
= [0xff, 0x55, 0xee, 0x00,
2265 0x00, 0xff, 0x00, 0xff,
2266 0x00, 0xff, 0x00, 0x22,
2267 0x00, 0xff, 0x00, 0x22,
2268 0x00, 0xff, 0x00, 0x22,
2269 0xff, 0x00, 0x00, 0xee];
2271 send_fbu_msg([{ x
: hotx
, y
: hoty
,
2272 width
: w
, height
: h
,
2273 encoding
: 0x574d5664}],
2276 expect(client
._cursor
.change
)
2277 .to
.have
.been
.calledOnce
;
2278 expect(client
._cursor
.change
)
2279 .to
.have
.been
.calledWith(expected_rgba
,
2284 it('should not update cursor when incorrect cursor type given', function () {
2286 push8(rect
, 3); // invalid cursor type
2287 push8(rect
, 0); // padding
2289 client
._cursor
.change
.resetHistory();
2290 send_fbu_msg([{ x
: 0, y
: 0, width
: 2, height
: 2,
2291 encoding
: 0x574d5664}],
2294 expect(client
._cursor
.change
)
2295 .to
.not
.have
.been
.called
;
2299 it('should handle the last_rect pseudo-encoding', function () {
2300 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
2301 expect(client
._FBU
.rects
).to
.equal(0);
2304 it('should handle the DesktopName pseudo-encoding', function () {
2307 pushString(data
, "som€ nam€");
2309 const spy
= sinon
.spy();
2310 client
.addEventListener("desktopname", spy
);
2312 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -307 }], [data
], client
);
2314 expect(client
._fb_name
).to
.equal('som€ nam€');
2315 expect(spy
).to
.have
.been
.calledOnce
;
2316 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
2321 describe('XVP Message Handling', function () {
2322 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
2323 const spy
= sinon
.spy();
2324 client
.addEventListener("capabilities", spy
);
2325 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
2326 expect(client
._rfb_xvp_ver
).to
.equal(10);
2327 expect(spy
).to
.have
.been
.calledOnce
;
2328 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
2329 expect(client
.capabilities
.power
).to
.be
.true;
2332 it('should fail on unknown XVP message types', function () {
2333 sinon
.spy(client
, "_fail");
2334 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
2335 expect(client
._fail
).to
.have
.been
.calledOnce
;
2339 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
2340 const expected_str
= 'cheese!';
2341 const data
= [3, 0, 0, 0];
2342 push32(data
, expected_str
.length
);
2343 for (let i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
2344 const spy
= sinon
.spy();
2345 client
.addEventListener("clipboard", spy
);
2347 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
2348 expect(spy
).to
.have
.been
.calledOnce
;
2349 expect(spy
.args
[0][0].detail
.text
).to
.equal(expected_str
);
2352 it('should fire the bell callback on Bell', function () {
2353 const spy
= sinon
.spy();
2354 client
.addEventListener("bell", spy
);
2355 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
2356 expect(spy
).to
.have
.been
.calledOnce
;
2359 it('should respond correctly to ServerFence', function () {
2360 const expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2361 const incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2363 const payload
= "foo\x00ab9";
2365 // ClientFence and ServerFence are identical in structure
2366 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
2367 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
2369 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
2371 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2373 expected_msg
._sQlen
= 0;
2374 incoming_msg
._sQlen
= 0;
2376 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
2377 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
2379 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
2381 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2384 it('should enable continuous updates on first EndOfContinousUpdates', function () {
2385 const expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2387 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
2389 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2391 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
2393 expect(client
._enabledContinuousUpdates
).to
.be
.true;
2394 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2397 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
2398 client
._enabledContinuousUpdates
= true;
2399 client
._supportsContinuousUpdates
= true;
2401 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
2403 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2406 it('should update continuous updates on resize', function () {
2407 const expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2408 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
2410 client
._resize(450, 160);
2412 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
2414 client
._enabledContinuousUpdates
= true;
2416 client
._resize(90, 700);
2418 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
2421 it('should fail on an unknown message type', function () {
2422 sinon
.spy(client
, "_fail");
2423 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
2424 expect(client
._fail
).to
.have
.been
.calledOnce
;
2428 describe('Asynchronous Events', function () {
2430 beforeEach(function () {
2431 client
= make_rfb();
2434 describe('Mouse event handlers', function () {
2435 it('should not send button messages in view-only mode', function () {
2436 client
._viewOnly
= true;
2437 sinon
.spy(client
._sock
, 'flush');
2438 client
._handleMouseButton(0, 0, 1, 0x001);
2439 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2442 it('should not send movement messages in view-only mode', function () {
2443 client
._viewOnly
= true;
2444 sinon
.spy(client
._sock
, 'flush');
2445 client
._handleMouseMove(0, 0);
2446 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2449 it('should send a pointer event on mouse button presses', function () {
2450 client
._handleMouseButton(10, 12, 1, 0x001);
2451 const pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush
: () => {}};
2452 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
2453 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2456 it('should send a mask of 1 on mousedown', function () {
2457 client
._handleMouseButton(10, 12, 1, 0x001);
2458 const pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush
: () => {}};
2459 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
2460 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2463 it('should send a mask of 0 on mouseup', function () {
2464 client
._mouse_buttonMask
= 0x001;
2465 client
._handleMouseButton(10, 12, 0, 0x001);
2466 const pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush
: () => {}};
2467 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
2468 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2471 it('should send a pointer event on mouse movement', function () {
2472 client
._handleMouseMove(10, 12);
2473 const pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush
: () => {}};
2474 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
2475 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2478 it('should set the button mask so that future mouse movements use it', function () {
2479 client
._handleMouseButton(10, 12, 1, 0x010);
2480 client
._handleMouseMove(13, 9);
2481 const pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush
: () => {}};
2482 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
2483 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
2484 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
2488 describe('Keyboard Event Handlers', function () {
2489 it('should send a key message on a key press', function () {
2490 client
._handleKeyEvent(0x41, 'KeyA', true);
2491 const key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
2492 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
2493 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
2496 it('should not send messages in view-only mode', function () {
2497 client
._viewOnly
= true;
2498 sinon
.spy(client
._sock
, 'flush');
2499 client
._handleKeyEvent('a', 'KeyA', true);
2500 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2504 describe('WebSocket event handlers', function () {
2506 it('should do nothing if we receive an empty message and have nothing in the queue', function () {
2507 client
._normal_msg
= sinon
.spy();
2508 client
._sock
._websocket
._receive_data(new Uint8Array([]));
2509 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
2512 it('should handle a message in the connected state as a normal message', function () {
2513 client
._normal_msg
= sinon
.spy();
2514 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2515 expect(client
._normal_msg
).to
.have
.been
.called
;
2518 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2519 client
._rfb_connection_state
= 'connecting';
2520 client
._rfb_init_state
= 'ProtocolVersion';
2521 client
._init_msg
= sinon
.spy();
2522 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2523 expect(client
._init_msg
).to
.have
.been
.called
;
2526 it('should process all normal messages directly', function () {
2527 const spy
= sinon
.spy();
2528 client
.addEventListener("bell", spy
);
2529 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2530 expect(spy
).to
.have
.been
.calledTwice
;
2534 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2535 client
= new RFB(document
.createElement('div'), 'wss://host:8675');
2537 client
._sock
._websocket
._open();
2538 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2541 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2542 sinon
.spy(client
, "_fail");
2543 client
._rfb_connection_state
= 'connected';
2544 client
._sock
._websocket
._open();
2545 expect(client
._fail
).to
.have
.been
.calledOnce
;
2549 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2550 const real
= client
._sock
._websocket
.close
;
2551 client
._sock
._websocket
.close
= () => {};
2552 client
.disconnect();
2553 expect(client
._rfb_connection_state
).to
.equal('disconnecting');
2554 client
._sock
._websocket
.close
= real
;
2555 client
._sock
._websocket
.close();
2556 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2559 it('should fail if we get a close event while connecting', function () {
2560 sinon
.spy(client
, "_fail");
2561 client
._rfb_connection_state
= 'connecting';
2562 client
._sock
._websocket
.close();
2563 expect(client
._fail
).to
.have
.been
.calledOnce
;
2566 it('should unregister close event handler', function () {
2567 sinon
.spy(client
._sock
, 'off');
2568 client
.disconnect();
2569 client
._sock
._websocket
.close();
2570 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2573 // error events do nothing