1 const expect
= chai
.expect
;
3 import RFB
from '../core/rfb.js';
4 import Websock
from '../core/websock.js';
5 import ZStream
from "../vendor/pako/lib/zlib/zstream.js";
6 import { deflateInit
, deflate
} from "../vendor/pako/lib/zlib/deflate.js";
7 import { encodings
} from '../core/encodings.js';
8 import { toUnsigned32bit
} from '../core/util/int.js';
9 import { encodeUTF8
} from '../core/util/strings.js';
11 import FakeWebSocket
from './fake.websocket.js';
13 /* UIEvent constructor polyfill for IE */
15 if (typeof window
.UIEvent
=== "function") return;
17 function UIEvent( event
, params
) {
18 params
= params
|| { bubbles
: false, cancelable
: false, view
: window
, detail
: undefined };
19 const evt
= document
.createEvent( 'UIEvent' );
20 evt
.initUIEvent( event
, params
.bubbles
, params
.cancelable
, params
.view
, params
.detail
);
24 UIEvent
.prototype = window
.UIEvent
.prototype;
26 window
.UIEvent
= UIEvent
;
29 function push8(arr
, num
) {
34 function push16(arr
, num
) {
36 arr
.push((num
>> 8) & 0xFF,
40 function push32(arr
, num
) {
42 arr
.push((num
>> 24) & 0xFF,
48 function pushString(arr
, string
) {
49 let utf8
= unescape(encodeURIComponent(string
));
50 for (let i
= 0; i
< utf8
.length
; i
++) {
51 arr
.push(utf8
.charCodeAt(i
));
55 function deflateWithSize(data
) {
56 // Adds the size of the string in front before deflating
59 unCompData
.push((data
.length
>> 24) & 0xFF,
60 (data
.length
>> 16) & 0xFF,
61 (data
.length
>> 8) & 0xFF,
62 (data
.length
& 0xFF));
64 for (let i
= 0; i
< data
.length
; i
++) {
65 unCompData
.push(data
.charCodeAt(i
));
68 let strm
= new ZStream();
69 let chunkSize
= 1024 * 10 * 10;
70 strm
.output
= new Uint8Array(chunkSize
);
73 /* eslint-disable camelcase */
74 strm
.input
= unCompData
;
75 strm
.avail_in
= strm
.input
.length
;
78 strm
.avail_out
= chunkSize
;
79 /* eslint-enable camelcase */
83 return new Uint8Array(strm
.output
.buffer
, 0, strm
.next_out
);
86 describe('Remote Frame Buffer Protocol Client', function () {
90 before(FakeWebSocket
.replace
);
91 after(FakeWebSocket
.restore
);
94 this.clock
= clock
= sinon
.useFakeTimers();
95 // sinon doesn't support this yet
96 raf
= window
.requestAnimationFrame
;
97 window
.requestAnimationFrame
= setTimeout
;
98 // Use a single set of buffers instead of reallocating to
100 const sock
= new Websock();
101 const _sQ
= new Uint8Array(sock
._sQbufferSize
);
102 const rQ
= new Uint8Array(sock
._rQbufferSize
);
104 Websock
.prototype._oldAllocateBuffers
= Websock
.prototype._allocateBuffers
;
105 Websock
.prototype._allocateBuffers = function () {
113 Websock
.prototype._allocateBuffers
= Websock
.prototype._oldAllocateBuffers
;
114 this.clock
.restore();
115 window
.requestAnimationFrame
= raf
;
121 beforeEach(function () {
122 // Create a container element for all RFB objects to attach to
123 container
= document
.createElement('div');
124 container
.style
.width
= "100%";
125 container
.style
.height
= "100%";
126 document
.body
.appendChild(container
);
128 // And track all created RFB objects
131 afterEach(function () {
132 // Make sure every created RFB object is properly cleaned up
133 // or they might affect subsequent tests
134 rfbs
.forEach(function (rfb
) {
136 expect(rfb
._disconnect
).to
.have
.been
.called
;
140 document
.body
.removeChild(container
);
144 function makeRFB(url
, options
) {
145 url
= url
|| 'wss://host:8675';
146 const rfb
= new RFB(container
, url
, options
);
148 rfb
._sock
._websocket
._open();
149 rfb
._rfbConnectionState
= 'connected';
150 sinon
.spy(rfb
, "_disconnect");
155 describe('Connecting/Disconnecting', function () {
156 describe('#RFB', function () {
157 it('should set the current state to "connecting"', function () {
158 const client
= new RFB(document
.createElement('div'), 'wss://host:8675');
159 client
._rfbConnectionState
= '';
161 expect(client
._rfbConnectionState
).to
.equal('connecting');
164 it('should actually connect to the websocket', function () {
165 const client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
166 sinon
.spy(client
._sock
, 'open');
168 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
169 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
173 describe('#disconnect', function () {
175 beforeEach(function () {
179 it('should go to state "disconnecting" before "disconnected"', function () {
180 sinon
.spy(client
, '_updateConnectionState');
182 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
183 expect(client
._updateConnectionState
.getCall(0).args
[0])
184 .to
.equal('disconnecting');
185 expect(client
._updateConnectionState
.getCall(1).args
[0])
186 .to
.equal('disconnected');
187 expect(client
._rfbConnectionState
).to
.equal('disconnected');
190 it('should unregister error event handler', function () {
191 sinon
.spy(client
._sock
, 'off');
193 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
196 it('should unregister message event handler', function () {
197 sinon
.spy(client
._sock
, 'off');
199 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
202 it('should unregister open event handler', function () {
203 sinon
.spy(client
._sock
, 'off');
205 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
209 describe('#sendCredentials', function () {
211 beforeEach(function () {
213 client
._rfbConnectionState
= 'connecting';
216 it('should set the rfb credentials properly"', function () {
217 client
.sendCredentials({ password
: 'pass' });
218 expect(client
._rfbCredentials
).to
.deep
.equal({ password
: 'pass' });
221 it('should call initMsg "soon"', function () {
222 client
._initMsg
= sinon
.spy();
223 client
.sendCredentials({ password
: 'pass' });
225 expect(client
._initMsg
).to
.have
.been
.calledOnce
;
230 describe('Public API Basic Behavior', function () {
232 beforeEach(function () {
236 describe('#sendCtrlAlDel', function () {
237 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
238 const expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush
: () => {}};
239 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
240 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
241 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
242 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
243 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
244 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
246 client
.sendCtrlAltDel();
247 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
250 it('should not send the keys if we are not in a normal state', function () {
251 sinon
.spy(client
._sock
, 'flush');
252 client
._rfbConnectionState
= "connecting";
253 client
.sendCtrlAltDel();
254 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
257 it('should not send the keys if we are set as view_only', function () {
258 sinon
.spy(client
._sock
, 'flush');
259 client
._viewOnly
= true;
260 client
.sendCtrlAltDel();
261 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
265 describe('#sendKey', function () {
266 it('should send a single key with the given code and state (down = true)', function () {
267 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
268 RFB
.messages
.keyEvent(expected
, 123, 1);
269 client
.sendKey(123, 'Key123', true);
270 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
273 it('should send both a down and up event if the state is not specified', function () {
274 const expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
275 RFB
.messages
.keyEvent(expected
, 123, 1);
276 RFB
.messages
.keyEvent(expected
, 123, 0);
277 client
.sendKey(123, 'Key123');
278 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
281 it('should not send the key if we are not in a normal state', function () {
282 sinon
.spy(client
._sock
, 'flush');
283 client
._rfbConnectionState
= "connecting";
284 client
.sendKey(123, 'Key123');
285 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
288 it('should not send the key if we are set as view_only', function () {
289 sinon
.spy(client
._sock
, 'flush');
290 client
._viewOnly
= true;
291 client
.sendKey(123, 'Key123');
292 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
295 it('should send QEMU extended events if supported', function () {
296 client
._qemuExtKeyEventSupported
= true;
297 const expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush
: () => {}};
298 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
299 client
.sendKey(0x20, 'Space', true);
300 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
303 it('should not send QEMU extended events if unknown key code', function () {
304 client
._qemuExtKeyEventSupported
= true;
305 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
306 RFB
.messages
.keyEvent(expected
, 123, 1);
307 client
.sendKey(123, 'FooBar', true);
308 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
312 describe('#focus', function () {
313 it('should move focus to canvas object', function () {
314 client
._canvas
.focus
= sinon
.spy();
316 expect(client
._canvas
.focus
).to
.have
.been
.calledOnce
;
320 describe('#blur', function () {
321 it('should remove focus from canvas object', function () {
322 client
._canvas
.blur
= sinon
.spy();
324 expect(client
._canvas
.blur
).to
.have
.been
.calledOnce
;
328 describe('#clipboardPasteFrom', function () {
329 describe('Clipboard update handling', function () {
330 beforeEach(function () {
331 sinon
.spy(RFB
.messages
, 'clientCutText');
332 sinon
.spy(RFB
.messages
, 'extendedClipboardNotify');
335 afterEach(function () {
336 RFB
.messages
.clientCutText
.restore();
337 RFB
.messages
.extendedClipboardNotify
.restore();
340 it('should send the given text in an clipboard update', function () {
341 client
.clipboardPasteFrom('abc');
343 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
344 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(client
._sock
,
345 new Uint8Array([97, 98, 99]));
348 it('should send an notify if extended clipboard is supported by server', function () {
349 // Send our capabilities
350 let data
= [3, 0, 0, 0];
351 const flags
= [0x1F, 0x00, 0x00, 0x01];
352 let fileSizes
= [0x00, 0x00, 0x00, 0x1E];
354 push32(data
, toUnsigned32bit(-8));
355 data
= data
.concat(flags
);
356 data
= data
.concat(fileSizes
);
357 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
359 client
.clipboardPasteFrom('extended test');
360 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
364 it('should flush multiple times for large clipboards', function () {
365 sinon
.spy(client
._sock
, 'flush');
367 for (let i
= 0; i
< client
._sock
._sQbufferSize
+ 100; i
++) {
370 client
.clipboardPasteFrom(longText
);
371 expect(client
._sock
.flush
).to
.have
.been
.calledTwice
;
374 it('should not send the text if we are not in a normal state', function () {
375 sinon
.spy(client
._sock
, 'flush');
376 client
._rfbConnectionState
= "connecting";
377 client
.clipboardPasteFrom('abc');
378 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
382 describe("XVP operations", function () {
383 beforeEach(function () {
384 client
._rfbXvpVer
= 1;
387 it('should send the shutdown signal on #machineShutdown', function () {
388 client
.machineShutdown();
389 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
392 it('should send the reboot signal on #machineReboot', function () {
393 client
.machineReboot();
394 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
397 it('should send the reset signal on #machineReset', function () {
398 client
.machineReset();
399 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
402 it('should not send XVP operations with higher versions than we support', function () {
403 sinon
.spy(client
._sock
, 'flush');
405 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
410 describe('Clipping', function () {
412 beforeEach(function () {
414 container
.style
.width
= '70px';
415 container
.style
.height
= '80px';
416 client
.clipViewport
= true;
419 it('should update display clip state when changing the property', function () {
420 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
422 client
.clipViewport
= false;
423 expect(spy
.set).to
.have
.been
.calledOnce
;
424 expect(spy
.set).to
.have
.been
.calledWith(false);
425 spy
.set.resetHistory();
427 client
.clipViewport
= true;
428 expect(spy
.set).to
.have
.been
.calledOnce
;
429 expect(spy
.set).to
.have
.been
.calledWith(true);
432 it('should update the viewport when the container size changes', function () {
433 sinon
.spy(client
._display
, "viewportChangeSize");
435 container
.style
.width
= '40px';
436 container
.style
.height
= '50px';
437 const event
= new UIEvent('resize');
438 window
.dispatchEvent(event
);
441 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledOnce
;
442 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(40, 50);
445 it('should update the viewport when the remote session resizes', function () {
446 // Simple ExtendedDesktopSize FBU message
447 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
448 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
449 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
450 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
451 0x00, 0x00, 0x00, 0x00 ];
453 sinon
.spy(client
._display
, "viewportChangeSize");
455 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
457 // FIXME: Display implicitly calls viewportChangeSize() when
458 // resizing the framebuffer, hence calledTwice.
459 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledTwice
;
460 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(70, 80);
463 it('should not update the viewport if not clipping', function () {
464 client
.clipViewport
= false;
465 sinon
.spy(client
._display
, "viewportChangeSize");
467 container
.style
.width
= '40px';
468 container
.style
.height
= '50px';
469 const event
= new UIEvent('resize');
470 window
.dispatchEvent(event
);
473 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
476 it('should not update the viewport if scaling', function () {
477 client
.scaleViewport
= true;
478 sinon
.spy(client
._display
, "viewportChangeSize");
480 container
.style
.width
= '40px';
481 container
.style
.height
= '50px';
482 const event
= new UIEvent('resize');
483 window
.dispatchEvent(event
);
486 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
489 describe('Dragging', function () {
490 beforeEach(function () {
491 client
.dragViewport
= true;
492 sinon
.spy(RFB
.messages
, "pointerEvent");
495 afterEach(function () {
496 RFB
.messages
.pointerEvent
.restore();
499 it('should not send button messages when initiating viewport dragging', function () {
500 client
._handleMouseButton(13, 9, 0x001);
501 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
504 it('should send button messages when release without movement', function () {
506 client
._handleMouseButton(13, 9, 0x001);
507 client
._handleMouseButton(13, 9, 0x000);
508 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
510 RFB
.messages
.pointerEvent
.resetHistory();
513 client
._handleMouseButton(13, 9, 0x001);
514 client
._handleMouseMove(15, 14);
515 client
._handleMouseButton(15, 14, 0x000);
516 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
519 it('should not send button messages when in view only', function () {
520 client
._viewOnly
= true;
521 client
._handleMouseButton(13, 9, 0x001);
522 client
._handleMouseButton(13, 9, 0x000);
523 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
526 it('should send button message directly when drag is disabled', function () {
527 client
.dragViewport
= false;
528 client
._handleMouseButton(13, 9, 0x001);
529 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
532 it('should be initiate viewport dragging on sufficient movement', function () {
533 sinon
.spy(client
._display
, "viewportChangePos");
535 // Too small movement
537 client
._handleMouseButton(13, 9, 0x001);
538 client
._handleMouseMove(18, 9);
540 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
541 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
543 // Sufficient movement
545 client
._handleMouseMove(43, 9);
547 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
548 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
549 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(-30, 0);
551 client
._display
.viewportChangePos
.resetHistory();
553 // Now a small movement should move right away
555 client
._handleMouseMove(43, 14);
557 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
558 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
559 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(0, -5);
562 it('should not send button messages when dragging ends', function () {
563 // First the movement
565 client
._handleMouseButton(13, 9, 0x001);
566 client
._handleMouseMove(43, 9);
567 client
._handleMouseButton(43, 9, 0x000);
569 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
572 it('should terminate viewport dragging on a button up event', function () {
573 // First the dragging movement
575 client
._handleMouseButton(13, 9, 0x001);
576 client
._handleMouseMove(43, 9);
577 client
._handleMouseButton(43, 9, 0x000);
579 // Another movement now should not move the viewport
581 sinon
.spy(client
._display
, "viewportChangePos");
583 client
._handleMouseMove(43, 59);
585 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
590 describe('Scaling', function () {
592 beforeEach(function () {
594 container
.style
.width
= '70px';
595 container
.style
.height
= '80px';
596 client
.scaleViewport
= true;
599 it('should update display scale factor when changing the property', function () {
600 const spy
= sinon
.spy(client
._display
, "scale", ["set"]);
601 sinon
.spy(client
._display
, "autoscale");
603 client
.scaleViewport
= false;
604 expect(spy
.set).to
.have
.been
.calledOnce
;
605 expect(spy
.set).to
.have
.been
.calledWith(1.0);
606 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
608 client
.scaleViewport
= true;
609 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
610 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
613 it('should update the clipping setting when changing the property', function () {
614 client
.clipViewport
= true;
616 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
618 client
.scaleViewport
= false;
619 expect(spy
.set).to
.have
.been
.calledOnce
;
620 expect(spy
.set).to
.have
.been
.calledWith(true);
622 spy
.set.resetHistory();
624 client
.scaleViewport
= true;
625 expect(spy
.set).to
.have
.been
.calledOnce
;
626 expect(spy
.set).to
.have
.been
.calledWith(false);
629 it('should update the scaling when the container size changes', function () {
630 sinon
.spy(client
._display
, "autoscale");
632 container
.style
.width
= '40px';
633 container
.style
.height
= '50px';
634 const event
= new UIEvent('resize');
635 window
.dispatchEvent(event
);
638 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
639 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(40, 50);
642 it('should update the scaling when the remote session resizes', function () {
643 // Simple ExtendedDesktopSize FBU message
644 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
645 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
646 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
647 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
648 0x00, 0x00, 0x00, 0x00 ];
650 sinon
.spy(client
._display
, "autoscale");
652 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
654 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
655 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
658 it('should not update the display scale factor if not scaling', function () {
659 client
.scaleViewport
= false;
661 sinon
.spy(client
._display
, "autoscale");
663 container
.style
.width
= '40px';
664 container
.style
.height
= '50px';
665 const event
= new UIEvent('resize');
666 window
.dispatchEvent(event
);
669 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
673 describe('Remote resize', function () {
675 beforeEach(function () {
677 client
._supportsSetDesktopSize
= true;
678 client
.resizeSession
= true;
679 container
.style
.width
= '70px';
680 container
.style
.height
= '80px';
681 sinon
.spy(RFB
.messages
, "setDesktopSize");
684 afterEach(function () {
685 RFB
.messages
.setDesktopSize
.restore();
688 it('should only request a resize when turned on', function () {
689 client
.resizeSession
= false;
690 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
691 client
.resizeSession
= true;
692 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
695 it('should request a resize when initially connecting', function () {
696 // Simple ExtendedDesktopSize FBU message
697 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
698 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
699 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
700 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
701 0x00, 0x00, 0x00, 0x00 ];
703 // First message should trigger a resize
705 client
._supportsSetDesktopSize
= false;
707 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
709 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
710 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 70, 80, 0, 0);
712 RFB
.messages
.setDesktopSize
.resetHistory();
714 // Second message should not trigger a resize
716 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
718 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
721 it('should request a resize when the container resizes', function () {
722 container
.style
.width
= '40px';
723 container
.style
.height
= '50px';
724 const event
= new UIEvent('resize');
725 window
.dispatchEvent(event
);
728 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
729 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
732 it('should not resize until the container size is stable', function () {
733 container
.style
.width
= '20px';
734 container
.style
.height
= '30px';
735 const event1
= new UIEvent('resize');
736 window
.dispatchEvent(event1
);
739 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
741 container
.style
.width
= '40px';
742 container
.style
.height
= '50px';
743 const event2
= new UIEvent('resize');
744 window
.dispatchEvent(event2
);
747 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
751 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
752 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
755 it('should not resize when resize is disabled', function () {
756 client
._resizeSession
= false;
758 container
.style
.width
= '40px';
759 container
.style
.height
= '50px';
760 const event
= new UIEvent('resize');
761 window
.dispatchEvent(event
);
764 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
767 it('should not resize when resize is not supported', function () {
768 client
._supportsSetDesktopSize
= false;
770 container
.style
.width
= '40px';
771 container
.style
.height
= '50px';
772 const event
= new UIEvent('resize');
773 window
.dispatchEvent(event
);
776 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
779 it('should not resize when in view only mode', function () {
780 client
._viewOnly
= true;
782 container
.style
.width
= '40px';
783 container
.style
.height
= '50px';
784 const event
= new UIEvent('resize');
785 window
.dispatchEvent(event
);
788 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
791 it('should not try to override a server resize', function () {
792 // Simple ExtendedDesktopSize FBU message
793 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
794 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
795 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
796 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
797 0x00, 0x00, 0x00, 0x00 ];
799 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
801 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
805 describe('Misc Internals', function () {
806 describe('#_updateConnectionState', function () {
808 beforeEach(function () {
812 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
813 const spy
= sinon
.spy();
814 client
._disconnTimer
= setTimeout(spy
, 50);
815 client
._rfbConnectionState
= 'connecting';
816 client
._updateConnectionState('connected');
818 expect(spy
).to
.not
.have
.been
.called
;
819 expect(client
._disconnTimer
).to
.be
.null;
822 it('should set the rfbConnectionState', function () {
823 client
._rfbConnectionState
= 'connecting';
824 client
._updateConnectionState('connected');
825 expect(client
._rfbConnectionState
).to
.equal('connected');
828 it('should not change the state when we are disconnected', function () {
830 expect(client
._rfbConnectionState
).to
.equal('disconnected');
831 client
._updateConnectionState('connecting');
832 expect(client
._rfbConnectionState
).to
.not
.equal('connecting');
835 it('should ignore state changes to the same state', function () {
836 const connectSpy
= sinon
.spy();
837 client
.addEventListener("connect", connectSpy
);
839 expect(client
._rfbConnectionState
).to
.equal('connected');
840 client
._updateConnectionState('connected');
841 expect(connectSpy
).to
.not
.have
.been
.called
;
845 const disconnectSpy
= sinon
.spy();
846 client
.addEventListener("disconnect", disconnectSpy
);
848 expect(client
._rfbConnectionState
).to
.equal('disconnected');
849 client
._updateConnectionState('disconnected');
850 expect(disconnectSpy
).to
.not
.have
.been
.called
;
853 it('should ignore illegal state changes', function () {
854 const spy
= sinon
.spy();
855 client
.addEventListener("disconnect", spy
);
856 client
._updateConnectionState('disconnected');
857 expect(client
._rfbConnectionState
).to
.not
.equal('disconnected');
858 expect(spy
).to
.not
.have
.been
.called
;
862 describe('#_fail', function () {
864 beforeEach(function () {
868 it('should close the WebSocket connection', function () {
869 sinon
.spy(client
._sock
, 'close');
871 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
874 it('should transition to disconnected', function () {
875 sinon
.spy(client
, '_updateConnectionState');
877 this.clock
.tick(2000);
878 expect(client
._updateConnectionState
).to
.have
.been
.called
;
879 expect(client
._rfbConnectionState
).to
.equal('disconnected');
882 it('should set clean_disconnect variable', function () {
883 client
._rfbCleanDisconnect
= true;
884 client
._rfbConnectionState
= 'connected';
886 expect(client
._rfbCleanDisconnect
).to
.be
.false;
889 it('should result in disconnect event with clean set to false', function () {
890 client
._rfbConnectionState
= 'connected';
891 const spy
= sinon
.spy();
892 client
.addEventListener("disconnect", spy
);
894 this.clock
.tick(2000);
895 expect(spy
).to
.have
.been
.calledOnce
;
896 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
902 describe('Connection States', function () {
903 describe('connecting', function () {
904 it('should open the websocket connection', function () {
905 const client
= new RFB(document
.createElement('div'),
906 'ws://HOST:8675/PATH');
907 sinon
.spy(client
._sock
, 'open');
909 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
913 describe('connected', function () {
915 beforeEach(function () {
919 it('should result in a connect event if state becomes connected', function () {
920 const spy
= sinon
.spy();
921 client
.addEventListener("connect", spy
);
922 client
._rfbConnectionState
= 'connecting';
923 client
._updateConnectionState('connected');
924 expect(spy
).to
.have
.been
.calledOnce
;
927 it('should not result in a connect event if the state is not "connected"', function () {
928 const spy
= sinon
.spy();
929 client
.addEventListener("connect", spy
);
930 client
._sock
._websocket
.open
= () => {}; // explicitly don't call onopen
931 client
._updateConnectionState('connecting');
932 expect(spy
).to
.not
.have
.been
.called
;
936 describe('disconnecting', function () {
938 beforeEach(function () {
942 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
943 sinon
.spy(client
, '_updateConnectionState');
944 client
._sock
._websocket
.close
= () => {}; // explicitly don't call onclose
945 client
._updateConnectionState('disconnecting');
946 this.clock
.tick(3 * 1000);
947 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
948 expect(client
._rfbDisconnectReason
).to
.not
.equal("");
949 expect(client
._rfbConnectionState
).to
.equal("disconnected");
952 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
953 client
._updateConnectionState('disconnecting');
954 this.clock
.tick(3 * 1000 / 2);
955 client
._sock
._websocket
.close();
956 this.clock
.tick(3 * 1000 / 2 + 1);
957 expect(client
._rfbConnectionState
).to
.equal('disconnected');
960 it('should close the WebSocket connection', function () {
961 sinon
.spy(client
._sock
, 'close');
962 client
._updateConnectionState('disconnecting');
963 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
966 it('should not result in a disconnect event', function () {
967 const spy
= sinon
.spy();
968 client
.addEventListener("disconnect", spy
);
969 client
._sock
._websocket
.close
= () => {}; // explicitly don't call onclose
970 client
._updateConnectionState('disconnecting');
971 expect(spy
).to
.not
.have
.been
.called
;
975 describe('disconnected', function () {
977 beforeEach(function () {
978 client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
981 it('should result in a disconnect event if state becomes "disconnected"', function () {
982 const spy
= sinon
.spy();
983 client
.addEventListener("disconnect", spy
);
984 client
._rfbConnectionState
= 'disconnecting';
985 client
._updateConnectionState('disconnected');
986 expect(spy
).to
.have
.been
.calledOnce
;
987 expect(spy
.args
[0][0].detail
.clean
).to
.be
.true;
990 it('should result in a disconnect event without msg when no reason given', function () {
991 const spy
= sinon
.spy();
992 client
.addEventListener("disconnect", spy
);
993 client
._rfbConnectionState
= 'disconnecting';
994 client
._rfbDisconnectReason
= "";
995 client
._updateConnectionState('disconnected');
996 expect(spy
).to
.have
.been
.calledOnce
;
997 expect(spy
.args
[0].length
).to
.equal(1);
1002 describe('Protocol Initialization States', function () {
1004 beforeEach(function () {
1006 client
._rfbConnectionState
= 'connecting';
1009 describe('ProtocolVersion', function () {
1010 function sendVer(ver
, client
) {
1011 const arr
= new Uint8Array(12);
1012 for (let i
= 0; i
< ver
.length
; i
++) {
1013 arr
[i
+4] = ver
.charCodeAt(i
);
1015 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
1017 client
._sock
._websocket
._receiveData(arr
);
1020 describe('version parsing', function () {
1021 it('should interpret version 003.003 as version 3.3', function () {
1022 sendVer('003.003', client
);
1023 expect(client
._rfbVersion
).to
.equal(3.3);
1026 it('should interpret version 003.006 as version 3.3', function () {
1027 sendVer('003.006', client
);
1028 expect(client
._rfbVersion
).to
.equal(3.3);
1031 it('should interpret version 003.889 as version 3.3', function () {
1032 sendVer('003.889', client
);
1033 expect(client
._rfbVersion
).to
.equal(3.3);
1036 it('should interpret version 003.007 as version 3.7', function () {
1037 sendVer('003.007', client
);
1038 expect(client
._rfbVersion
).to
.equal(3.7);
1041 it('should interpret version 003.008 as version 3.8', function () {
1042 sendVer('003.008', client
);
1043 expect(client
._rfbVersion
).to
.equal(3.8);
1046 it('should interpret version 004.000 as version 3.8', function () {
1047 sendVer('004.000', client
);
1048 expect(client
._rfbVersion
).to
.equal(3.8);
1051 it('should interpret version 004.001 as version 3.8', function () {
1052 sendVer('004.001', client
);
1053 expect(client
._rfbVersion
).to
.equal(3.8);
1056 it('should interpret version 005.000 as version 3.8', function () {
1057 sendVer('005.000', client
);
1058 expect(client
._rfbVersion
).to
.equal(3.8);
1061 it('should fail on an invalid version', function () {
1062 sinon
.spy(client
, "_fail");
1063 sendVer('002.000', client
);
1064 expect(client
._fail
).to
.have
.been
.calledOnce
;
1068 it('should send back the interpreted version', function () {
1069 sendVer('004.000', client
);
1071 const expectedStr
= 'RFB 003.008\n';
1072 const expected
= [];
1073 for (let i
= 0; i
< expectedStr
.length
; i
++) {
1074 expected
[i
] = expectedStr
.charCodeAt(i
);
1077 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1080 it('should transition to the Security state on successful negotiation', function () {
1081 sendVer('003.008', client
);
1082 expect(client
._rfbInitState
).to
.equal('Security');
1085 describe('Repeater', function () {
1086 beforeEach(function () {
1087 client
= makeRFB('wss://host:8675', { repeaterID
: "12345" });
1088 client
._rfbConnectionState
= 'connecting';
1091 it('should interpret version 000.000 as a repeater', function () {
1092 sendVer('000.000', client
);
1093 expect(client
._rfbVersion
).to
.equal(0);
1095 const sentData
= client
._sock
._websocket
._getSentData();
1096 expect(new Uint8Array(sentData
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
1097 expect(sentData
).to
.have
.length(250);
1100 it('should handle two step repeater negotiation', function () {
1101 sendVer('000.000', client
);
1102 sendVer('003.008', client
);
1103 expect(client
._rfbVersion
).to
.equal(3.8);
1108 describe('Security', function () {
1109 beforeEach(function () {
1110 client
._rfbInitState
= 'Security';
1113 it('should simply receive the auth scheme when for versions < 3.7', function () {
1114 client
._rfbVersion
= 3.6;
1115 const authSchemeRaw
= [1, 2, 3, 4];
1116 const authScheme
= (authSchemeRaw
[0] << 24) + (authSchemeRaw
[1] << 16) +
1117 (authSchemeRaw
[2] << 8) + authSchemeRaw
[3];
1118 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemeRaw
));
1119 expect(client
._rfbAuthScheme
).to
.equal(authScheme
);
1122 it('should prefer no authentication is possible', function () {
1123 client
._rfbVersion
= 3.7;
1124 const authSchemes
= [2, 1, 3];
1125 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1126 expect(client
._rfbAuthScheme
).to
.equal(1);
1127 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
1130 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
1131 client
._rfbVersion
= 3.7;
1132 const authSchemes
= [2, 22, 16];
1133 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1134 expect(client
._rfbAuthScheme
).to
.equal(22);
1135 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
1138 it('should fail if there are no supported schemes for versions >= 3.7', function () {
1139 sinon
.spy(client
, "_fail");
1140 client
._rfbVersion
= 3.7;
1141 const authSchemes
= [1, 32];
1142 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1143 expect(client
._fail
).to
.have
.been
.calledOnce
;
1146 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
1147 client
._rfbVersion
= 3.7;
1148 const failureData
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1149 sinon
.spy(client
, '_fail');
1150 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1152 expect(client
._fail
).to
.have
.been
.calledOnce
;
1153 expect(client
._fail
).to
.have
.been
.calledWith(
1154 'Security negotiation failed on no security types (reason: whoops)');
1157 it('should transition to the Authentication state and continue on successful negotiation', function () {
1158 client
._rfbVersion
= 3.7;
1159 const authSchemes
= [1, 1];
1160 client
._negotiateAuthentication
= sinon
.spy();
1161 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1162 expect(client
._rfbInitState
).to
.equal('Authentication');
1163 expect(client
._negotiateAuthentication
).to
.have
.been
.calledOnce
;
1167 describe('Authentication', function () {
1168 beforeEach(function () {
1169 client
._rfbInitState
= 'Security';
1172 function sendSecurity(type
, cl
) {
1173 cl
._sock
._websocket
._receiveData(new Uint8Array([1, type
]));
1176 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
1177 client
._rfbVersion
= 3.6;
1178 const errMsg
= "Whoopsies";
1179 const data
= [0, 0, 0, 0];
1180 const errLen
= errMsg
.length
;
1181 push32(data
, errLen
);
1182 for (let i
= 0; i
< errLen
; i
++) {
1183 data
.push(errMsg
.charCodeAt(i
));
1186 sinon
.spy(client
, '_fail');
1187 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1188 expect(client
._fail
).to
.have
.been
.calledWith(
1189 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
1192 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
1193 client
._rfbVersion
= 3.8;
1194 sendSecurity(1, client
);
1195 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1198 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
1199 client
._rfbVersion
= 3.7;
1200 sendSecurity(1, client
);
1201 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1204 it('should fail on an unknown auth scheme', function () {
1205 sinon
.spy(client
, "_fail");
1206 client
._rfbVersion
= 3.8;
1207 sendSecurity(57, client
);
1208 expect(client
._fail
).to
.have
.been
.calledOnce
;
1211 describe('VNC Authentication (type 2) Handler', function () {
1212 beforeEach(function () {
1213 client
._rfbInitState
= 'Security';
1214 client
._rfbVersion
= 3.8;
1217 it('should fire the credentialsrequired event if missing a password', function () {
1218 const spy
= sinon
.spy();
1219 client
.addEventListener("credentialsrequired", spy
);
1220 sendSecurity(2, client
);
1222 const challenge
= [];
1223 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1224 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1226 expect(client
._rfbCredentials
).to
.be
.empty
;
1227 expect(spy
).to
.have
.been
.calledOnce
;
1228 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
1231 it('should encrypt the password with DES and then send it back', function () {
1232 client
._rfbCredentials
= { password
: 'passwd' };
1233 sendSecurity(2, client
);
1234 client
._sock
._websocket
._getSentData(); // skip the choice of auth reply
1236 const challenge
= [];
1237 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1238 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1240 const desPass
= RFB
.genDES('passwd', challenge
);
1241 expect(client
._sock
).to
.have
.sent(new Uint8Array(desPass
));
1244 it('should transition to SecurityResult immediately after sending the password', function () {
1245 client
._rfbCredentials
= { password
: 'passwd' };
1246 sendSecurity(2, client
);
1248 const challenge
= [];
1249 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1250 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1252 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1256 describe('XVP Authentication (type 22) Handler', function () {
1257 beforeEach(function () {
1258 client
._rfbInitState
= 'Security';
1259 client
._rfbVersion
= 3.8;
1262 it('should fall through to standard VNC authentication upon completion', function () {
1263 client
._rfbCredentials
= { username
: 'user',
1265 password
: 'password' };
1266 client
._negotiateStdVNCAuth
= sinon
.spy();
1267 sendSecurity(22, client
);
1268 expect(client
._negotiateStdVNCAuth
).to
.have
.been
.calledOnce
;
1271 it('should fire the credentialsrequired event if all credentials are missing', function () {
1272 const spy
= sinon
.spy();
1273 client
.addEventListener("credentialsrequired", spy
);
1274 client
._rfbCredentials
= {};
1275 sendSecurity(22, client
);
1277 expect(client
._rfbCredentials
).to
.be
.empty
;
1278 expect(spy
).to
.have
.been
.calledOnce
;
1279 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1282 it('should fire the credentialsrequired event if some credentials are missing', function () {
1283 const spy
= sinon
.spy();
1284 client
.addEventListener("credentialsrequired", spy
);
1285 client
._rfbCredentials
= { username
: 'user',
1287 sendSecurity(22, client
);
1289 expect(spy
).to
.have
.been
.calledOnce
;
1290 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1293 it('should send user and target separately', function () {
1294 client
._rfbCredentials
= { username
: 'user',
1296 password
: 'password' };
1297 client
._negotiateStdVNCAuth
= sinon
.spy();
1299 sendSecurity(22, client
);
1301 const expected
= [22, 4, 6]; // auth selection, len user, len target
1302 for (let i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
1304 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1308 describe('TightVNC Authentication (type 16) Handler', function () {
1309 beforeEach(function () {
1310 client
._rfbInitState
= 'Security';
1311 client
._rfbVersion
= 3.8;
1312 sendSecurity(16, client
);
1313 client
._sock
._websocket
._getSentData(); // skip the security reply
1316 function sendNumStrPairs(pairs
, client
) {
1318 push32(data
, pairs
.length
);
1320 for (let i
= 0; i
< pairs
.length
; i
++) {
1321 push32(data
, pairs
[i
][0]);
1322 for (let j
= 0; j
< 4; j
++) {
1323 data
.push(pairs
[i
][1].charCodeAt(j
));
1325 for (let j
= 0; j
< 8; j
++) {
1326 data
.push(pairs
[i
][2].charCodeAt(j
));
1330 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1333 it('should skip tunnel negotiation if no tunnels are requested', function () {
1334 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1335 expect(client
._rfbTightVNC
).to
.be
.true;
1338 it('should fail if no supported tunnels are listed', function () {
1339 sinon
.spy(client
, "_fail");
1340 sendNumStrPairs([[123, 'OTHR', 'SOMETHNG']], client
);
1341 expect(client
._fail
).to
.have
.been
.calledOnce
;
1344 it('should choose the notunnel tunnel type', function () {
1345 sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
1346 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1349 it('should choose the notunnel tunnel type for Siemens devices', function () {
1350 sendNumStrPairs([[1, 'SICR', 'SCHANNEL'], [2, 'SICR', 'SCHANLPW']], client
);
1351 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1354 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
1355 sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client
);
1356 client
._sock
._websocket
._getSentData(); // skip the tunnel choice here
1357 sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client
);
1358 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1359 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1362 /*it('should attempt to use VNC auth over no auth when possible', function () {
1363 client._rfbTightVNC = true;
1364 client._negotiateStdVNCAuth = sinon.spy();
1365 sendNumStrPairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
1366 expect(client._sock).to.have.sent([0, 0, 0, 1]);
1367 expect(client._negotiateStdVNCAuth).to.have.been.calledOnce;
1368 expect(client._rfbAuthScheme).to.equal(2);
1369 });*/ // while this would make sense, the original code doesn't actually do this
1371 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
1372 client
._rfbTightVNC
= true;
1373 sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client
);
1374 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1375 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1378 it('should accept VNC authentication and transition to that', function () {
1379 client
._rfbTightVNC
= true;
1380 client
._negotiateStdVNCAuth
= sinon
.spy();
1381 sendNumStrPairs([[2, 'STDV', 'VNCAUTH__']], client
);
1382 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1383 expect(client
._negotiateStdVNCAuth
).to
.have
.been
.calledOnce
;
1384 expect(client
._rfbAuthScheme
).to
.equal(2);
1387 it('should fail if there are no supported auth types', function () {
1388 sinon
.spy(client
, "_fail");
1389 client
._rfbTightVNC
= true;
1390 sendNumStrPairs([[23, 'stdv', 'badval__']], client
);
1391 expect(client
._fail
).to
.have
.been
.calledOnce
;
1396 describe('SecurityResult', function () {
1397 beforeEach(function () {
1398 client
._rfbInitState
= 'SecurityResult';
1401 it('should fall through to ServerInitialisation on a response code of 0', function () {
1402 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1403 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1406 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1407 client
._rfbVersion
= 3.8;
1408 sinon
.spy(client
, '_fail');
1409 const failureData
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1410 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1411 expect(client
._fail
).to
.have
.been
.calledWith(
1412 'Security negotiation failed on security result (reason: whoops)');
1415 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1416 sinon
.spy(client
, '_fail');
1417 client
._rfbVersion
= 3.7;
1418 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 1]));
1419 expect(client
._fail
).to
.have
.been
.calledWith(
1420 'Security handshake failed');
1423 it('should result in securityfailure event when receiving a non zero status', function () {
1424 const spy
= sinon
.spy();
1425 client
.addEventListener("securityfailure", spy
);
1426 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 2]));
1427 expect(spy
).to
.have
.been
.calledOnce
;
1428 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1431 it('should include reason when provided in securityfailure event', function () {
1432 client
._rfbVersion
= 3.8;
1433 const spy
= sinon
.spy();
1434 client
.addEventListener("securityfailure", spy
);
1435 const failureData
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
1436 32, 102, 97, 105, 108, 117, 114, 101];
1437 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1438 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1439 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
1442 it('should not include reason when length is zero in securityfailure event', function () {
1443 client
._rfbVersion
= 3.9;
1444 const spy
= sinon
.spy();
1445 client
.addEventListener("securityfailure", spy
);
1446 const failureData
= [0, 0, 0, 1, 0, 0, 0, 0];
1447 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1448 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1449 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1452 it('should not include reason in securityfailure event for version < 3.8', function () {
1453 client
._rfbVersion
= 3.6;
1454 const spy
= sinon
.spy();
1455 client
.addEventListener("securityfailure", spy
);
1456 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 2]));
1457 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1458 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1462 describe('ClientInitialisation', function () {
1463 it('should transition to the ServerInitialisation state', function () {
1464 const client
= makeRFB();
1465 client
._rfbConnectionState
= 'connecting';
1466 client
._rfbInitState
= 'SecurityResult';
1467 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1468 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1471 it('should send 1 if we are in shared mode', function () {
1472 const client
= makeRFB('wss://host:8675', { shared
: true });
1473 client
._rfbConnectionState
= 'connecting';
1474 client
._rfbInitState
= 'SecurityResult';
1475 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1476 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1479 it('should send 0 if we are not in shared mode', function () {
1480 const client
= makeRFB('wss://host:8675', { shared
: false });
1481 client
._rfbConnectionState
= 'connecting';
1482 client
._rfbInitState
= 'SecurityResult';
1483 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1484 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1488 describe('ServerInitialisation', function () {
1489 beforeEach(function () {
1490 client
._rfbInitState
= 'ServerInitialisation';
1493 function sendServerInit(opts
, client
) {
1494 const fullOpts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, bigEndian
: 0,
1495 trueColor
: 1, redMax
: 255, greenMax
: 255, blueMax
: 255,
1496 redShift
: 16, greenShift
: 8, blueShift
: 0, name
: 'a name' };
1497 for (let opt
in opts
) {
1498 fullOpts
[opt
] = opts
[opt
];
1502 push16(data
, fullOpts
.width
);
1503 push16(data
, fullOpts
.height
);
1505 data
.push(fullOpts
.bpp
);
1506 data
.push(fullOpts
.depth
);
1507 data
.push(fullOpts
.bigEndian
);
1508 data
.push(fullOpts
.trueColor
);
1510 push16(data
, fullOpts
.redMax
);
1511 push16(data
, fullOpts
.greenMax
);
1512 push16(data
, fullOpts
.blueMax
);
1513 push8(data
, fullOpts
.redShift
);
1514 push8(data
, fullOpts
.greenShift
);
1515 push8(data
, fullOpts
.blueShift
);
1522 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1524 const nameData
= [];
1526 pushString(nameData
, fullOpts
.name
);
1527 push32(nameLen
, nameData
.length
);
1529 client
._sock
._websocket
._receiveData(new Uint8Array(nameLen
));
1530 client
._sock
._websocket
._receiveData(new Uint8Array(nameData
));
1533 it('should set the framebuffer width and height', function () {
1534 sendServerInit({ width
: 32, height
: 84 }, client
);
1535 expect(client
._fbWidth
).to
.equal(32);
1536 expect(client
._fbHeight
).to
.equal(84);
1539 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1541 it('should set the framebuffer name and call the callback', function () {
1542 const spy
= sinon
.spy();
1543 client
.addEventListener("desktopname", spy
);
1544 sendServerInit({ name
: 'som€ nam€' }, client
);
1546 expect(client
._fbName
).to
.equal('som€ nam€');
1547 expect(spy
).to
.have
.been
.calledOnce
;
1548 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
1551 it('should handle the extended init message of the tight encoding', function () {
1552 // NB(sross): we don't actually do anything with it, so just test that we can
1553 // read it w/o throwing an error
1554 client
._rfbTightVNC
= true;
1555 sendServerInit({}, client
);
1557 const tightData
= [];
1558 push16(tightData
, 1);
1559 push16(tightData
, 2);
1560 push16(tightData
, 3);
1561 push16(tightData
, 0);
1562 for (let i
= 0; i
< 16 + 32 + 48; i
++) {
1565 client
._sock
._websocket
._receiveData(new Uint8Array(tightData
));
1567 expect(client
._rfbConnectionState
).to
.equal('connected');
1570 it('should resize the display', function () {
1571 sinon
.spy(client
._display
, 'resize');
1572 sendServerInit({ width
: 27, height
: 32 }, client
);
1574 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1575 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1578 it('should grab the mouse and keyboard', function () {
1579 sinon
.spy(client
._keyboard
, 'grab');
1580 sinon
.spy(client
._mouse
, 'grab');
1581 sendServerInit({}, client
);
1582 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1583 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1586 describe('Initial Update Request', function () {
1587 beforeEach(function () {
1588 sinon
.spy(RFB
.messages
, "pixelFormat");
1589 sinon
.spy(RFB
.messages
, "clientEncodings");
1590 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1593 afterEach(function () {
1594 RFB
.messages
.pixelFormat
.restore();
1595 RFB
.messages
.clientEncodings
.restore();
1596 RFB
.messages
.fbUpdateRequest
.restore();
1599 // TODO(directxman12): test the various options in this configuration matrix
1600 it('should reply with the pixel format, client encodings, and initial update request', function () {
1601 sendServerInit({ width
: 27, height
: 32 }, client
);
1603 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1604 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1605 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1606 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1607 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1608 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1609 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1610 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1613 it('should reply with restricted settings for Intel AMT servers', function () {
1614 sendServerInit({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1616 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1617 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1618 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1619 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1620 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1621 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1622 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1623 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1624 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1628 it('should transition to the "connected" state', function () {
1629 sendServerInit({}, client
);
1630 expect(client
._rfbConnectionState
).to
.equal('connected');
1635 describe('Protocol Message Processing After Completing Initialization', function () {
1638 beforeEach(function () {
1640 client
._fbName
= 'some device';
1641 client
._fbWidth
= 640;
1642 client
._fbHeight
= 20;
1645 describe('Framebuffer Update Handling', function () {
1646 const targetDataArr
= [
1647 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1648 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1649 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1650 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1654 const targetDataCheckArr
= [
1655 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1656 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1657 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1658 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1660 let targetDataCheck
;
1662 before(function () {
1663 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1664 targetData
= new Uint8Array(targetDataArr
);
1665 targetDataCheck
= new Uint8Array(targetDataCheckArr
);
1668 function sendFbuMsg(rectInfo
, rectData
, client
, rectCnt
) {
1671 if (!rectCnt
|| rectCnt
> -1) {
1673 data
.push(0); // msg type
1674 data
.push(0); // padding
1675 push16(data
, rectCnt
|| rectData
.length
);
1678 for (let i
= 0; i
< rectData
.length
; i
++) {
1680 push16(data
, rectInfo
[i
].x
);
1681 push16(data
, rectInfo
[i
].y
);
1682 push16(data
, rectInfo
[i
].width
);
1683 push16(data
, rectInfo
[i
].height
);
1684 push32(data
, rectInfo
[i
].encoding
);
1686 data
= data
.concat(rectData
[i
]);
1689 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1692 it('should send an update request if there is sufficient data', function () {
1693 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1694 RFB
.messages
.fbUpdateRequest(expectedMsg
, true, 0, 0, 640, 20);
1696 client
._framebufferUpdate
= () => true;
1697 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1699 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
1702 it('should not send an update request if we need more data', function () {
1703 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1704 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1707 it('should resume receiving an update if we previously did not have enough data', function () {
1708 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1709 RFB
.messages
.fbUpdateRequest(expectedMsg
, true, 0, 0, 640, 20);
1711 // just enough to set FBU.rects
1712 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 3]));
1713 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1715 client
._framebufferUpdate = function () { this._sock
.rQskipBytes(1); return true; }; // we magically have enough data
1716 // 247 should *not* be used as the message type here
1717 client
._sock
._websocket
._receiveData(new Uint8Array([247]));
1718 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
1721 it('should not send a request in continuous updates mode', function () {
1722 client
._enabledContinuousUpdates
= true;
1723 client
._framebufferUpdate
= () => true;
1724 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1726 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1729 it('should fail on an unsupported encoding', function () {
1730 sinon
.spy(client
, "_fail");
1731 const rectInfo
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1732 sendFbuMsg([rectInfo
], [[]], client
);
1733 expect(client
._fail
).to
.have
.been
.calledOnce
;
1736 it('should be able to pause and resume receiving rects if not enought data', function () {
1737 // seed some initial data to copy
1738 client
._fbWidth
= 4;
1739 client
._fbHeight
= 4;
1740 client
._display
.resize(4, 4);
1741 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(targetDataCheckArr
.slice(0, 32)), 0);
1743 const info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1744 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1745 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1746 const rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1747 sendFbuMsg([info
[0]], [rects
[0]], client
, 2);
1748 sendFbuMsg([info
[1]], [rects
[1]], client
, -1);
1749 expect(client
._display
).to
.have
.displayed(targetDataCheck
);
1752 describe('Message Encoding Handlers', function () {
1753 beforeEach(function () {
1754 // a really small frame
1755 client
._fbWidth
= 4;
1756 client
._fbHeight
= 4;
1757 client
._fbDepth
= 24;
1758 client
._display
.resize(4, 4);
1761 it('should handle the RAW encoding', function () {
1762 const info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1763 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1764 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1765 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1768 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1769 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1770 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1771 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1772 sendFbuMsg(info
, rects
, client
);
1773 expect(client
._display
).to
.have
.displayed(targetData
);
1776 it('should handle the RAW encoding in low colour mode', function () {
1777 const info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1778 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1779 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1780 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1782 [0x03, 0x03, 0x03, 0x03],
1783 [0x0c, 0x0c, 0x0c, 0x0c],
1784 [0x0c, 0x0c, 0x03, 0x03],
1785 [0x0c, 0x0c, 0x03, 0x03]];
1786 client
._fbDepth
= 8;
1787 sendFbuMsg(info
, rects
, client
);
1788 expect(client
._display
).to
.have
.displayed(targetDataCheck
);
1791 it('should handle the COPYRECT encoding', function () {
1792 // seed some initial data to copy
1793 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(targetDataCheckArr
.slice(0, 32)), 0);
1795 const info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1796 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1797 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1798 const rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1799 sendFbuMsg(info
, rects
, client
);
1800 expect(client
._display
).to
.have
.displayed(targetDataCheck
);
1803 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1804 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1806 it('should handle the RRE encoding', function () {
1807 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1809 push32(rect
, 2); // 2 subrects
1810 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1811 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1815 push16(rect
, 0); // x: 0
1816 push16(rect
, 0); // y: 0
1817 push16(rect
, 2); // width: 2
1818 push16(rect
, 2); // height: 2
1819 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1823 push16(rect
, 2); // x: 2
1824 push16(rect
, 2); // y: 2
1825 push16(rect
, 2); // width: 2
1826 push16(rect
, 2); // height: 2
1827 sendFbuMsg(info
, [rect
], client
);
1828 expect(client
._display
).to
.have
.displayed(targetDataCheck
);
1831 describe('the HEXTILE encoding handler', function () {
1832 it('should handle a tile with fg, bg specified, normal subrects', function () {
1833 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1835 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1836 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1837 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1841 rect
.push(2); // 2 subrects
1842 rect
.push(0); // x: 0, y: 0
1843 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1844 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1845 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1846 sendFbuMsg(info
, [rect
], client
);
1847 expect(client
._display
).to
.have
.displayed(targetDataCheck
);
1850 it('should handle a raw tile', function () {
1851 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1853 rect
.push(0x01); // raw
1854 for (let i
= 0; i
< targetData
.length
; i
+= 4) {
1855 rect
.push(targetData
[i
+ 2]);
1856 rect
.push(targetData
[i
+ 1]);
1857 rect
.push(targetData
[i
]);
1858 rect
.push(targetData
[i
+ 3]);
1860 sendFbuMsg(info
, [rect
], client
);
1861 expect(client
._display
).to
.have
.displayed(targetData
);
1864 it('should handle a tile with only bg specified (solid bg)', function () {
1865 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1868 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1869 sendFbuMsg(info
, [rect
], client
);
1871 const expected
= [];
1872 for (let i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1873 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1876 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1877 // set the width so we can have two tiles
1878 client
._fbWidth
= 8;
1879 client
._display
.resize(8, 4);
1881 const info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1887 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1889 // send an empty frame
1892 sendFbuMsg(info
, [rect
], client
);
1894 const expected
= [];
1895 for (let i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1896 for (let i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1897 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1900 it('should handle a tile with bg and coloured subrects', function () {
1901 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1903 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1904 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1905 rect
.push(2); // 2 subrects
1906 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1910 rect
.push(0); // x: 0, y: 0
1911 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1912 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1916 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1917 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1918 sendFbuMsg(info
, [rect
], client
);
1919 expect(client
._display
).to
.have
.displayed(targetDataCheck
);
1922 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1923 client
._fbWidth
= 4;
1924 client
._fbHeight
= 17;
1925 client
._display
.resize(4, 17);
1927 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1929 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1930 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1931 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1935 rect
.push(8); // 8 subrects
1936 for (let i
= 0; i
< 4; i
++) {
1937 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1938 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1939 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1940 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1942 rect
.push(0x08); // anysubrects
1943 rect
.push(1); // 1 subrect
1944 rect
.push(0); // x: 0, y: 0
1945 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1946 sendFbuMsg(info
, [rect
], client
);
1949 for (let i
= 0; i
< 4; i
++) { expected
= expected
.concat(targetDataCheckArr
); }
1950 expected
= expected
.concat(targetDataCheckArr
.slice(0, 16));
1951 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1954 it('should fail on an invalid subencoding', function () {
1955 sinon
.spy(client
, "_fail");
1956 const info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1957 const rects
= [[45]]; // an invalid subencoding
1958 sendFbuMsg(info
, rects
, client
);
1959 expect(client
._fail
).to
.have
.been
.calledOnce
;
1963 it
.skip('should handle the TIGHT encoding', function () {
1964 // TODO(directxman12): test this
1967 it
.skip('should handle the TIGHT_PNG encoding', function () {
1968 // TODO(directxman12): test this
1971 it('should handle the DesktopSize pseduo-encoding', function () {
1972 sinon
.spy(client
._display
, 'resize');
1973 sendFbuMsg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1975 expect(client
._fbWidth
).to
.equal(20);
1976 expect(client
._fbHeight
).to
.equal(50);
1978 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1979 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1982 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1983 beforeEach(function () {
1984 // a really small frame
1985 client
._fbWidth
= 4;
1986 client
._fbHeight
= 4;
1987 client
._display
.resize(4, 4);
1988 sinon
.spy(client
._display
, 'resize');
1991 function makeScreenData(nrOfScreens
) {
1993 push8(data
, nrOfScreens
); // number-of-screens
1994 push8(data
, 0); // padding
1995 push16(data
, 0); // padding
1996 for (let i
=0; i
<nrOfScreens
; i
+= 1) {
1997 push32(data
, 0); // id
1998 push16(data
, 0); // x-position
1999 push16(data
, 0); // y-position
2000 push16(data
, 20); // width
2001 push16(data
, 50); // height
2002 push32(data
, 0); // flags
2007 it('should handle a resize requested by this client', function () {
2008 const reasonForChange
= 1; // requested by this client
2009 const statusCode
= 0; // No error
2011 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
2012 width
: 20, height
: 50, encoding
: -308 }],
2013 makeScreenData(1), client
);
2015 expect(client
._fbWidth
).to
.equal(20);
2016 expect(client
._fbHeight
).to
.equal(50);
2018 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
2019 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
2022 it('should handle a resize requested by another client', function () {
2023 const reasonForChange
= 2; // requested by another client
2024 const statusCode
= 0; // No error
2026 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
2027 width
: 20, height
: 50, encoding
: -308 }],
2028 makeScreenData(1), client
);
2030 expect(client
._fbWidth
).to
.equal(20);
2031 expect(client
._fbHeight
).to
.equal(50);
2033 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
2034 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
2037 it('should be able to recieve requests which contain data for multiple screens', function () {
2038 const reasonForChange
= 2; // requested by another client
2039 const statusCode
= 0; // No error
2041 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
2042 width
: 60, height
: 50, encoding
: -308 }],
2043 makeScreenData(3), client
);
2045 expect(client
._fbWidth
).to
.equal(60);
2046 expect(client
._fbHeight
).to
.equal(50);
2048 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
2049 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
2052 it('should not handle a failed request', function () {
2053 const reasonForChange
= 1; // requested by this client
2054 const statusCode
= 1; // Resize is administratively prohibited
2056 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
2057 width
: 20, height
: 50, encoding
: -308 }],
2058 makeScreenData(1), client
);
2060 expect(client
._fbWidth
).to
.equal(4);
2061 expect(client
._fbHeight
).to
.equal(4);
2063 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
2067 describe('the Cursor pseudo-encoding handler', function () {
2068 beforeEach(function () {
2069 sinon
.spy(client
._cursor
, 'change');
2072 it('should handle a standard cursor', function () {
2073 const info
= { x
: 5, y
: 7,
2074 width
: 4, height
: 4,
2079 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2080 push32(rect
, 0x11223300);
2082 push32(rect
, 0xa0a0a0a0);
2084 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
2085 push32(expected
, 0x332211ff);
2086 push32(expected
, 0x33221100);
2088 expected
= new Uint8Array(expected
);
2090 sendFbuMsg([info
], [rect
], client
);
2092 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2093 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
2096 it('should handle an empty cursor', function () {
2097 const info
= { x
: 0, y
: 0,
2098 width
: 0, height
: 0,
2102 sendFbuMsg([info
], [rect
], client
);
2104 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2105 expect(client
._cursor
.change
).to
.have
.been
.calledWith(new Uint8Array
, 0, 0, 0, 0);
2108 it('should handle a transparent cursor', function () {
2109 const info
= { x
: 5, y
: 7,
2110 width
: 4, height
: 4,
2115 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2116 push32(rect
, 0x11223300);
2118 push32(rect
, 0x00000000);
2120 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2121 push32(expected
, 0x33221100);
2123 expected
= new Uint8Array(expected
);
2125 sendFbuMsg([info
], [rect
], client
);
2127 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2128 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
2131 describe('dot for empty cursor', function () {
2132 beforeEach(function () {
2133 client
.showDotCursor
= true;
2134 // Was called when we enabled dot cursor
2135 client
._cursor
.change
.resetHistory();
2138 it('should show a standard cursor', function () {
2139 const info
= { x
: 5, y
: 7,
2140 width
: 4, height
: 4,
2145 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2146 push32(rect
, 0x11223300);
2148 push32(rect
, 0xa0a0a0a0);
2150 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
2151 push32(expected
, 0x332211ff);
2152 push32(expected
, 0x33221100);
2154 expected
= new Uint8Array(expected
);
2156 sendFbuMsg([info
], [rect
], client
);
2158 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2159 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
2162 it('should handle an empty cursor', function () {
2163 const info
= { x
: 0, y
: 0,
2164 width
: 0, height
: 0,
2167 const dot
= RFB
.cursors
.dot
;
2169 sendFbuMsg([info
], [rect
], client
);
2171 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2172 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
2179 it('should handle a transparent cursor', function () {
2180 const info
= { x
: 5, y
: 7,
2181 width
: 4, height
: 4,
2184 const dot
= RFB
.cursors
.dot
;
2186 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
2187 push32(rect
, 0x11223300);
2189 push32(rect
, 0x00000000);
2191 sendFbuMsg([info
], [rect
], client
);
2193 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
2194 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
2203 describe('the VMware Cursor pseudo-encoding handler', function () {
2204 beforeEach(function () {
2205 sinon
.spy(client
._cursor
, 'change');
2207 afterEach(function () {
2208 client
._cursor
.change
.resetHistory();
2211 it('should handle the VMware cursor pseudo-encoding', function () {
2212 let data
= [0x00, 0x00, 0xff, 0,
2213 0x00, 0xff, 0x00, 0,
2214 0x00, 0xff, 0x00, 0,
2215 0x00, 0x00, 0xff, 0];
2221 for (let i
= 0; i
< data
.length
; i
++) {
2222 push8(rect
, data
[i
]);
2225 for (let i
= 0; i
< data
.length
; i
++) {
2226 push8(rect
, data
[i
]);
2229 sendFbuMsg([{ x
: 0, y
: 0, width
: 2, height
: 2,
2230 encoding
: 0x574d5664}],
2232 expect(client
._FBU
.rects
).to
.equal(0);
2235 it('should handle insufficient cursor pixel data', function () {
2237 // Specified 14x23 pixels for the cursor,
2238 // but only send 2x2 pixels worth of data
2241 let data
= [0x00, 0x00, 0xff, 0,
2242 0x00, 0xff, 0x00, 0];
2249 for (let i
= 0; i
< data
.length
; i
++) {
2250 push8(rect
, data
[i
]);
2253 for (let i
= 0; i
< data
.length
; i
++) {
2254 push8(rect
, data
[i
]);
2257 sendFbuMsg([{ x
: 0, y
: 0, width
: w
, height
: h
,
2258 encoding
: 0x574d5664}],
2261 // expect one FBU to remain unhandled
2262 expect(client
._FBU
.rects
).to
.equal(1);
2265 it('should update the cursor when type is classic', function () {
2267 [0xff, 0xff, 0xff, 0xff, //Transparent
2268 0xff, 0xff, 0xff, 0xff, //Transparent
2269 0x00, 0x00, 0x00, 0x00, //Opaque
2270 0xff, 0xff, 0xff, 0xff]; //Inverted
2273 [0x00, 0x00, 0x00, 0x00, //Transparent
2274 0x00, 0x00, 0x00, 0x00, //Transparent
2275 0x11, 0x22, 0x33, 0x44, //Opaque
2276 0xff, 0xff, 0xff, 0x44]; //Inverted
2279 push8(rect
, 0); //cursor_type
2280 push8(rect
, 0); //padding
2287 for (let i
= 0; i
< andMask
.length
; i
++) {
2288 push8(rect
, andMask
[i
]);
2291 for (let i
= 0; i
< xorMask
.length
; i
++) {
2292 push8(rect
, xorMask
[i
]);
2295 let expectedRgba
= [0x00, 0x00, 0x00, 0x00,
2296 0x00, 0x00, 0x00, 0x00,
2297 0x33, 0x22, 0x11, 0xff,
2298 0x00, 0x00, 0x00, 0xff];
2300 sendFbuMsg([{ x
: hotx
, y
: hoty
,
2301 width
: w
, height
: h
,
2302 encoding
: 0x574d5664}],
2305 expect(client
._cursor
.change
)
2306 .to
.have
.been
.calledOnce
;
2307 expect(client
._cursor
.change
)
2308 .to
.have
.been
.calledWith(expectedRgba
,
2313 it('should update the cursor when type is alpha', function () {
2314 let data
= [0xee, 0x55, 0xff, 0x00, // rgba
2315 0x00, 0xff, 0x00, 0xff,
2316 0x00, 0xff, 0x00, 0x22,
2317 0x00, 0xff, 0x00, 0x22,
2318 0x00, 0xff, 0x00, 0x22,
2319 0x00, 0x00, 0xff, 0xee];
2321 push8(rect
, 1); //cursor_type
2322 push8(rect
, 0); //padding
2328 for (let i
= 0; i
< data
.length
; i
++) {
2329 push8(rect
, data
[i
]);
2332 let expectedRgba
= [0xee, 0x55, 0xff, 0x00,
2333 0x00, 0xff, 0x00, 0xff,
2334 0x00, 0xff, 0x00, 0x22,
2335 0x00, 0xff, 0x00, 0x22,
2336 0x00, 0xff, 0x00, 0x22,
2337 0x00, 0x00, 0xff, 0xee];
2339 sendFbuMsg([{ x
: hotx
, y
: hoty
,
2340 width
: w
, height
: h
,
2341 encoding
: 0x574d5664}],
2344 expect(client
._cursor
.change
)
2345 .to
.have
.been
.calledOnce
;
2346 expect(client
._cursor
.change
)
2347 .to
.have
.been
.calledWith(expectedRgba
,
2352 it('should not update cursor when incorrect cursor type given', function () {
2354 push8(rect
, 3); // invalid cursor type
2355 push8(rect
, 0); // padding
2357 client
._cursor
.change
.resetHistory();
2358 sendFbuMsg([{ x
: 0, y
: 0, width
: 2, height
: 2,
2359 encoding
: 0x574d5664}],
2362 expect(client
._cursor
.change
)
2363 .to
.not
.have
.been
.called
;
2367 it('should handle the last_rect pseudo-encoding', function () {
2368 sendFbuMsg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
2369 expect(client
._FBU
.rects
).to
.equal(0);
2372 it('should handle the DesktopName pseudo-encoding', function () {
2375 pushString(data
, "som€ nam€");
2377 const spy
= sinon
.spy();
2378 client
.addEventListener("desktopname", spy
);
2380 sendFbuMsg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -307 }], [data
], client
);
2382 expect(client
._fbName
).to
.equal('som€ nam€');
2383 expect(spy
).to
.have
.been
.calledOnce
;
2384 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
2389 describe('XVP Message Handling', function () {
2390 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
2391 const spy
= sinon
.spy();
2392 client
.addEventListener("capabilities", spy
);
2393 client
._sock
._websocket
._receiveData(new Uint8Array([250, 0, 10, 1]));
2394 expect(client
._rfbXvpVer
).to
.equal(10);
2395 expect(spy
).to
.have
.been
.calledOnce
;
2396 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
2397 expect(client
.capabilities
.power
).to
.be
.true;
2400 it('should fail on unknown XVP message types', function () {
2401 sinon
.spy(client
, "_fail");
2402 client
._sock
._websocket
._receiveData(new Uint8Array([250, 0, 10, 237]));
2403 expect(client
._fail
).to
.have
.been
.calledOnce
;
2407 describe('Normal Clipboard Handling Receive', function () {
2408 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
2409 const expectedStr
= 'cheese!';
2410 const data
= [3, 0, 0, 0];
2411 push32(data
, expectedStr
.length
);
2412 for (let i
= 0; i
< expectedStr
.length
; i
++) { data
.push(expectedStr
.charCodeAt(i
)); }
2413 const spy
= sinon
.spy();
2414 client
.addEventListener("clipboard", spy
);
2416 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2417 expect(spy
).to
.have
.been
.calledOnce
;
2418 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedStr
);
2422 describe('Extended clipboard Handling', function () {
2424 describe('Extended clipboard initialization', function () {
2425 beforeEach(function () {
2426 sinon
.spy(RFB
.messages
, 'extendedClipboardCaps');
2429 afterEach(function () {
2430 RFB
.messages
.extendedClipboardCaps
.restore();
2433 it('should update capabilities when receiving a Caps message', function () {
2434 let data
= [3, 0, 0, 0];
2435 const flags
= [0x1F, 0x00, 0x00, 0x03];
2436 let fileSizes
= [0x00, 0x00, 0x00, 0x1E,
2437 0x00, 0x00, 0x00, 0x3C];
2439 push32(data
, toUnsigned32bit(-12));
2440 data
= data
.concat(flags
);
2441 data
= data
.concat(fileSizes
);
2442 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2444 // Check that we give an response caps when we receive one
2445 expect(RFB
.messages
.extendedClipboardCaps
).to
.have
.been
.calledOnce
;
2447 // FIXME: Can we avoid checking internal variables?
2448 expect(client
._clipboardServerCapabilitiesFormats
[0]).to
.not
.equal(true);
2449 expect(client
._clipboardServerCapabilitiesFormats
[1]).to
.equal(true);
2450 expect(client
._clipboardServerCapabilitiesFormats
[2]).to
.equal(true);
2451 expect(client
._clipboardServerCapabilitiesActions
[(1 << 24)]).to
.equal(true);
2457 describe('Extended Clipboard Handling Receive', function () {
2459 beforeEach(function () {
2460 // Send our capabilities
2461 let data
= [3, 0, 0, 0];
2462 const flags
= [0x1F, 0x00, 0x00, 0x01];
2463 let fileSizes
= [0x00, 0x00, 0x00, 0x1E];
2465 push32(data
, toUnsigned32bit(-8));
2466 data
= data
.concat(flags
);
2467 data
= data
.concat(fileSizes
);
2468 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2471 describe('Handle Provide', function () {
2472 it('should update clipboard with correct Unicode data from a Provide message', function () {
2473 let expectedData
= "Aå漢å—!";
2474 let data
= [3, 0, 0, 0];
2475 const flags
= [0x10, 0x00, 0x00, 0x01];
2477 /* The size 10 (utf8 encoded string size) and the
2478 string "Aå漢å—!" utf8 encoded and deflated. */
2479 let deflatedData
= [120, 94, 99, 96, 96, 224, 114, 60,
2480 188, 244, 217, 158, 69, 79, 215,
2481 78, 87, 4, 0, 35, 207, 6, 66];
2483 // How much data we are sending.
2484 push32(data
, toUnsigned32bit(-(4 + deflatedData
.length
)));
2486 data
= data
.concat(flags
);
2487 data
= data
.concat(deflatedData
);
2489 const spy
= sinon
.spy();
2490 client
.addEventListener("clipboard", spy
);
2492 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2493 expect(spy
).to
.have
.been
.calledOnce
;
2494 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2495 client
.removeEventListener("clipboard", spy
);
2498 it('should update clipboard with correct escape characters from a Provide message ', function () {
2499 let expectedData
= "Oh\nmy!";
2500 let data
= [3, 0, 0, 0];
2501 const flags
= [0x10, 0x00, 0x00, 0x01];
2503 let text
= encodeUTF8("Oh\r\nmy!\0");
2505 let deflatedText
= deflateWithSize(text
);
2507 // How much data we are sending.
2508 push32(data
, toUnsigned32bit(-(4 + deflatedText
.length
)));
2510 data
= data
.concat(flags
);
2512 let sendData
= new Uint8Array(data
.length
+ deflatedText
.length
);
2514 sendData
.set(deflatedText
, data
.length
);
2516 const spy
= sinon
.spy();
2517 client
.addEventListener("clipboard", spy
);
2519 client
._sock
._websocket
._receiveData(sendData
);
2520 expect(spy
).to
.have
.been
.calledOnce
;
2521 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2522 client
.removeEventListener("clipboard", spy
);
2525 it('should be able to handle large Provide messages', function () {
2526 // repeat() is not supported in IE so a loop is needed instead
2527 let expectedData
= "hello";
2528 for (let i
= 1; i
<= 100000; i
++) {
2529 expectedData
+= "hello";
2532 let data
= [3, 0, 0, 0];
2533 const flags
= [0x10, 0x00, 0x00, 0x01];
2535 let text
= encodeUTF8(expectedData
+ "\0");
2537 let deflatedText
= deflateWithSize(text
);
2539 // How much data we are sending.
2540 push32(data
, toUnsigned32bit(-(4 + deflatedText
.length
)));
2542 data
= data
.concat(flags
);
2544 let sendData
= new Uint8Array(data
.length
+ deflatedText
.length
);
2546 sendData
.set(deflatedText
, data
.length
);
2548 const spy
= sinon
.spy();
2549 client
.addEventListener("clipboard", spy
);
2551 client
._sock
._websocket
._receiveData(sendData
);
2552 expect(spy
).to
.have
.been
.calledOnce
;
2553 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2554 client
.removeEventListener("clipboard", spy
);
2559 describe('Handle Notify', function () {
2560 beforeEach(function () {
2561 sinon
.spy(RFB
.messages
, 'extendedClipboardRequest');
2564 afterEach(function () {
2565 RFB
.messages
.extendedClipboardRequest
.restore();
2568 it('should make a request with supported formats when receiving a notify message', function () {
2569 let data
= [3, 0, 0, 0];
2570 const flags
= [0x08, 0x00, 0x00, 0x07];
2571 push32(data
, toUnsigned32bit(-4));
2572 data
= data
.concat(flags
);
2573 let expectedData
= [0x01];
2575 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2577 expect(RFB
.messages
.extendedClipboardRequest
).to
.have
.been
.calledOnce
;
2578 expect(RFB
.messages
.extendedClipboardRequest
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2582 describe('Handle Peek', function () {
2583 beforeEach(function () {
2584 sinon
.spy(RFB
.messages
, 'extendedClipboardNotify');
2587 afterEach(function () {
2588 RFB
.messages
.extendedClipboardNotify
.restore();
2591 it('should send an empty Notify when receiving a Peek and no excisting clipboard data', function () {
2592 let data
= [3, 0, 0, 0];
2593 const flags
= [0x04, 0x00, 0x00, 0x00];
2594 push32(data
, toUnsigned32bit(-4));
2595 data
= data
.concat(flags
);
2596 let expectedData
= [];
2598 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2600 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
2601 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2604 it('should send a Notify message with supported formats when receiving a Peek', function () {
2605 let data
= [3, 0, 0, 0];
2606 const flags
= [0x04, 0x00, 0x00, 0x00];
2607 push32(data
, toUnsigned32bit(-4));
2608 data
= data
.concat(flags
);
2609 let expectedData
= [0x01];
2611 // Needed to have clipboard data to read.
2612 // This will trigger a call to Notify, reset history
2613 client
.clipboardPasteFrom("HejHej");
2614 RFB
.messages
.extendedClipboardNotify
.resetHistory();
2616 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2618 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
2619 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2623 describe('Handle Request', function () {
2624 beforeEach(function () {
2625 sinon
.spy(RFB
.messages
, 'extendedClipboardProvide');
2628 afterEach(function () {
2629 RFB
.messages
.extendedClipboardProvide
.restore();
2632 it('should send a Provide message with supported formats when receiving a Request', function () {
2633 let data
= [3, 0, 0, 0];
2634 const flags
= [0x02, 0x00, 0x00, 0x01];
2635 push32(data
, toUnsigned32bit(-4));
2636 data
= data
.concat(flags
);
2637 let expectedData
= [0x01];
2639 client
.clipboardPasteFrom("HejHej");
2640 expect(RFB
.messages
.extendedClipboardProvide
).to
.not
.have
.been
.called
;
2642 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2644 expect(RFB
.messages
.extendedClipboardProvide
).to
.have
.been
.calledOnce
;
2645 expect(RFB
.messages
.extendedClipboardProvide
).to
.have
.been
.calledWith(client
._sock
, expectedData
, ["HejHej"]);
2652 it('should fire the bell callback on Bell', function () {
2653 const spy
= sinon
.spy();
2654 client
.addEventListener("bell", spy
);
2655 client
._sock
._websocket
._receiveData(new Uint8Array([2]));
2656 expect(spy
).to
.have
.been
.calledOnce
;
2659 it('should respond correctly to ServerFence', function () {
2660 const expectedMsg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2661 const incomingMsg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2663 const payload
= "foo\x00ab9";
2665 // ClientFence and ServerFence are identical in structure
2666 RFB
.messages
.clientFence(expectedMsg
, (1<<0) | (1<<1), payload
);
2667 RFB
.messages
.clientFence(incomingMsg
, 0xffffffff, payload
);
2669 client
._sock
._websocket
._receiveData(incomingMsg
._sQ
);
2671 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2673 expectedMsg
._sQlen
= 0;
2674 incomingMsg
._sQlen
= 0;
2676 RFB
.messages
.clientFence(expectedMsg
, (1<<0), payload
);
2677 RFB
.messages
.clientFence(incomingMsg
, (1<<0) | (1<<31), payload
);
2679 client
._sock
._websocket
._receiveData(incomingMsg
._sQ
);
2681 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2684 it('should enable continuous updates on first EndOfContinousUpdates', function () {
2685 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2687 RFB
.messages
.enableContinuousUpdates(expectedMsg
, true, 0, 0, 640, 20);
2689 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2691 client
._sock
._websocket
._receiveData(new Uint8Array([150]));
2693 expect(client
._enabledContinuousUpdates
).to
.be
.true;
2694 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2697 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
2698 client
._enabledContinuousUpdates
= true;
2699 client
._supportsContinuousUpdates
= true;
2701 client
._sock
._websocket
._receiveData(new Uint8Array([150]));
2703 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2706 it('should update continuous updates on resize', function () {
2707 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2708 RFB
.messages
.enableContinuousUpdates(expectedMsg
, true, 0, 0, 90, 700);
2710 client
._resize(450, 160);
2712 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
2714 client
._enabledContinuousUpdates
= true;
2716 client
._resize(90, 700);
2718 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2721 it('should fail on an unknown message type', function () {
2722 sinon
.spy(client
, "_fail");
2723 client
._sock
._websocket
._receiveData(new Uint8Array([87]));
2724 expect(client
._fail
).to
.have
.been
.calledOnce
;
2728 describe('Asynchronous Events', function () {
2730 beforeEach(function () {
2734 describe('Mouse event handlers', function () {
2735 beforeEach(function () {
2736 this.clock
= sinon
.useFakeTimers(Date
.now());
2737 sinon
.spy(RFB
.messages
, 'pointerEvent');
2739 afterEach(function () {
2740 this.clock
.restore();
2741 RFB
.messages
.pointerEvent
.restore();
2744 it('should not send button messages in view-only mode', function () {
2745 client
._viewOnly
= true;
2746 client
._handleMouseButton(0, 0, 1, 0x001);
2747 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
2750 it('should not send movement messages in view-only mode', function () {
2751 client
._viewOnly
= true;
2752 client
._handleMouseMove(0, 0);
2753 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
2756 it('should send a pointer event on mouse button presses', function () {
2757 client
._handleMouseButton(10, 12, 1, 0x001);
2758 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
2761 it('should send a mask of 1 on mousedown', function () {
2762 client
._handleMouseButton(11, 13, 1, 0x001);
2763 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledWith(
2764 client
._sock
, 11, 13, 0x001);
2767 it('should send a mask of 0 on mouseup', function () {
2768 client
._mouseButtonMask
= 0x001;
2769 client
._handleMouseButton(105, 120, 0, 0x001);
2770 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledWith(
2771 client
._sock
, 105, 120, 0x000);
2774 it('should send a mask of 0 on mousemove', function () {
2775 client
._handleMouseMove(100, 200);
2776 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledWith(
2777 client
._sock
, 100, 200, 0x000);
2780 it('should set the button mask so that future mouse movements use it', function () {
2781 client
._handleMouseButton(10, 12, 1, 0x010);
2782 client
._handleMouseMove(13, 9);
2783 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
2784 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledWith(
2785 client
._sock
, 13, 9, 0x010);
2788 it('should send a single pointer event on mouse movement', function () {
2789 client
._handleMouseMove(100, 200);
2790 this.clock
.tick(100);
2791 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
2794 it('should delay one move if two events are too close', function () {
2795 client
._handleMouseMove(18, 30);
2796 client
._handleMouseMove(20, 50);
2797 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
2798 this.clock
.tick(100);
2799 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
2802 it('should only send first and last move of many close events', function () {
2803 client
._handleMouseMove(18, 40);
2804 client
._handleMouseMove(20, 50);
2805 client
._handleMouseMove(21, 55);
2807 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
2808 this.clock
.tick(60);
2810 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
2811 expect(RFB
.messages
.pointerEvent
.firstCall
).to
.have
.been
.calledWith(
2812 client
._sock
, 18, 40, 0x000);
2813 expect(RFB
.messages
.pointerEvent
.secondCall
).to
.have
.been
.calledWith(
2814 client
._sock
, 21, 55, 0x000);
2817 // We selected the 17ms since that is ~60 FPS
2818 it('should send move events every 17 ms', function () {
2819 client
._handleMouseMove(1, 10); // instant send
2820 this.clock
.tick(10);
2821 client
._handleMouseMove(2, 20); // delayed
2822 this.clock
.tick(10); // timeout send
2823 client
._handleMouseMove(3, 30); // delayed
2824 this.clock
.tick(10);
2825 client
._handleMouseMove(4, 40); // delayed
2826 this.clock
.tick(10); // timeout send
2827 client
._handleMouseMove(5, 50); // delayed
2829 expect(RFB
.messages
.pointerEvent
).to
.have
.callCount(3);
2830 expect(RFB
.messages
.pointerEvent
.firstCall
).to
.have
.been
.calledWith(
2831 client
._sock
, 1, 10, 0x000);
2832 expect(RFB
.messages
.pointerEvent
.secondCall
).to
.have
.been
.calledWith(
2833 client
._sock
, 2, 20, 0x000);
2834 expect(RFB
.messages
.pointerEvent
.thirdCall
).to
.have
.been
.calledWith(
2835 client
._sock
, 4, 40, 0x000);
2838 it('should send waiting move events before a button press', function () {
2839 client
._handleMouseMove(13, 9);
2840 client
._handleMouseMove(20, 70);
2841 client
._handleMouseButton(10, 12, 1, 0x100);
2842 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledThrice
;
2843 expect(RFB
.messages
.pointerEvent
.firstCall
).to
.have
.been
.calledWith(
2844 client
._sock
, 13, 9, 0x000);
2845 expect(RFB
.messages
.pointerEvent
.secondCall
).to
.have
.been
.calledWith(
2846 client
._sock
, 10, 12, 0x000);
2847 expect(RFB
.messages
.pointerEvent
.thirdCall
).to
.have
.been
.calledWith(
2848 client
._sock
, 10, 12, 0x100);
2851 it('should not delay events when button mask changes', function () {
2852 client
._handleMouseMove(13, 9); // instant
2853 client
._handleMouseMove(11, 10); // delayed
2854 client
._handleMouseButton(10, 12, 1, 0x010); // flush delayed
2855 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledThrice
;
2858 it('should send move events with enough time apart normally', function () {
2859 client
._handleMouseMove(58, 60);
2860 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
2862 this.clock
.tick(20);
2864 client
._handleMouseMove(25, 60);
2865 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
2868 it('should not send waiting move events if disconnected', function () {
2869 client
._handleMouseMove(88, 99);
2870 client
._handleMouseMove(66, 77);
2871 client
.disconnect();
2872 this.clock
.tick(20);
2873 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
2877 describe('Keyboard Event Handlers', function () {
2878 it('should send a key message on a key press', function () {
2879 client
._handleKeyEvent(0x41, 'KeyA', true);
2880 const keyMsg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
2881 RFB
.messages
.keyEvent(keyMsg
, 0x41, 1);
2882 expect(client
._sock
).to
.have
.sent(keyMsg
._sQ
);
2885 it('should not send messages in view-only mode', function () {
2886 client
._viewOnly
= true;
2887 sinon
.spy(client
._sock
, 'flush');
2888 client
._handleKeyEvent('a', 'KeyA', true);
2889 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2893 describe('WebSocket event handlers', function () {
2895 it('should do nothing if we receive an empty message and have nothing in the queue', function () {
2896 client
._normalMsg
= sinon
.spy();
2897 client
._sock
._websocket
._receiveData(new Uint8Array([]));
2898 expect(client
._normalMsg
).to
.not
.have
.been
.called
;
2901 it('should handle a message in the connected state as a normal message', function () {
2902 client
._normalMsg
= sinon
.spy();
2903 client
._sock
._websocket
._receiveData(new Uint8Array([1, 2, 3]));
2904 expect(client
._normalMsg
).to
.have
.been
.called
;
2907 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2908 client
._rfbConnectionState
= 'connecting';
2909 client
._rfbInitState
= 'ProtocolVersion';
2910 client
._initMsg
= sinon
.spy();
2911 client
._sock
._websocket
._receiveData(new Uint8Array([1, 2, 3]));
2912 expect(client
._initMsg
).to
.have
.been
.called
;
2915 it('should process all normal messages directly', function () {
2916 const spy
= sinon
.spy();
2917 client
.addEventListener("bell", spy
);
2918 client
._sock
._websocket
._receiveData(new Uint8Array([0x02, 0x02]));
2919 expect(spy
).to
.have
.been
.calledTwice
;
2923 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2924 client
= new RFB(document
.createElement('div'), 'wss://host:8675');
2926 client
._sock
._websocket
._open();
2927 expect(client
._rfbInitState
).to
.equal('ProtocolVersion');
2930 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2931 sinon
.spy(client
, "_fail");
2932 client
._rfbConnectionState
= 'connected';
2933 client
._sock
._websocket
._open();
2934 expect(client
._fail
).to
.have
.been
.calledOnce
;
2938 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2939 const real
= client
._sock
._websocket
.close
;
2940 client
._sock
._websocket
.close
= () => {};
2941 client
.disconnect();
2942 expect(client
._rfbConnectionState
).to
.equal('disconnecting');
2943 client
._sock
._websocket
.close
= real
;
2944 client
._sock
._websocket
.close();
2945 expect(client
._rfbConnectionState
).to
.equal('disconnected');
2948 it('should fail if we get a close event while connecting', function () {
2949 sinon
.spy(client
, "_fail");
2950 client
._rfbConnectionState
= 'connecting';
2951 client
._sock
._websocket
.close();
2952 expect(client
._fail
).to
.have
.been
.calledOnce
;
2955 it('should unregister close event handler', function () {
2956 sinon
.spy(client
._sock
, 'off');
2957 client
.disconnect();
2958 client
._sock
._websocket
.close();
2959 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2962 // error events do nothing
2966 describe('Quality level setting', function () {
2967 const defaultQuality
= 6;
2971 beforeEach(function () {
2973 sinon
.spy(RFB
.messages
, "clientEncodings");
2976 afterEach(function () {
2977 RFB
.messages
.clientEncodings
.restore();
2980 it(`should equal ${defaultQuality} by default`, function () {
2981 expect(client
._qualityLevel
).to
.equal(defaultQuality
);
2984 it('should ignore non-integers when set', function () {
2985 client
.qualityLevel
= '1';
2986 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
2988 RFB
.messages
.clientEncodings
.resetHistory();
2990 client
.qualityLevel
= 1.5;
2991 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
2993 RFB
.messages
.clientEncodings
.resetHistory();
2995 client
.qualityLevel
= null;
2996 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
2998 RFB
.messages
.clientEncodings
.resetHistory();
3000 client
.qualityLevel
= undefined;
3001 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3003 RFB
.messages
.clientEncodings
.resetHistory();
3005 client
.qualityLevel
= {};
3006 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3009 it('should ignore integers out of range [0, 9]', function () {
3010 client
.qualityLevel
= -1;
3011 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3013 RFB
.messages
.clientEncodings
.resetHistory();
3015 client
.qualityLevel
= 10;
3016 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3019 it('should send clientEncodings with new quality value', function () {
3023 client
.qualityLevel
= newQuality
;
3024 expect(client
.qualityLevel
).to
.equal(newQuality
);
3025 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3026 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3029 it('should not send clientEncodings if quality is the same', function () {
3033 client
.qualityLevel
= newQuality
;
3034 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3035 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3037 RFB
.messages
.clientEncodings
.resetHistory();
3039 client
.qualityLevel
= newQuality
;
3040 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3043 it('should not send clientEncodings if not in connected state', function () {
3046 client
._rfbConnectionState
= '';
3048 client
.qualityLevel
= newQuality
;
3049 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3051 RFB
.messages
.clientEncodings
.resetHistory();
3053 client
._rfbConnectionState
= 'connnecting';
3055 client
.qualityLevel
= newQuality
;
3056 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3058 RFB
.messages
.clientEncodings
.resetHistory();
3060 client
._rfbConnectionState
= 'connected';
3062 client
.qualityLevel
= newQuality
;
3063 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3064 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3068 describe('Compression level setting', function () {
3069 const defaultCompression
= 2;
3073 beforeEach(function () {
3075 sinon
.spy(RFB
.messages
, "clientEncodings");
3078 afterEach(function () {
3079 RFB
.messages
.clientEncodings
.restore();
3082 it(`should equal ${defaultCompression} by default`, function () {
3083 expect(client
._compressionLevel
).to
.equal(defaultCompression
);
3086 it('should ignore non-integers when set', function () {
3087 client
.compressionLevel
= '1';
3088 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3090 RFB
.messages
.clientEncodings
.resetHistory();
3092 client
.compressionLevel
= 1.5;
3093 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3095 RFB
.messages
.clientEncodings
.resetHistory();
3097 client
.compressionLevel
= null;
3098 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3100 RFB
.messages
.clientEncodings
.resetHistory();
3102 client
.compressionLevel
= undefined;
3103 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3105 RFB
.messages
.clientEncodings
.resetHistory();
3107 client
.compressionLevel
= {};
3108 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3111 it('should ignore integers out of range [0, 9]', function () {
3112 client
.compressionLevel
= -1;
3113 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3115 RFB
.messages
.clientEncodings
.resetHistory();
3117 client
.compressionLevel
= 10;
3118 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3121 it('should send clientEncodings with new compression value', function () {
3125 client
.compressionLevel
= newCompression
;
3126 expect(client
.compressionLevel
).to
.equal(newCompression
);
3127 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3128 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3131 it('should not send clientEncodings if compression is the same', function () {
3135 client
.compressionLevel
= newCompression
;
3136 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3137 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3139 RFB
.messages
.clientEncodings
.resetHistory();
3141 client
.compressionLevel
= newCompression
;
3142 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3145 it('should not send clientEncodings if not in connected state', function () {
3148 client
._rfbConnectionState
= '';
3150 client
.compressionLevel
= newCompression
;
3151 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3153 RFB
.messages
.clientEncodings
.resetHistory();
3155 client
._rfbConnectionState
= 'connnecting';
3157 client
.compressionLevel
= newCompression
;
3158 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3160 RFB
.messages
.clientEncodings
.resetHistory();
3162 client
._rfbConnectionState
= 'connected';
3164 client
.compressionLevel
= newCompression
;
3165 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3166 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3171 describe('RFB messages', function () {
3174 before(function () {
3175 FakeWebSocket
.replace();
3176 sock
= new Websock();
3181 FakeWebSocket
.restore();
3184 describe('Extended Clipboard Handling Send', function () {
3185 beforeEach(function () {
3186 sinon
.spy(RFB
.messages
, 'clientCutText');
3189 afterEach(function () {
3190 RFB
.messages
.clientCutText
.restore();
3193 it('should call clientCutText with correct Caps data', function () {
3198 let expectedData
= new Uint8Array([0x1F, 0x00, 0x00, 0x05,
3199 0x00, 0x00, 0x00, 0x02,
3200 0x00, 0x00, 0x10, 0x19]);
3209 RFB
.messages
.extendedClipboardCaps(sock
, actions
, formats
);
3210 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3211 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3214 it('should call clientCutText with correct Request data', function () {
3215 let formats
= new Uint8Array([0x01]);
3216 let expectedData
= new Uint8Array([0x02, 0x00, 0x00, 0x01]);
3218 RFB
.messages
.extendedClipboardRequest(sock
, formats
);
3219 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3220 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3223 it('should call clientCutText with correct Notify data', function () {
3224 let formats
= new Uint8Array([0x01]);
3225 let expectedData
= new Uint8Array([0x08, 0x00, 0x00, 0x01]);
3227 RFB
.messages
.extendedClipboardNotify(sock
, formats
);
3228 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3229 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3232 it('should call clientCutText with correct Provide data', function () {
3233 let testText
= "Test string";
3234 let expectedText
= encodeUTF8(testText
+ "\0");
3236 let deflatedData
= deflateWithSize(expectedText
);
3238 // Build Expected with flags and deflated data
3239 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3240 expectedData
[0] = 0x10; // The client capabilities
3241 expectedData
[1] = 0x00; // Reserved flags
3242 expectedData
[2] = 0x00; // Reserved flags
3243 expectedData
[3] = 0x01; // The formats client supports
3244 expectedData
.set(deflatedData
, 4);
3246 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3247 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3248 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3252 describe('End of line characters', function () {
3253 it('Carriage return', function () {
3255 let testText
= "Hello\rworld\r\r!";
3256 let expectedText
= encodeUTF8("Hello\r\nworld\r\n\r\n!\0");
3258 let deflatedData
= deflateWithSize(expectedText
);
3260 // Build Expected with flags and deflated data
3261 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3262 expectedData
[0] = 0x10; // The client capabilities
3263 expectedData
[1] = 0x00; // Reserved flags
3264 expectedData
[2] = 0x00; // Reserved flags
3265 expectedData
[3] = 0x01; // The formats client supports
3266 expectedData
.set(deflatedData
, 4);
3268 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3269 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3270 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3273 it('Carriage return Line feed', function () {
3275 let testText
= "Hello\r\n\r\nworld\r\n!";
3276 let expectedText
= encodeUTF8(testText
+ "\0");
3278 let deflatedData
= deflateWithSize(expectedText
);
3280 // Build Expected with flags and deflated data
3281 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3282 expectedData
[0] = 0x10; // The client capabilities
3283 expectedData
[1] = 0x00; // Reserved flags
3284 expectedData
[2] = 0x00; // Reserved flags
3285 expectedData
[3] = 0x01; // The formats client supports
3286 expectedData
.set(deflatedData
, 4);
3288 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3289 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3290 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3293 it('Line feed', function () {
3294 let testText
= "Hello\n\n\nworld\n!";
3295 let expectedText
= encodeUTF8("Hello\r\n\r\n\r\nworld\r\n!\0");
3297 let deflatedData
= deflateWithSize(expectedText
);
3299 // Build Expected with flags and deflated data
3300 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3301 expectedData
[0] = 0x10; // The client capabilities
3302 expectedData
[1] = 0x00; // Reserved flags
3303 expectedData
[2] = 0x00; // Reserved flags
3304 expectedData
[3] = 0x01; // The formats client supports
3305 expectedData
.set(deflatedData
, 4);
3307 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3308 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3309 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3312 it('Carriage return and Line feed mixed', function () {
3313 let testText
= "\rHello\r\n\rworld\n\n!";
3314 let expectedText
= encodeUTF8("\r\nHello\r\n\r\nworld\r\n\r\n!\0");
3316 let deflatedData
= deflateWithSize(expectedText
);
3318 // Build Expected with flags and deflated data
3319 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3320 expectedData
[0] = 0x10; // The client capabilities
3321 expectedData
[1] = 0x00; // Reserved flags
3322 expectedData
[2] = 0x00; // Reserved flags
3323 expectedData
[3] = 0x01; // The formats client supports
3324 expectedData
.set(deflatedData
, 4);
3326 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3327 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3328 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);