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';
10 import KeyTable
from '../core/input/keysym.js';
12 import FakeWebSocket
from './fake.websocket.js';
14 function push8(arr
, num
) {
19 function push16(arr
, num
) {
21 arr
.push((num
>> 8) & 0xFF,
25 function push32(arr
, num
) {
27 arr
.push((num
>> 24) & 0xFF,
33 function pushString(arr
, string
) {
34 let utf8
= unescape(encodeURIComponent(string
));
35 for (let i
= 0; i
< utf8
.length
; i
++) {
36 arr
.push(utf8
.charCodeAt(i
));
40 function deflateWithSize(data
) {
41 // Adds the size of the string in front before deflating
44 unCompData
.push((data
.length
>> 24) & 0xFF,
45 (data
.length
>> 16) & 0xFF,
46 (data
.length
>> 8) & 0xFF,
47 (data
.length
& 0xFF));
49 for (let i
= 0; i
< data
.length
; i
++) {
50 unCompData
.push(data
.charCodeAt(i
));
53 let strm
= new ZStream();
54 let chunkSize
= 1024 * 10 * 10;
55 strm
.output
= new Uint8Array(chunkSize
);
58 /* eslint-disable camelcase */
59 strm
.input
= unCompData
;
60 strm
.avail_in
= strm
.input
.length
;
63 strm
.avail_out
= chunkSize
;
64 /* eslint-enable camelcase */
68 return new Uint8Array(strm
.output
.buffer
, 0, strm
.next_out
);
71 describe('Remote Frame Buffer Protocol Client', function () {
75 before(FakeWebSocket
.replace
);
76 after(FakeWebSocket
.restore
);
79 this.clock
= clock
= sinon
.useFakeTimers(Date
.now());
80 // sinon doesn't support this yet
81 raf
= window
.requestAnimationFrame
;
82 window
.requestAnimationFrame
= setTimeout
;
83 // Use a single set of buffers instead of reallocating to
85 const sock
= new Websock();
86 const _sQ
= new Uint8Array(sock
._sQbufferSize
);
87 const rQ
= new Uint8Array(sock
._rQbufferSize
);
89 Websock
.prototype._oldAllocateBuffers
= Websock
.prototype._allocateBuffers
;
90 Websock
.prototype._allocateBuffers = function () {
95 // Avoiding printing the entire Websock buffer on errors
96 Websock
.prototype.toString = function () { return "[object Websock]"; };
100 delete Websock
.prototype.toString
;
101 this.clock
.restore();
102 window
.requestAnimationFrame
= raf
;
108 beforeEach(function () {
109 // Create a container element for all RFB objects to attach to
110 container
= document
.createElement('div');
111 container
.style
.width
= "100%";
112 container
.style
.height
= "100%";
113 document
.body
.appendChild(container
);
115 // And track all created RFB objects
118 afterEach(function () {
119 // Make sure every created RFB object is properly cleaned up
120 // or they might affect subsequent tests
121 rfbs
.forEach(function (rfb
) {
123 expect(rfb
._disconnect
).to
.have
.been
.called
;
127 document
.body
.removeChild(container
);
131 function makeRFB(url
, options
) {
132 url
= url
|| 'wss://host:8675';
133 const rfb
= new RFB(container
, url
, options
);
135 rfb
._sock
._websocket
._open();
136 rfb
._rfbConnectionState
= 'connected';
137 sinon
.spy(rfb
, "_disconnect");
142 describe('Connecting/Disconnecting', function () {
143 describe('#RFB', function () {
144 it('should set the current state to "connecting"', function () {
145 const client
= new RFB(document
.createElement('div'), 'wss://host:8675');
146 client
._rfbConnectionState
= '';
148 expect(client
._rfbConnectionState
).to
.equal('connecting');
151 it('should actually connect to the websocket', function () {
152 const client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
153 sinon
.spy(client
._sock
, 'open');
155 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
156 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
160 describe('#disconnect', function () {
162 beforeEach(function () {
166 it('should go to state "disconnecting" before "disconnected"', function () {
167 sinon
.spy(client
, '_updateConnectionState');
169 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
170 expect(client
._updateConnectionState
.getCall(0).args
[0])
171 .to
.equal('disconnecting');
172 expect(client
._updateConnectionState
.getCall(1).args
[0])
173 .to
.equal('disconnected');
174 expect(client
._rfbConnectionState
).to
.equal('disconnected');
177 it('should unregister error event handler', function () {
178 sinon
.spy(client
._sock
, 'off');
180 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
183 it('should unregister message event handler', function () {
184 sinon
.spy(client
._sock
, 'off');
186 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
189 it('should unregister open event handler', function () {
190 sinon
.spy(client
._sock
, 'off');
192 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
196 describe('#sendCredentials', function () {
198 beforeEach(function () {
200 client
._rfbConnectionState
= 'connecting';
203 it('should set the rfb credentials properly"', function () {
204 client
.sendCredentials({ password
: 'pass' });
205 expect(client
._rfbCredentials
).to
.deep
.equal({ password
: 'pass' });
208 it('should call initMsg "soon"', function () {
209 client
._initMsg
= sinon
.spy();
210 client
.sendCredentials({ password
: 'pass' });
212 expect(client
._initMsg
).to
.have
.been
.calledOnce
;
217 describe('Public API Basic Behavior', function () {
219 beforeEach(function () {
223 describe('#sendCtrlAlDel', function () {
224 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
225 const expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush
: () => {}};
226 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
227 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
228 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
229 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
230 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
231 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
233 client
.sendCtrlAltDel();
234 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
237 it('should not send the keys if we are not in a normal state', function () {
238 sinon
.spy(client
._sock
, 'flush');
239 client
._rfbConnectionState
= "connecting";
240 client
.sendCtrlAltDel();
241 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
244 it('should not send the keys if we are set as view_only', function () {
245 sinon
.spy(client
._sock
, 'flush');
246 client
._viewOnly
= true;
247 client
.sendCtrlAltDel();
248 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
252 describe('#sendKey', function () {
253 it('should send a single key with the given code and state (down = true)', function () {
254 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
255 RFB
.messages
.keyEvent(expected
, 123, 1);
256 client
.sendKey(123, 'Key123', true);
257 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
260 it('should send both a down and up event if the state is not specified', function () {
261 const expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
262 RFB
.messages
.keyEvent(expected
, 123, 1);
263 RFB
.messages
.keyEvent(expected
, 123, 0);
264 client
.sendKey(123, 'Key123');
265 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
268 it('should not send the key if we are not in a normal state', function () {
269 sinon
.spy(client
._sock
, 'flush');
270 client
._rfbConnectionState
= "connecting";
271 client
.sendKey(123, 'Key123');
272 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
275 it('should not send the key if we are set as view_only', function () {
276 sinon
.spy(client
._sock
, 'flush');
277 client
._viewOnly
= true;
278 client
.sendKey(123, 'Key123');
279 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
282 it('should send QEMU extended events if supported', function () {
283 client
._qemuExtKeyEventSupported
= true;
284 const expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush
: () => {}};
285 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
286 client
.sendKey(0x20, 'Space', true);
287 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
290 it('should not send QEMU extended events if unknown key code', function () {
291 client
._qemuExtKeyEventSupported
= true;
292 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
293 RFB
.messages
.keyEvent(expected
, 123, 1);
294 client
.sendKey(123, 'FooBar', true);
295 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
299 describe('#focus', function () {
300 it('should move focus to canvas object', function () {
301 client
._canvas
.focus
= sinon
.spy();
303 expect(client
._canvas
.focus
).to
.have
.been
.calledOnce
;
307 describe('#blur', function () {
308 it('should remove focus from canvas object', function () {
309 client
._canvas
.blur
= sinon
.spy();
311 expect(client
._canvas
.blur
).to
.have
.been
.calledOnce
;
315 describe('#clipboardPasteFrom', function () {
316 describe('Clipboard update handling', function () {
317 beforeEach(function () {
318 sinon
.spy(RFB
.messages
, 'clientCutText');
319 sinon
.spy(RFB
.messages
, 'extendedClipboardNotify');
322 afterEach(function () {
323 RFB
.messages
.clientCutText
.restore();
324 RFB
.messages
.extendedClipboardNotify
.restore();
327 it('should send the given text in an clipboard update', function () {
328 client
.clipboardPasteFrom('abc');
330 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
331 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(client
._sock
,
332 new Uint8Array([97, 98, 99]));
335 it('should send an notify if extended clipboard is supported by server', function () {
336 // Send our capabilities
337 let data
= [3, 0, 0, 0];
338 const flags
= [0x1F, 0x00, 0x00, 0x01];
339 let fileSizes
= [0x00, 0x00, 0x00, 0x1E];
341 push32(data
, toUnsigned32bit(-8));
342 data
= data
.concat(flags
);
343 data
= data
.concat(fileSizes
);
344 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
346 client
.clipboardPasteFrom('extended test');
347 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
351 it('should flush multiple times for large clipboards', function () {
352 sinon
.spy(client
._sock
, 'flush');
354 for (let i
= 0; i
< client
._sock
._sQbufferSize
+ 100; i
++) {
357 client
.clipboardPasteFrom(longText
);
358 expect(client
._sock
.flush
).to
.have
.been
.calledTwice
;
361 it('should not send the text if we are not in a normal state', function () {
362 sinon
.spy(client
._sock
, 'flush');
363 client
._rfbConnectionState
= "connecting";
364 client
.clipboardPasteFrom('abc');
365 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
369 describe("XVP operations", function () {
370 beforeEach(function () {
371 client
._rfbXvpVer
= 1;
374 it('should send the shutdown signal on #machineShutdown', function () {
375 client
.machineShutdown();
376 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
379 it('should send the reboot signal on #machineReboot', function () {
380 client
.machineReboot();
381 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
384 it('should send the reset signal on #machineReset', function () {
385 client
.machineReset();
386 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
389 it('should not send XVP operations with higher versions than we support', function () {
390 sinon
.spy(client
._sock
, 'flush');
392 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
397 describe('Clipping', function () {
399 beforeEach(function () {
401 container
.style
.width
= '70px';
402 container
.style
.height
= '80px';
403 client
.clipViewport
= true;
406 it('should update display clip state when changing the property', function () {
407 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
409 client
.clipViewport
= false;
410 expect(spy
.set).to
.have
.been
.calledOnce
;
411 expect(spy
.set).to
.have
.been
.calledWith(false);
412 spy
.set.resetHistory();
414 client
.clipViewport
= true;
415 expect(spy
.set).to
.have
.been
.calledOnce
;
416 expect(spy
.set).to
.have
.been
.calledWith(true);
419 it('should update the viewport when the container size changes', function () {
420 sinon
.spy(client
._display
, "viewportChangeSize");
422 container
.style
.width
= '40px';
423 container
.style
.height
= '50px';
424 const event
= new UIEvent('resize');
425 window
.dispatchEvent(event
);
428 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledOnce
;
429 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(40, 50);
432 it('should update the viewport when the remote session resizes', function () {
433 // Simple ExtendedDesktopSize FBU message
434 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
435 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
436 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
437 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
438 0x00, 0x00, 0x00, 0x00 ];
440 sinon
.spy(client
._display
, "viewportChangeSize");
442 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
444 // FIXME: Display implicitly calls viewportChangeSize() when
445 // resizing the framebuffer, hence calledTwice.
446 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledTwice
;
447 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(70, 80);
450 it('should not update the viewport if not clipping', function () {
451 client
.clipViewport
= false;
452 sinon
.spy(client
._display
, "viewportChangeSize");
454 container
.style
.width
= '40px';
455 container
.style
.height
= '50px';
456 const event
= new UIEvent('resize');
457 window
.dispatchEvent(event
);
460 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
463 it('should not update the viewport if scaling', function () {
464 client
.scaleViewport
= true;
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 describe('Dragging', function () {
477 beforeEach(function () {
478 client
.dragViewport
= true;
479 sinon
.spy(RFB
.messages
, "pointerEvent");
482 afterEach(function () {
483 RFB
.messages
.pointerEvent
.restore();
486 it('should not send button messages when initiating viewport dragging', function () {
487 client
._handleMouseButton(13, 9, 0x001);
488 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
491 it('should send button messages when release without movement', function () {
493 client
._handleMouseButton(13, 9, 0x001);
494 client
._handleMouseButton(13, 9, 0x000);
495 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
497 RFB
.messages
.pointerEvent
.resetHistory();
500 client
._handleMouseButton(13, 9, 0x001);
501 client
._handleMouseMove(15, 14);
502 client
._handleMouseButton(15, 14, 0x000);
503 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
506 it('should not send button messages when in view only', function () {
507 client
._viewOnly
= true;
508 client
._handleMouseButton(13, 9, 0x001);
509 client
._handleMouseButton(13, 9, 0x000);
510 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
513 it('should send button message directly when drag is disabled', function () {
514 client
.dragViewport
= false;
515 client
._handleMouseButton(13, 9, 0x001);
516 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
519 it('should be initiate viewport dragging on sufficient movement', function () {
520 sinon
.spy(client
._display
, "viewportChangePos");
522 // Too small movement
524 client
._handleMouseButton(13, 9, 0x001);
525 client
._handleMouseMove(18, 9);
527 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
528 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
530 // Sufficient movement
532 client
._handleMouseMove(43, 9);
534 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
535 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
536 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(-30, 0);
538 client
._display
.viewportChangePos
.resetHistory();
540 // Now a small movement should move right away
542 client
._handleMouseMove(43, 14);
544 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
545 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
546 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(0, -5);
549 it('should not send button messages when dragging ends', function () {
550 // First the movement
552 client
._handleMouseButton(13, 9, 0x001);
553 client
._handleMouseMove(43, 9);
554 client
._handleMouseButton(43, 9, 0x000);
556 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
559 it('should terminate viewport dragging on a button up event', function () {
560 // First the dragging movement
562 client
._handleMouseButton(13, 9, 0x001);
563 client
._handleMouseMove(43, 9);
564 client
._handleMouseButton(43, 9, 0x000);
566 // Another movement now should not move the viewport
568 sinon
.spy(client
._display
, "viewportChangePos");
570 client
._handleMouseMove(43, 59);
572 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
577 describe('Scaling', function () {
579 beforeEach(function () {
581 container
.style
.width
= '70px';
582 container
.style
.height
= '80px';
583 client
.scaleViewport
= true;
586 it('should update display scale factor when changing the property', function () {
587 const spy
= sinon
.spy(client
._display
, "scale", ["set"]);
588 sinon
.spy(client
._display
, "autoscale");
590 client
.scaleViewport
= false;
591 expect(spy
.set).to
.have
.been
.calledOnce
;
592 expect(spy
.set).to
.have
.been
.calledWith(1.0);
593 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
595 client
.scaleViewport
= true;
596 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
597 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
600 it('should update the clipping setting when changing the property', function () {
601 client
.clipViewport
= true;
603 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
605 client
.scaleViewport
= false;
606 expect(spy
.set).to
.have
.been
.calledOnce
;
607 expect(spy
.set).to
.have
.been
.calledWith(true);
609 spy
.set.resetHistory();
611 client
.scaleViewport
= true;
612 expect(spy
.set).to
.have
.been
.calledOnce
;
613 expect(spy
.set).to
.have
.been
.calledWith(false);
616 it('should update the scaling when the container size changes', function () {
617 sinon
.spy(client
._display
, "autoscale");
619 container
.style
.width
= '40px';
620 container
.style
.height
= '50px';
621 const event
= new UIEvent('resize');
622 window
.dispatchEvent(event
);
625 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
626 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(40, 50);
629 it('should update the scaling when the remote session resizes', function () {
630 // Simple ExtendedDesktopSize FBU message
631 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
632 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
633 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
634 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
635 0x00, 0x00, 0x00, 0x00 ];
637 sinon
.spy(client
._display
, "autoscale");
639 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
641 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
642 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
645 it('should not update the display scale factor if not scaling', function () {
646 client
.scaleViewport
= false;
648 sinon
.spy(client
._display
, "autoscale");
650 container
.style
.width
= '40px';
651 container
.style
.height
= '50px';
652 const event
= new UIEvent('resize');
653 window
.dispatchEvent(event
);
656 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
660 describe('Remote resize', function () {
662 beforeEach(function () {
664 client
._supportsSetDesktopSize
= true;
665 client
.resizeSession
= true;
666 container
.style
.width
= '70px';
667 container
.style
.height
= '80px';
668 sinon
.spy(RFB
.messages
, "setDesktopSize");
671 afterEach(function () {
672 RFB
.messages
.setDesktopSize
.restore();
675 it('should only request a resize when turned on', function () {
676 client
.resizeSession
= false;
677 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
678 client
.resizeSession
= true;
679 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
682 it('should request a resize when initially connecting', function () {
683 // Simple ExtendedDesktopSize FBU message
684 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
685 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
686 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
687 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
688 0x00, 0x00, 0x00, 0x00 ];
690 // First message should trigger a resize
692 client
._supportsSetDesktopSize
= false;
694 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
696 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
697 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 70, 80, 0, 0);
699 RFB
.messages
.setDesktopSize
.resetHistory();
701 // Second message should not trigger a resize
703 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
705 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
708 it('should request a resize when the container resizes', function () {
709 container
.style
.width
= '40px';
710 container
.style
.height
= '50px';
711 const event
= new UIEvent('resize');
712 window
.dispatchEvent(event
);
715 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
716 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
719 it('should not resize until the container size is stable', function () {
720 container
.style
.width
= '20px';
721 container
.style
.height
= '30px';
722 const event1
= new UIEvent('resize');
723 window
.dispatchEvent(event1
);
726 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
728 container
.style
.width
= '40px';
729 container
.style
.height
= '50px';
730 const event2
= new UIEvent('resize');
731 window
.dispatchEvent(event2
);
734 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
738 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
739 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
742 it('should not resize when resize is disabled', function () {
743 client
._resizeSession
= false;
745 container
.style
.width
= '40px';
746 container
.style
.height
= '50px';
747 const event
= new UIEvent('resize');
748 window
.dispatchEvent(event
);
751 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
754 it('should not resize when resize is not supported', function () {
755 client
._supportsSetDesktopSize
= false;
757 container
.style
.width
= '40px';
758 container
.style
.height
= '50px';
759 const event
= new UIEvent('resize');
760 window
.dispatchEvent(event
);
763 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
766 it('should not resize when in view only mode', function () {
767 client
._viewOnly
= true;
769 container
.style
.width
= '40px';
770 container
.style
.height
= '50px';
771 const event
= new UIEvent('resize');
772 window
.dispatchEvent(event
);
775 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
778 it('should not try to override a server resize', function () {
779 // Simple ExtendedDesktopSize FBU message
780 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
781 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
782 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
783 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
784 0x00, 0x00, 0x00, 0x00 ];
786 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
788 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
792 describe('Misc Internals', function () {
793 describe('#_updateConnectionState', function () {
795 beforeEach(function () {
799 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
800 const spy
= sinon
.spy();
801 client
._disconnTimer
= setTimeout(spy
, 50);
802 client
._rfbConnectionState
= 'connecting';
803 client
._updateConnectionState('connected');
805 expect(spy
).to
.not
.have
.been
.called
;
806 expect(client
._disconnTimer
).to
.be
.null;
809 it('should set the rfbConnectionState', function () {
810 client
._rfbConnectionState
= 'connecting';
811 client
._updateConnectionState('connected');
812 expect(client
._rfbConnectionState
).to
.equal('connected');
815 it('should not change the state when we are disconnected', function () {
817 expect(client
._rfbConnectionState
).to
.equal('disconnected');
818 client
._updateConnectionState('connecting');
819 expect(client
._rfbConnectionState
).to
.not
.equal('connecting');
822 it('should ignore state changes to the same state', function () {
823 const connectSpy
= sinon
.spy();
824 client
.addEventListener("connect", connectSpy
);
826 expect(client
._rfbConnectionState
).to
.equal('connected');
827 client
._updateConnectionState('connected');
828 expect(connectSpy
).to
.not
.have
.been
.called
;
832 const disconnectSpy
= sinon
.spy();
833 client
.addEventListener("disconnect", disconnectSpy
);
835 expect(client
._rfbConnectionState
).to
.equal('disconnected');
836 client
._updateConnectionState('disconnected');
837 expect(disconnectSpy
).to
.not
.have
.been
.called
;
840 it('should ignore illegal state changes', function () {
841 const spy
= sinon
.spy();
842 client
.addEventListener("disconnect", spy
);
843 client
._updateConnectionState('disconnected');
844 expect(client
._rfbConnectionState
).to
.not
.equal('disconnected');
845 expect(spy
).to
.not
.have
.been
.called
;
849 describe('#_fail', function () {
851 beforeEach(function () {
855 it('should close the WebSocket connection', function () {
856 sinon
.spy(client
._sock
, 'close');
858 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
861 it('should transition to disconnected', function () {
862 sinon
.spy(client
, '_updateConnectionState');
864 this.clock
.tick(2000);
865 expect(client
._updateConnectionState
).to
.have
.been
.called
;
866 expect(client
._rfbConnectionState
).to
.equal('disconnected');
869 it('should set clean_disconnect variable', function () {
870 client
._rfbCleanDisconnect
= true;
871 client
._rfbConnectionState
= 'connected';
873 expect(client
._rfbCleanDisconnect
).to
.be
.false;
876 it('should result in disconnect event with clean set to false', function () {
877 client
._rfbConnectionState
= 'connected';
878 const spy
= sinon
.spy();
879 client
.addEventListener("disconnect", spy
);
881 this.clock
.tick(2000);
882 expect(spy
).to
.have
.been
.calledOnce
;
883 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
889 describe('Connection States', function () {
890 describe('connecting', function () {
891 it('should open the websocket connection', function () {
892 const client
= new RFB(document
.createElement('div'),
893 'ws://HOST:8675/PATH');
894 sinon
.spy(client
._sock
, 'open');
896 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
900 describe('connected', function () {
902 beforeEach(function () {
906 it('should result in a connect event if state becomes connected', function () {
907 const spy
= sinon
.spy();
908 client
.addEventListener("connect", spy
);
909 client
._rfbConnectionState
= 'connecting';
910 client
._updateConnectionState('connected');
911 expect(spy
).to
.have
.been
.calledOnce
;
914 it('should not result in a connect event if the state is not "connected"', function () {
915 const spy
= sinon
.spy();
916 client
.addEventListener("connect", spy
);
917 client
._sock
._websocket
.open
= () => {}; // explicitly don't call onopen
918 client
._updateConnectionState('connecting');
919 expect(spy
).to
.not
.have
.been
.called
;
923 describe('disconnecting', function () {
925 beforeEach(function () {
929 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
930 sinon
.spy(client
, '_updateConnectionState');
931 client
._sock
._websocket
.close
= () => {}; // explicitly don't call onclose
932 client
._updateConnectionState('disconnecting');
933 this.clock
.tick(3 * 1000);
934 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
935 expect(client
._rfbDisconnectReason
).to
.not
.equal("");
936 expect(client
._rfbConnectionState
).to
.equal("disconnected");
939 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
940 client
._updateConnectionState('disconnecting');
941 this.clock
.tick(3 * 1000 / 2);
942 client
._sock
._websocket
.close();
943 this.clock
.tick(3 * 1000 / 2 + 1);
944 expect(client
._rfbConnectionState
).to
.equal('disconnected');
947 it('should close the WebSocket connection', function () {
948 sinon
.spy(client
._sock
, 'close');
949 client
._updateConnectionState('disconnecting');
950 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
953 it('should not result in a disconnect event', function () {
954 const spy
= sinon
.spy();
955 client
.addEventListener("disconnect", spy
);
956 client
._sock
._websocket
.close
= () => {}; // explicitly don't call onclose
957 client
._updateConnectionState('disconnecting');
958 expect(spy
).to
.not
.have
.been
.called
;
962 describe('disconnected', function () {
964 beforeEach(function () {
965 client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
968 it('should result in a disconnect event if state becomes "disconnected"', function () {
969 const spy
= sinon
.spy();
970 client
.addEventListener("disconnect", spy
);
971 client
._rfbConnectionState
= 'disconnecting';
972 client
._updateConnectionState('disconnected');
973 expect(spy
).to
.have
.been
.calledOnce
;
974 expect(spy
.args
[0][0].detail
.clean
).to
.be
.true;
977 it('should result in a disconnect event without msg when no reason given', function () {
978 const spy
= sinon
.spy();
979 client
.addEventListener("disconnect", spy
);
980 client
._rfbConnectionState
= 'disconnecting';
981 client
._rfbDisconnectReason
= "";
982 client
._updateConnectionState('disconnected');
983 expect(spy
).to
.have
.been
.calledOnce
;
984 expect(spy
.args
[0].length
).to
.equal(1);
989 describe('Protocol Initialization States', function () {
991 beforeEach(function () {
993 client
._rfbConnectionState
= 'connecting';
996 describe('ProtocolVersion', function () {
997 function sendVer(ver
, client
) {
998 const arr
= new Uint8Array(12);
999 for (let i
= 0; i
< ver
.length
; i
++) {
1000 arr
[i
+4] = ver
.charCodeAt(i
);
1002 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
1004 client
._sock
._websocket
._receiveData(arr
);
1007 describe('version parsing', function () {
1008 it('should interpret version 003.003 as version 3.3', function () {
1009 sendVer('003.003', client
);
1010 expect(client
._rfbVersion
).to
.equal(3.3);
1013 it('should interpret version 003.006 as version 3.3', function () {
1014 sendVer('003.006', client
);
1015 expect(client
._rfbVersion
).to
.equal(3.3);
1018 it('should interpret version 003.889 as version 3.3', function () {
1019 sendVer('003.889', client
);
1020 expect(client
._rfbVersion
).to
.equal(3.3);
1023 it('should interpret version 003.007 as version 3.7', function () {
1024 sendVer('003.007', client
);
1025 expect(client
._rfbVersion
).to
.equal(3.7);
1028 it('should interpret version 003.008 as version 3.8', function () {
1029 sendVer('003.008', client
);
1030 expect(client
._rfbVersion
).to
.equal(3.8);
1033 it('should interpret version 004.000 as version 3.8', function () {
1034 sendVer('004.000', client
);
1035 expect(client
._rfbVersion
).to
.equal(3.8);
1038 it('should interpret version 004.001 as version 3.8', function () {
1039 sendVer('004.001', client
);
1040 expect(client
._rfbVersion
).to
.equal(3.8);
1043 it('should interpret version 005.000 as version 3.8', function () {
1044 sendVer('005.000', client
);
1045 expect(client
._rfbVersion
).to
.equal(3.8);
1048 it('should fail on an invalid version', function () {
1049 sinon
.spy(client
, "_fail");
1050 sendVer('002.000', client
);
1051 expect(client
._fail
).to
.have
.been
.calledOnce
;
1055 it('should send back the interpreted version', function () {
1056 sendVer('004.000', client
);
1058 const expectedStr
= 'RFB 003.008\n';
1059 const expected
= [];
1060 for (let i
= 0; i
< expectedStr
.length
; i
++) {
1061 expected
[i
] = expectedStr
.charCodeAt(i
);
1064 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1067 it('should transition to the Security state on successful negotiation', function () {
1068 sendVer('003.008', client
);
1069 expect(client
._rfbInitState
).to
.equal('Security');
1072 describe('Repeater', function () {
1073 beforeEach(function () {
1074 client
= makeRFB('wss://host:8675', { repeaterID
: "12345" });
1075 client
._rfbConnectionState
= 'connecting';
1078 it('should interpret version 000.000 as a repeater', function () {
1079 sendVer('000.000', client
);
1080 expect(client
._rfbVersion
).to
.equal(0);
1082 const sentData
= client
._sock
._websocket
._getSentData();
1083 expect(new Uint8Array(sentData
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
1084 expect(sentData
).to
.have
.length(250);
1087 it('should handle two step repeater negotiation', function () {
1088 sendVer('000.000', client
);
1089 sendVer('003.008', client
);
1090 expect(client
._rfbVersion
).to
.equal(3.8);
1095 describe('Security', function () {
1096 beforeEach(function () {
1097 client
._rfbInitState
= 'Security';
1100 it('should simply receive the auth scheme when for versions < 3.7', function () {
1101 client
._rfbVersion
= 3.6;
1102 const authSchemeRaw
= [1, 2, 3, 4];
1103 const authScheme
= (authSchemeRaw
[0] << 24) + (authSchemeRaw
[1] << 16) +
1104 (authSchemeRaw
[2] << 8) + authSchemeRaw
[3];
1105 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemeRaw
));
1106 expect(client
._rfbAuthScheme
).to
.equal(authScheme
);
1109 it('should prefer no authentication is possible', function () {
1110 client
._rfbVersion
= 3.7;
1111 const authSchemes
= [2, 1, 3];
1112 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1113 expect(client
._rfbAuthScheme
).to
.equal(1);
1114 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
1117 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
1118 client
._rfbVersion
= 3.7;
1119 const authSchemes
= [2, 22, 16];
1120 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1121 expect(client
._rfbAuthScheme
).to
.equal(22);
1122 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
1125 it('should fail if there are no supported schemes for versions >= 3.7', function () {
1126 sinon
.spy(client
, "_fail");
1127 client
._rfbVersion
= 3.7;
1128 const authSchemes
= [1, 32];
1129 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1130 expect(client
._fail
).to
.have
.been
.calledOnce
;
1133 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
1134 client
._rfbVersion
= 3.7;
1135 const failureData
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1136 sinon
.spy(client
, '_fail');
1137 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1139 expect(client
._fail
).to
.have
.been
.calledOnce
;
1140 expect(client
._fail
).to
.have
.been
.calledWith(
1141 'Security negotiation failed on no security types (reason: whoops)');
1144 it('should transition to the Authentication state and continue on successful negotiation', function () {
1145 client
._rfbVersion
= 3.7;
1146 const authSchemes
= [1, 1];
1147 client
._negotiateAuthentication
= sinon
.spy();
1148 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1149 expect(client
._rfbInitState
).to
.equal('Authentication');
1150 expect(client
._negotiateAuthentication
).to
.have
.been
.calledOnce
;
1154 describe('Authentication', function () {
1155 beforeEach(function () {
1156 client
._rfbInitState
= 'Security';
1159 function sendSecurity(type
, cl
) {
1160 cl
._sock
._websocket
._receiveData(new Uint8Array([1, type
]));
1163 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
1164 client
._rfbVersion
= 3.6;
1165 const errMsg
= "Whoopsies";
1166 const data
= [0, 0, 0, 0];
1167 const errLen
= errMsg
.length
;
1168 push32(data
, errLen
);
1169 for (let i
= 0; i
< errLen
; i
++) {
1170 data
.push(errMsg
.charCodeAt(i
));
1173 sinon
.spy(client
, '_fail');
1174 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1175 expect(client
._fail
).to
.have
.been
.calledWith(
1176 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
1179 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
1180 client
._rfbVersion
= 3.8;
1181 sendSecurity(1, client
);
1182 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1185 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
1186 client
._rfbVersion
= 3.7;
1187 sendSecurity(1, client
);
1188 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1191 it('should fail on an unknown auth scheme', function () {
1192 sinon
.spy(client
, "_fail");
1193 client
._rfbVersion
= 3.8;
1194 sendSecurity(57, client
);
1195 expect(client
._fail
).to
.have
.been
.calledOnce
;
1198 describe('VNC Authentication (type 2) Handler', function () {
1199 beforeEach(function () {
1200 client
._rfbInitState
= 'Security';
1201 client
._rfbVersion
= 3.8;
1204 it('should fire the credentialsrequired event if missing a password', function () {
1205 const spy
= sinon
.spy();
1206 client
.addEventListener("credentialsrequired", spy
);
1207 sendSecurity(2, client
);
1209 const challenge
= [];
1210 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1211 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1213 expect(client
._rfbCredentials
).to
.be
.empty
;
1214 expect(spy
).to
.have
.been
.calledOnce
;
1215 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
1218 it('should encrypt the password with DES and then send it back', function () {
1219 client
._rfbCredentials
= { password
: 'passwd' };
1220 sendSecurity(2, client
);
1221 client
._sock
._websocket
._getSentData(); // skip the choice of auth reply
1223 const challenge
= [];
1224 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1225 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1227 const desPass
= RFB
.genDES('passwd', challenge
);
1228 expect(client
._sock
).to
.have
.sent(new Uint8Array(desPass
));
1231 it('should transition to SecurityResult immediately after sending the password', function () {
1232 client
._rfbCredentials
= { password
: 'passwd' };
1233 sendSecurity(2, client
);
1235 const challenge
= [];
1236 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1237 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1239 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1243 describe('XVP Authentication (type 22) Handler', function () {
1244 beforeEach(function () {
1245 client
._rfbInitState
= 'Security';
1246 client
._rfbVersion
= 3.8;
1249 it('should fall through to standard VNC authentication upon completion', function () {
1250 client
._rfbCredentials
= { username
: 'user',
1252 password
: 'password' };
1253 client
._negotiateStdVNCAuth
= sinon
.spy();
1254 sendSecurity(22, client
);
1255 expect(client
._negotiateStdVNCAuth
).to
.have
.been
.calledOnce
;
1258 it('should fire the credentialsrequired event if all credentials are missing', function () {
1259 const spy
= sinon
.spy();
1260 client
.addEventListener("credentialsrequired", spy
);
1261 client
._rfbCredentials
= {};
1262 sendSecurity(22, client
);
1264 expect(client
._rfbCredentials
).to
.be
.empty
;
1265 expect(spy
).to
.have
.been
.calledOnce
;
1266 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1269 it('should fire the credentialsrequired event if some credentials are missing', function () {
1270 const spy
= sinon
.spy();
1271 client
.addEventListener("credentialsrequired", spy
);
1272 client
._rfbCredentials
= { username
: 'user',
1274 sendSecurity(22, client
);
1276 expect(spy
).to
.have
.been
.calledOnce
;
1277 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1280 it('should send user and target separately', function () {
1281 client
._rfbCredentials
= { username
: 'user',
1283 password
: 'password' };
1284 client
._negotiateStdVNCAuth
= sinon
.spy();
1286 sendSecurity(22, client
);
1288 const expected
= [22, 4, 6]; // auth selection, len user, len target
1289 for (let i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
1291 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1295 describe('TightVNC Authentication (type 16) Handler', function () {
1296 beforeEach(function () {
1297 client
._rfbInitState
= 'Security';
1298 client
._rfbVersion
= 3.8;
1299 sendSecurity(16, client
);
1300 client
._sock
._websocket
._getSentData(); // skip the security reply
1303 function sendNumStrPairs(pairs
, client
) {
1305 push32(data
, pairs
.length
);
1307 for (let i
= 0; i
< pairs
.length
; i
++) {
1308 push32(data
, pairs
[i
][0]);
1309 for (let j
= 0; j
< 4; j
++) {
1310 data
.push(pairs
[i
][1].charCodeAt(j
));
1312 for (let j
= 0; j
< 8; j
++) {
1313 data
.push(pairs
[i
][2].charCodeAt(j
));
1317 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1320 it('should skip tunnel negotiation if no tunnels are requested', function () {
1321 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1322 expect(client
._rfbTightVNC
).to
.be
.true;
1325 it('should fail if no supported tunnels are listed', function () {
1326 sinon
.spy(client
, "_fail");
1327 sendNumStrPairs([[123, 'OTHR', 'SOMETHNG']], client
);
1328 expect(client
._fail
).to
.have
.been
.calledOnce
;
1331 it('should choose the notunnel tunnel type', function () {
1332 sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
1333 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1336 it('should choose the notunnel tunnel type for Siemens devices', function () {
1337 sendNumStrPairs([[1, 'SICR', 'SCHANNEL'], [2, 'SICR', 'SCHANLPW']], client
);
1338 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1341 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
1342 sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client
);
1343 client
._sock
._websocket
._getSentData(); // skip the tunnel choice here
1344 sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client
);
1345 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1346 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1349 /*it('should attempt to use VNC auth over no auth when possible', function () {
1350 client._rfbTightVNC = true;
1351 client._negotiateStdVNCAuth = sinon.spy();
1352 sendNumStrPairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
1353 expect(client._sock).to.have.sent([0, 0, 0, 1]);
1354 expect(client._negotiateStdVNCAuth).to.have.been.calledOnce;
1355 expect(client._rfbAuthScheme).to.equal(2);
1356 });*/ // while this would make sense, the original code doesn't actually do this
1358 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
1359 client
._rfbTightVNC
= true;
1360 sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client
);
1361 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1362 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1365 it('should accept VNC authentication and transition to that', function () {
1366 client
._rfbTightVNC
= true;
1367 client
._negotiateStdVNCAuth
= sinon
.spy();
1368 sendNumStrPairs([[2, 'STDV', 'VNCAUTH__']], client
);
1369 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1370 expect(client
._negotiateStdVNCAuth
).to
.have
.been
.calledOnce
;
1371 expect(client
._rfbAuthScheme
).to
.equal(2);
1374 it('should fail if there are no supported auth types', function () {
1375 sinon
.spy(client
, "_fail");
1376 client
._rfbTightVNC
= true;
1377 sendNumStrPairs([[23, 'stdv', 'badval__']], client
);
1378 expect(client
._fail
).to
.have
.been
.calledOnce
;
1383 describe('SecurityResult', function () {
1384 beforeEach(function () {
1385 client
._rfbInitState
= 'SecurityResult';
1388 it('should fall through to ServerInitialisation on a response code of 0', function () {
1389 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1390 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1393 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1394 client
._rfbVersion
= 3.8;
1395 sinon
.spy(client
, '_fail');
1396 const failureData
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1397 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1398 expect(client
._fail
).to
.have
.been
.calledWith(
1399 'Security negotiation failed on security result (reason: whoops)');
1402 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1403 sinon
.spy(client
, '_fail');
1404 client
._rfbVersion
= 3.7;
1405 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 1]));
1406 expect(client
._fail
).to
.have
.been
.calledWith(
1407 'Security handshake failed');
1410 it('should result in securityfailure event when receiving a non zero status', function () {
1411 const spy
= sinon
.spy();
1412 client
.addEventListener("securityfailure", spy
);
1413 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 2]));
1414 expect(spy
).to
.have
.been
.calledOnce
;
1415 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1418 it('should include reason when provided in securityfailure event', function () {
1419 client
._rfbVersion
= 3.8;
1420 const spy
= sinon
.spy();
1421 client
.addEventListener("securityfailure", spy
);
1422 const failureData
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
1423 32, 102, 97, 105, 108, 117, 114, 101];
1424 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1425 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1426 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
1429 it('should not include reason when length is zero in securityfailure event', function () {
1430 client
._rfbVersion
= 3.9;
1431 const spy
= sinon
.spy();
1432 client
.addEventListener("securityfailure", spy
);
1433 const failureData
= [0, 0, 0, 1, 0, 0, 0, 0];
1434 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1435 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1436 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1439 it('should not include reason in securityfailure event for version < 3.8', function () {
1440 client
._rfbVersion
= 3.6;
1441 const spy
= sinon
.spy();
1442 client
.addEventListener("securityfailure", spy
);
1443 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 2]));
1444 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1445 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1449 describe('ClientInitialisation', function () {
1450 it('should transition to the ServerInitialisation state', function () {
1451 const client
= makeRFB();
1452 client
._rfbConnectionState
= 'connecting';
1453 client
._rfbInitState
= 'SecurityResult';
1454 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1455 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1458 it('should send 1 if we are in shared mode', function () {
1459 const client
= makeRFB('wss://host:8675', { shared
: true });
1460 client
._rfbConnectionState
= 'connecting';
1461 client
._rfbInitState
= 'SecurityResult';
1462 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1463 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1466 it('should send 0 if we are not in shared mode', function () {
1467 const client
= makeRFB('wss://host:8675', { shared
: false });
1468 client
._rfbConnectionState
= 'connecting';
1469 client
._rfbInitState
= 'SecurityResult';
1470 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1471 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1475 describe('ServerInitialisation', function () {
1476 beforeEach(function () {
1477 client
._rfbInitState
= 'ServerInitialisation';
1480 function sendServerInit(opts
, client
) {
1481 const fullOpts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, bigEndian
: 0,
1482 trueColor
: 1, redMax
: 255, greenMax
: 255, blueMax
: 255,
1483 redShift
: 16, greenShift
: 8, blueShift
: 0, name
: 'a name' };
1484 for (let opt
in opts
) {
1485 fullOpts
[opt
] = opts
[opt
];
1489 push16(data
, fullOpts
.width
);
1490 push16(data
, fullOpts
.height
);
1492 data
.push(fullOpts
.bpp
);
1493 data
.push(fullOpts
.depth
);
1494 data
.push(fullOpts
.bigEndian
);
1495 data
.push(fullOpts
.trueColor
);
1497 push16(data
, fullOpts
.redMax
);
1498 push16(data
, fullOpts
.greenMax
);
1499 push16(data
, fullOpts
.blueMax
);
1500 push8(data
, fullOpts
.redShift
);
1501 push8(data
, fullOpts
.greenShift
);
1502 push8(data
, fullOpts
.blueShift
);
1509 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1511 const nameData
= [];
1513 pushString(nameData
, fullOpts
.name
);
1514 push32(nameLen
, nameData
.length
);
1516 client
._sock
._websocket
._receiveData(new Uint8Array(nameLen
));
1517 client
._sock
._websocket
._receiveData(new Uint8Array(nameData
));
1520 it('should set the framebuffer width and height', function () {
1521 sendServerInit({ width
: 32, height
: 84 }, client
);
1522 expect(client
._fbWidth
).to
.equal(32);
1523 expect(client
._fbHeight
).to
.equal(84);
1526 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1528 it('should set the framebuffer name and call the callback', function () {
1529 const spy
= sinon
.spy();
1530 client
.addEventListener("desktopname", spy
);
1531 sendServerInit({ name
: 'som€ nam€' }, client
);
1533 expect(client
._fbName
).to
.equal('som€ nam€');
1534 expect(spy
).to
.have
.been
.calledOnce
;
1535 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
1538 it('should handle the extended init message of the tight encoding', function () {
1539 // NB(sross): we don't actually do anything with it, so just test that we can
1540 // read it w/o throwing an error
1541 client
._rfbTightVNC
= true;
1542 sendServerInit({}, client
);
1544 const tightData
= [];
1545 push16(tightData
, 1);
1546 push16(tightData
, 2);
1547 push16(tightData
, 3);
1548 push16(tightData
, 0);
1549 for (let i
= 0; i
< 16 + 32 + 48; i
++) {
1552 client
._sock
._websocket
._receiveData(new Uint8Array(tightData
));
1554 expect(client
._rfbConnectionState
).to
.equal('connected');
1557 it('should resize the display', function () {
1558 sinon
.spy(client
._display
, 'resize');
1559 sendServerInit({ width
: 27, height
: 32 }, client
);
1561 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1562 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1565 it('should grab the keyboard', function () {
1566 sinon
.spy(client
._keyboard
, 'grab');
1567 sendServerInit({}, client
);
1568 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1571 describe('Initial Update Request', function () {
1572 beforeEach(function () {
1573 sinon
.spy(RFB
.messages
, "pixelFormat");
1574 sinon
.spy(RFB
.messages
, "clientEncodings");
1575 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1578 afterEach(function () {
1579 RFB
.messages
.pixelFormat
.restore();
1580 RFB
.messages
.clientEncodings
.restore();
1581 RFB
.messages
.fbUpdateRequest
.restore();
1584 // TODO(directxman12): test the various options in this configuration matrix
1585 it('should reply with the pixel format, client encodings, and initial update request', function () {
1586 sendServerInit({ width
: 27, height
: 32 }, client
);
1588 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1589 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1590 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1591 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1592 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1593 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1594 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1595 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1598 it('should reply with restricted settings for Intel AMT servers', function () {
1599 sendServerInit({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1601 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1602 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1603 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1604 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1605 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1606 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1607 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1608 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1609 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1613 it('should transition to the "connected" state', function () {
1614 sendServerInit({}, client
);
1615 expect(client
._rfbConnectionState
).to
.equal('connected');
1620 describe('Protocol Message Processing After Completing Initialization', function () {
1623 beforeEach(function () {
1625 client
._fbName
= 'some device';
1626 client
._fbWidth
= 640;
1627 client
._fbHeight
= 20;
1630 describe('Framebuffer Update Handling', function () {
1631 function sendFbuMsg(rectInfo
, rectData
, client
, rectCnt
) {
1634 if (!rectCnt
|| rectCnt
> -1) {
1636 data
.push(0); // msg type
1637 data
.push(0); // padding
1638 push16(data
, rectCnt
|| rectData
.length
);
1641 for (let i
= 0; i
< rectData
.length
; i
++) {
1643 push16(data
, rectInfo
[i
].x
);
1644 push16(data
, rectInfo
[i
].y
);
1645 push16(data
, rectInfo
[i
].width
);
1646 push16(data
, rectInfo
[i
].height
);
1647 push32(data
, rectInfo
[i
].encoding
);
1649 data
= data
.concat(rectData
[i
]);
1652 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1655 it('should send an update request if there is sufficient data', function () {
1656 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1657 RFB
.messages
.fbUpdateRequest(expectedMsg
, true, 0, 0, 640, 20);
1659 client
._framebufferUpdate
= () => true;
1660 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1662 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
1665 it('should not send an update request if we need more data', function () {
1666 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1667 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1670 it('should resume receiving an update if we previously did not have enough data', function () {
1671 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1672 RFB
.messages
.fbUpdateRequest(expectedMsg
, true, 0, 0, 640, 20);
1674 // just enough to set FBU.rects
1675 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 3]));
1676 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1678 client
._framebufferUpdate = function () { this._sock
.rQskipBytes(1); return true; }; // we magically have enough data
1679 // 247 should *not* be used as the message type here
1680 client
._sock
._websocket
._receiveData(new Uint8Array([247]));
1681 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
1684 it('should not send a request in continuous updates mode', function () {
1685 client
._enabledContinuousUpdates
= true;
1686 client
._framebufferUpdate
= () => true;
1687 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1689 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1692 it('should fail on an unsupported encoding', function () {
1693 sinon
.spy(client
, "_fail");
1694 const rectInfo
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1695 sendFbuMsg([rectInfo
], [[]], client
);
1696 expect(client
._fail
).to
.have
.been
.calledOnce
;
1699 describe('Message Encoding Handlers', function () {
1700 beforeEach(function () {
1701 // a really small frame
1702 client
._fbWidth
= 4;
1703 client
._fbHeight
= 4;
1704 client
._fbDepth
= 24;
1705 client
._display
.resize(4, 4);
1708 it('should handle the DesktopSize pseduo-encoding', function () {
1709 sinon
.spy(client
._display
, 'resize');
1710 sendFbuMsg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1712 expect(client
._fbWidth
).to
.equal(20);
1713 expect(client
._fbHeight
).to
.equal(50);
1715 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1716 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1719 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1720 beforeEach(function () {
1721 // a really small frame
1722 client
._fbWidth
= 4;
1723 client
._fbHeight
= 4;
1724 client
._display
.resize(4, 4);
1725 sinon
.spy(client
._display
, 'resize');
1728 function makeScreenData(nrOfScreens
) {
1730 push8(data
, nrOfScreens
); // number-of-screens
1731 push8(data
, 0); // padding
1732 push16(data
, 0); // padding
1733 for (let i
=0; i
<nrOfScreens
; i
+= 1) {
1734 push32(data
, 0); // id
1735 push16(data
, 0); // x-position
1736 push16(data
, 0); // y-position
1737 push16(data
, 20); // width
1738 push16(data
, 50); // height
1739 push32(data
, 0); // flags
1744 it('should handle a resize requested by this client', function () {
1745 const reasonForChange
= 1; // requested by this client
1746 const statusCode
= 0; // No error
1748 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1749 width
: 20, height
: 50, encoding
: -308 }],
1750 makeScreenData(1), client
);
1752 expect(client
._fbWidth
).to
.equal(20);
1753 expect(client
._fbHeight
).to
.equal(50);
1755 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1756 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1759 it('should handle a resize requested by another client', function () {
1760 const reasonForChange
= 2; // requested by another client
1761 const statusCode
= 0; // No error
1763 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1764 width
: 20, height
: 50, encoding
: -308 }],
1765 makeScreenData(1), client
);
1767 expect(client
._fbWidth
).to
.equal(20);
1768 expect(client
._fbHeight
).to
.equal(50);
1770 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1771 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1774 it('should be able to recieve requests which contain data for multiple screens', function () {
1775 const reasonForChange
= 2; // requested by another client
1776 const statusCode
= 0; // No error
1778 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1779 width
: 60, height
: 50, encoding
: -308 }],
1780 makeScreenData(3), client
);
1782 expect(client
._fbWidth
).to
.equal(60);
1783 expect(client
._fbHeight
).to
.equal(50);
1785 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1786 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1789 it('should not handle a failed request', function () {
1790 const reasonForChange
= 1; // requested by this client
1791 const statusCode
= 1; // Resize is administratively prohibited
1793 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1794 width
: 20, height
: 50, encoding
: -308 }],
1795 makeScreenData(1), client
);
1797 expect(client
._fbWidth
).to
.equal(4);
1798 expect(client
._fbHeight
).to
.equal(4);
1800 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1804 describe('the Cursor pseudo-encoding handler', function () {
1805 beforeEach(function () {
1806 sinon
.spy(client
._cursor
, 'change');
1809 it('should handle a standard cursor', function () {
1810 const info
= { x
: 5, y
: 7,
1811 width
: 4, height
: 4,
1816 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1817 push32(rect
, 0x11223300);
1819 push32(rect
, 0xa0a0a0a0);
1821 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
1822 push32(expected
, 0x332211ff);
1823 push32(expected
, 0x33221100);
1825 expected
= new Uint8Array(expected
);
1827 sendFbuMsg([info
], [rect
], client
);
1829 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1830 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1833 it('should handle an empty cursor', function () {
1834 const info
= { x
: 0, y
: 0,
1835 width
: 0, height
: 0,
1839 sendFbuMsg([info
], [rect
], client
);
1841 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1842 expect(client
._cursor
.change
).to
.have
.been
.calledWith(new Uint8Array
, 0, 0, 0, 0);
1845 it('should handle a transparent cursor', function () {
1846 const info
= { x
: 5, y
: 7,
1847 width
: 4, height
: 4,
1852 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1853 push32(rect
, 0x11223300);
1855 push32(rect
, 0x00000000);
1857 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1858 push32(expected
, 0x33221100);
1860 expected
= new Uint8Array(expected
);
1862 sendFbuMsg([info
], [rect
], client
);
1864 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1865 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1868 describe('dot for empty cursor', function () {
1869 beforeEach(function () {
1870 client
.showDotCursor
= true;
1871 // Was called when we enabled dot cursor
1872 client
._cursor
.change
.resetHistory();
1875 it('should show a standard cursor', function () {
1876 const info
= { x
: 5, y
: 7,
1877 width
: 4, height
: 4,
1882 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1883 push32(rect
, 0x11223300);
1885 push32(rect
, 0xa0a0a0a0);
1887 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
1888 push32(expected
, 0x332211ff);
1889 push32(expected
, 0x33221100);
1891 expected
= new Uint8Array(expected
);
1893 sendFbuMsg([info
], [rect
], client
);
1895 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1896 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1899 it('should handle an empty cursor', function () {
1900 const info
= { x
: 0, y
: 0,
1901 width
: 0, height
: 0,
1904 const dot
= RFB
.cursors
.dot
;
1906 sendFbuMsg([info
], [rect
], client
);
1908 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1909 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
1916 it('should handle a transparent cursor', function () {
1917 const info
= { x
: 5, y
: 7,
1918 width
: 4, height
: 4,
1921 const dot
= RFB
.cursors
.dot
;
1923 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1924 push32(rect
, 0x11223300);
1926 push32(rect
, 0x00000000);
1928 sendFbuMsg([info
], [rect
], client
);
1930 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1931 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
1940 describe('the VMware Cursor pseudo-encoding handler', function () {
1941 beforeEach(function () {
1942 sinon
.spy(client
._cursor
, 'change');
1944 afterEach(function () {
1945 client
._cursor
.change
.resetHistory();
1948 it('should handle the VMware cursor pseudo-encoding', function () {
1949 let data
= [0x00, 0x00, 0xff, 0,
1950 0x00, 0xff, 0x00, 0,
1951 0x00, 0xff, 0x00, 0,
1952 0x00, 0x00, 0xff, 0];
1958 for (let i
= 0; i
< data
.length
; i
++) {
1959 push8(rect
, data
[i
]);
1962 for (let i
= 0; i
< data
.length
; i
++) {
1963 push8(rect
, data
[i
]);
1966 sendFbuMsg([{ x
: 0, y
: 0, width
: 2, height
: 2,
1967 encoding
: 0x574d5664}],
1969 expect(client
._FBU
.rects
).to
.equal(0);
1972 it('should handle insufficient cursor pixel data', function () {
1974 // Specified 14x23 pixels for the cursor,
1975 // but only send 2x2 pixels worth of data
1978 let data
= [0x00, 0x00, 0xff, 0,
1979 0x00, 0xff, 0x00, 0];
1986 for (let i
= 0; i
< data
.length
; i
++) {
1987 push8(rect
, data
[i
]);
1990 for (let i
= 0; i
< data
.length
; i
++) {
1991 push8(rect
, data
[i
]);
1994 sendFbuMsg([{ x
: 0, y
: 0, width
: w
, height
: h
,
1995 encoding
: 0x574d5664}],
1998 // expect one FBU to remain unhandled
1999 expect(client
._FBU
.rects
).to
.equal(1);
2002 it('should update the cursor when type is classic', function () {
2004 [0xff, 0xff, 0xff, 0xff, //Transparent
2005 0xff, 0xff, 0xff, 0xff, //Transparent
2006 0x00, 0x00, 0x00, 0x00, //Opaque
2007 0xff, 0xff, 0xff, 0xff]; //Inverted
2010 [0x00, 0x00, 0x00, 0x00, //Transparent
2011 0x00, 0x00, 0x00, 0x00, //Transparent
2012 0x11, 0x22, 0x33, 0x44, //Opaque
2013 0xff, 0xff, 0xff, 0x44]; //Inverted
2016 push8(rect
, 0); //cursor_type
2017 push8(rect
, 0); //padding
2024 for (let i
= 0; i
< andMask
.length
; i
++) {
2025 push8(rect
, andMask
[i
]);
2028 for (let i
= 0; i
< xorMask
.length
; i
++) {
2029 push8(rect
, xorMask
[i
]);
2032 let expectedRgba
= [0x00, 0x00, 0x00, 0x00,
2033 0x00, 0x00, 0x00, 0x00,
2034 0x33, 0x22, 0x11, 0xff,
2035 0x00, 0x00, 0x00, 0xff];
2037 sendFbuMsg([{ x
: hotx
, y
: hoty
,
2038 width
: w
, height
: h
,
2039 encoding
: 0x574d5664}],
2042 expect(client
._cursor
.change
)
2043 .to
.have
.been
.calledOnce
;
2044 expect(client
._cursor
.change
)
2045 .to
.have
.been
.calledWith(expectedRgba
,
2050 it('should update the cursor when type is alpha', function () {
2051 let data
= [0xee, 0x55, 0xff, 0x00, // rgba
2052 0x00, 0xff, 0x00, 0xff,
2053 0x00, 0xff, 0x00, 0x22,
2054 0x00, 0xff, 0x00, 0x22,
2055 0x00, 0xff, 0x00, 0x22,
2056 0x00, 0x00, 0xff, 0xee];
2058 push8(rect
, 1); //cursor_type
2059 push8(rect
, 0); //padding
2065 for (let i
= 0; i
< data
.length
; i
++) {
2066 push8(rect
, data
[i
]);
2069 let expectedRgba
= [0xee, 0x55, 0xff, 0x00,
2070 0x00, 0xff, 0x00, 0xff,
2071 0x00, 0xff, 0x00, 0x22,
2072 0x00, 0xff, 0x00, 0x22,
2073 0x00, 0xff, 0x00, 0x22,
2074 0x00, 0x00, 0xff, 0xee];
2076 sendFbuMsg([{ x
: hotx
, y
: hoty
,
2077 width
: w
, height
: h
,
2078 encoding
: 0x574d5664}],
2081 expect(client
._cursor
.change
)
2082 .to
.have
.been
.calledOnce
;
2083 expect(client
._cursor
.change
)
2084 .to
.have
.been
.calledWith(expectedRgba
,
2089 it('should not update cursor when incorrect cursor type given', function () {
2091 push8(rect
, 3); // invalid cursor type
2092 push8(rect
, 0); // padding
2094 client
._cursor
.change
.resetHistory();
2095 sendFbuMsg([{ x
: 0, y
: 0, width
: 2, height
: 2,
2096 encoding
: 0x574d5664}],
2099 expect(client
._cursor
.change
)
2100 .to
.not
.have
.been
.called
;
2104 it('should handle the last_rect pseudo-encoding', function () {
2105 sendFbuMsg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
2106 expect(client
._FBU
.rects
).to
.equal(0);
2109 it('should handle the DesktopName pseudo-encoding', function () {
2112 pushString(data
, "som€ nam€");
2114 const spy
= sinon
.spy();
2115 client
.addEventListener("desktopname", spy
);
2117 sendFbuMsg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -307 }], [data
], client
);
2119 expect(client
._fbName
).to
.equal('som€ nam€');
2120 expect(spy
).to
.have
.been
.calledOnce
;
2121 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
2126 describe('XVP Message Handling', function () {
2127 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
2128 const spy
= sinon
.spy();
2129 client
.addEventListener("capabilities", spy
);
2130 client
._sock
._websocket
._receiveData(new Uint8Array([250, 0, 10, 1]));
2131 expect(client
._rfbXvpVer
).to
.equal(10);
2132 expect(spy
).to
.have
.been
.calledOnce
;
2133 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
2134 expect(client
.capabilities
.power
).to
.be
.true;
2137 it('should fail on unknown XVP message types', function () {
2138 sinon
.spy(client
, "_fail");
2139 client
._sock
._websocket
._receiveData(new Uint8Array([250, 0, 10, 237]));
2140 expect(client
._fail
).to
.have
.been
.calledOnce
;
2144 describe('Normal Clipboard Handling Receive', function () {
2145 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
2146 const expectedStr
= 'cheese!';
2147 const data
= [3, 0, 0, 0];
2148 push32(data
, expectedStr
.length
);
2149 for (let i
= 0; i
< expectedStr
.length
; i
++) { data
.push(expectedStr
.charCodeAt(i
)); }
2150 const spy
= sinon
.spy();
2151 client
.addEventListener("clipboard", spy
);
2153 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2154 expect(spy
).to
.have
.been
.calledOnce
;
2155 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedStr
);
2159 describe('Extended clipboard Handling', function () {
2161 describe('Extended clipboard initialization', function () {
2162 beforeEach(function () {
2163 sinon
.spy(RFB
.messages
, 'extendedClipboardCaps');
2166 afterEach(function () {
2167 RFB
.messages
.extendedClipboardCaps
.restore();
2170 it('should update capabilities when receiving a Caps message', function () {
2171 let data
= [3, 0, 0, 0];
2172 const flags
= [0x1F, 0x00, 0x00, 0x03];
2173 let fileSizes
= [0x00, 0x00, 0x00, 0x1E,
2174 0x00, 0x00, 0x00, 0x3C];
2176 push32(data
, toUnsigned32bit(-12));
2177 data
= data
.concat(flags
);
2178 data
= data
.concat(fileSizes
);
2179 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2181 // Check that we give an response caps when we receive one
2182 expect(RFB
.messages
.extendedClipboardCaps
).to
.have
.been
.calledOnce
;
2184 // FIXME: Can we avoid checking internal variables?
2185 expect(client
._clipboardServerCapabilitiesFormats
[0]).to
.not
.equal(true);
2186 expect(client
._clipboardServerCapabilitiesFormats
[1]).to
.equal(true);
2187 expect(client
._clipboardServerCapabilitiesFormats
[2]).to
.equal(true);
2188 expect(client
._clipboardServerCapabilitiesActions
[(1 << 24)]).to
.equal(true);
2194 describe('Extended Clipboard Handling Receive', function () {
2196 beforeEach(function () {
2197 // Send our capabilities
2198 let data
= [3, 0, 0, 0];
2199 const flags
= [0x1F, 0x00, 0x00, 0x01];
2200 let fileSizes
= [0x00, 0x00, 0x00, 0x1E];
2202 push32(data
, toUnsigned32bit(-8));
2203 data
= data
.concat(flags
);
2204 data
= data
.concat(fileSizes
);
2205 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2208 describe('Handle Provide', function () {
2209 it('should update clipboard with correct Unicode data from a Provide message', function () {
2210 let expectedData
= "Aå漢å—!";
2211 let data
= [3, 0, 0, 0];
2212 const flags
= [0x10, 0x00, 0x00, 0x01];
2214 /* The size 10 (utf8 encoded string size) and the
2215 string "Aå漢å—!" utf8 encoded and deflated. */
2216 let deflatedData
= [120, 94, 99, 96, 96, 224, 114, 60,
2217 188, 244, 217, 158, 69, 79, 215,
2218 78, 87, 4, 0, 35, 207, 6, 66];
2220 // How much data we are sending.
2221 push32(data
, toUnsigned32bit(-(4 + deflatedData
.length
)));
2223 data
= data
.concat(flags
);
2224 data
= data
.concat(deflatedData
);
2226 const spy
= sinon
.spy();
2227 client
.addEventListener("clipboard", spy
);
2229 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2230 expect(spy
).to
.have
.been
.calledOnce
;
2231 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2232 client
.removeEventListener("clipboard", spy
);
2235 it('should update clipboard with correct escape characters from a Provide message ', function () {
2236 let expectedData
= "Oh\nmy!";
2237 let data
= [3, 0, 0, 0];
2238 const flags
= [0x10, 0x00, 0x00, 0x01];
2240 let text
= encodeUTF8("Oh\r\nmy!\0");
2242 let deflatedText
= deflateWithSize(text
);
2244 // How much data we are sending.
2245 push32(data
, toUnsigned32bit(-(4 + deflatedText
.length
)));
2247 data
= data
.concat(flags
);
2249 let sendData
= new Uint8Array(data
.length
+ deflatedText
.length
);
2251 sendData
.set(deflatedText
, data
.length
);
2253 const spy
= sinon
.spy();
2254 client
.addEventListener("clipboard", spy
);
2256 client
._sock
._websocket
._receiveData(sendData
);
2257 expect(spy
).to
.have
.been
.calledOnce
;
2258 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2259 client
.removeEventListener("clipboard", spy
);
2262 it('should be able to handle large Provide messages', function () {
2263 let expectedData
= "hello".repeat(100000);
2264 let data
= [3, 0, 0, 0];
2265 const flags
= [0x10, 0x00, 0x00, 0x01];
2267 let text
= encodeUTF8(expectedData
+ "\0");
2269 let deflatedText
= deflateWithSize(text
);
2271 // How much data we are sending.
2272 push32(data
, toUnsigned32bit(-(4 + deflatedText
.length
)));
2274 data
= data
.concat(flags
);
2276 let sendData
= new Uint8Array(data
.length
+ deflatedText
.length
);
2278 sendData
.set(deflatedText
, data
.length
);
2280 const spy
= sinon
.spy();
2281 client
.addEventListener("clipboard", spy
);
2283 client
._sock
._websocket
._receiveData(sendData
);
2284 expect(spy
).to
.have
.been
.calledOnce
;
2285 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2286 client
.removeEventListener("clipboard", spy
);
2291 describe('Handle Notify', function () {
2292 beforeEach(function () {
2293 sinon
.spy(RFB
.messages
, 'extendedClipboardRequest');
2296 afterEach(function () {
2297 RFB
.messages
.extendedClipboardRequest
.restore();
2300 it('should make a request with supported formats when receiving a notify message', function () {
2301 let data
= [3, 0, 0, 0];
2302 const flags
= [0x08, 0x00, 0x00, 0x07];
2303 push32(data
, toUnsigned32bit(-4));
2304 data
= data
.concat(flags
);
2305 let expectedData
= [0x01];
2307 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2309 expect(RFB
.messages
.extendedClipboardRequest
).to
.have
.been
.calledOnce
;
2310 expect(RFB
.messages
.extendedClipboardRequest
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2314 describe('Handle Peek', function () {
2315 beforeEach(function () {
2316 sinon
.spy(RFB
.messages
, 'extendedClipboardNotify');
2319 afterEach(function () {
2320 RFB
.messages
.extendedClipboardNotify
.restore();
2323 it('should send an empty Notify when receiving a Peek and no excisting clipboard data', function () {
2324 let data
= [3, 0, 0, 0];
2325 const flags
= [0x04, 0x00, 0x00, 0x00];
2326 push32(data
, toUnsigned32bit(-4));
2327 data
= data
.concat(flags
);
2328 let expectedData
= [];
2330 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2332 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
2333 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2336 it('should send a Notify message with supported formats when receiving a Peek', function () {
2337 let data
= [3, 0, 0, 0];
2338 const flags
= [0x04, 0x00, 0x00, 0x00];
2339 push32(data
, toUnsigned32bit(-4));
2340 data
= data
.concat(flags
);
2341 let expectedData
= [0x01];
2343 // Needed to have clipboard data to read.
2344 // This will trigger a call to Notify, reset history
2345 client
.clipboardPasteFrom("HejHej");
2346 RFB
.messages
.extendedClipboardNotify
.resetHistory();
2348 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2350 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
2351 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2355 describe('Handle Request', function () {
2356 beforeEach(function () {
2357 sinon
.spy(RFB
.messages
, 'extendedClipboardProvide');
2360 afterEach(function () {
2361 RFB
.messages
.extendedClipboardProvide
.restore();
2364 it('should send a Provide message with supported formats when receiving a Request', function () {
2365 let data
= [3, 0, 0, 0];
2366 const flags
= [0x02, 0x00, 0x00, 0x01];
2367 push32(data
, toUnsigned32bit(-4));
2368 data
= data
.concat(flags
);
2369 let expectedData
= [0x01];
2371 client
.clipboardPasteFrom("HejHej");
2372 expect(RFB
.messages
.extendedClipboardProvide
).to
.not
.have
.been
.called
;
2374 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2376 expect(RFB
.messages
.extendedClipboardProvide
).to
.have
.been
.calledOnce
;
2377 expect(RFB
.messages
.extendedClipboardProvide
).to
.have
.been
.calledWith(client
._sock
, expectedData
, ["HejHej"]);
2384 it('should fire the bell callback on Bell', function () {
2385 const spy
= sinon
.spy();
2386 client
.addEventListener("bell", spy
);
2387 client
._sock
._websocket
._receiveData(new Uint8Array([2]));
2388 expect(spy
).to
.have
.been
.calledOnce
;
2391 it('should respond correctly to ServerFence', function () {
2392 const expectedMsg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2393 const incomingMsg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2395 const payload
= "foo\x00ab9";
2397 // ClientFence and ServerFence are identical in structure
2398 RFB
.messages
.clientFence(expectedMsg
, (1<<0) | (1<<1), payload
);
2399 RFB
.messages
.clientFence(incomingMsg
, 0xffffffff, payload
);
2401 client
._sock
._websocket
._receiveData(incomingMsg
._sQ
);
2403 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2405 expectedMsg
._sQlen
= 0;
2406 incomingMsg
._sQlen
= 0;
2408 RFB
.messages
.clientFence(expectedMsg
, (1<<0), payload
);
2409 RFB
.messages
.clientFence(incomingMsg
, (1<<0) | (1<<31), payload
);
2411 client
._sock
._websocket
._receiveData(incomingMsg
._sQ
);
2413 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2416 it('should enable continuous updates on first EndOfContinousUpdates', function () {
2417 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2419 RFB
.messages
.enableContinuousUpdates(expectedMsg
, true, 0, 0, 640, 20);
2421 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2423 client
._sock
._websocket
._receiveData(new Uint8Array([150]));
2425 expect(client
._enabledContinuousUpdates
).to
.be
.true;
2426 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2429 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
2430 client
._enabledContinuousUpdates
= true;
2431 client
._supportsContinuousUpdates
= true;
2433 client
._sock
._websocket
._receiveData(new Uint8Array([150]));
2435 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2438 it('should update continuous updates on resize', function () {
2439 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2440 RFB
.messages
.enableContinuousUpdates(expectedMsg
, true, 0, 0, 90, 700);
2442 client
._resize(450, 160);
2444 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
2446 client
._enabledContinuousUpdates
= true;
2448 client
._resize(90, 700);
2450 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2453 it('should fail on an unknown message type', function () {
2454 sinon
.spy(client
, "_fail");
2455 client
._sock
._websocket
._receiveData(new Uint8Array([87]));
2456 expect(client
._fail
).to
.have
.been
.calledOnce
;
2460 describe('Asynchronous Events', function () {
2466 beforeEach(function () {
2468 client
._display
.resize(100, 100);
2470 // We need to disable this as focusing the canvas will
2471 // cause the browser to scoll to it, messing up our
2472 // client coordinate calculations
2473 client
.focusOnClick
= false;
2475 pointerEvent
= sinon
.spy(RFB
.messages
, 'pointerEvent');
2476 keyEvent
= sinon
.spy(RFB
.messages
, 'keyEvent');
2477 qemuKeyEvent
= sinon
.spy(RFB
.messages
, 'QEMUExtendedKeyEvent');
2480 afterEach(function () {
2481 pointerEvent
.restore();
2483 qemuKeyEvent
.restore();
2486 function elementToClient(x
, y
) {
2487 let res
= { x
: 0, y
: 0 };
2489 let bounds
= client
._canvas
.getBoundingClientRect();
2492 * If the canvas is on a fractional position we will calculate
2493 * a fractional mouse position. But that gets truncated when we
2494 * send the event, AND the same thing happens in RFB when it
2495 * generates the PointerEvent message. To compensate for that
2496 * fact we round the value upwards here.
2498 res
.x
= Math
.ceil(bounds
.left
+ x
);
2499 res
.y
= Math
.ceil(bounds
.top
+ y
);
2504 describe('Mouse Events', function () {
2505 function sendMouseMoveEvent(x
, y
) {
2506 let pos
= elementToClient(x
, y
);
2509 ev
= new MouseEvent('mousemove',
2510 { 'screenX': pos
.x
+ window
.screenX
,
2511 'screenY': pos
.y
+ window
.screenY
,
2513 'clientY': pos
.y
});
2514 client
._canvas
.dispatchEvent(ev
);
2517 function sendMouseButtonEvent(x
, y
, down
, button
) {
2518 let pos
= elementToClient(x
, y
);
2521 ev
= new MouseEvent(down
? 'mousedown' : 'mouseup',
2522 { 'screenX': pos
.x
+ window
.screenX
,
2523 'screenY': pos
.y
+ window
.screenY
,
2527 'buttons': 1 << button
});
2528 client
._canvas
.dispatchEvent(ev
);
2531 it('should not send button messages in view-only mode', function () {
2532 client
._viewOnly
= true;
2533 sendMouseButtonEvent(10, 10, true, 0);
2535 expect(pointerEvent
).to
.not
.have
.been
.called
;
2538 it('should not send movement messages in view-only mode', function () {
2539 client
._viewOnly
= true;
2540 sendMouseMoveEvent(10, 10);
2542 expect(pointerEvent
).to
.not
.have
.been
.called
;
2545 it('should handle left mouse button', function () {
2546 sendMouseButtonEvent(10, 10, true, 0);
2548 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2550 pointerEvent
.resetHistory();
2552 sendMouseButtonEvent(10, 10, false, 0);
2554 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2558 it('should handle middle mouse button', function () {
2559 sendMouseButtonEvent(10, 10, true, 1);
2561 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2563 pointerEvent
.resetHistory();
2565 sendMouseButtonEvent(10, 10, false, 1);
2567 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2571 it('should handle right mouse button', function () {
2572 sendMouseButtonEvent(10, 10, true, 2);
2574 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2576 pointerEvent
.resetHistory();
2578 sendMouseButtonEvent(10, 10, false, 2);
2580 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2584 it('should handle multiple mouse buttons', function () {
2585 sendMouseButtonEvent(10, 10, true, 0);
2586 sendMouseButtonEvent(10, 10, true, 2);
2588 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2589 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2591 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2594 pointerEvent
.resetHistory();
2596 sendMouseButtonEvent(10, 10, false, 0);
2597 sendMouseButtonEvent(10, 10, false, 2);
2599 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2600 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2602 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2606 it('should handle mouse movement', function () {
2607 sendMouseMoveEvent(50, 70);
2608 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2612 it('should handle click and drag', function () {
2613 sendMouseButtonEvent(10, 10, true, 0);
2614 sendMouseMoveEvent(50, 70);
2616 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2617 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2619 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2622 pointerEvent
.resetHistory();
2624 sendMouseButtonEvent(50, 70, false, 0);
2626 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2630 describe('Event Aggregation', function () {
2631 it('should send a single pointer event on mouse movement', function () {
2632 sendMouseMoveEvent(50, 70);
2634 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2638 it('should delay one move if two events are too close', function () {
2639 sendMouseMoveEvent(18, 30);
2640 sendMouseMoveEvent(20, 50);
2642 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2644 pointerEvent
.resetHistory();
2648 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2652 it('should only send first and last move of many close events', function () {
2653 sendMouseMoveEvent(18, 30);
2654 sendMouseMoveEvent(20, 50);
2655 sendMouseMoveEvent(21, 55);
2657 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2659 pointerEvent
.resetHistory();
2663 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2667 // We selected the 17ms since that is ~60 FPS
2668 it('should send move events every 17 ms', function () {
2669 sendMouseMoveEvent(1, 10); // instant send
2672 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2674 pointerEvent
.resetHistory();
2676 sendMouseMoveEvent(2, 20); // delayed
2677 clock
.tick(10); // timeout send
2679 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2681 pointerEvent
.resetHistory();
2683 sendMouseMoveEvent(3, 30); // delayed
2685 sendMouseMoveEvent(4, 40); // delayed
2686 clock
.tick(10); // timeout send
2688 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2690 pointerEvent
.resetHistory();
2692 sendMouseMoveEvent(5, 50); // delayed
2694 expect(pointerEvent
).to
.not
.have
.been
.called
;
2697 it('should send waiting move events before a button press', function () {
2698 sendMouseMoveEvent(13, 9);
2700 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2702 pointerEvent
.resetHistory();
2704 sendMouseMoveEvent(20, 70);
2706 expect(pointerEvent
).to
.not
.have
.been
.called
;
2708 sendMouseButtonEvent(20, 70, true, 0);
2710 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2711 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2713 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2717 it('should send move events with enough time apart normally', function () {
2718 sendMouseMoveEvent(58, 60);
2720 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2722 pointerEvent
.resetHistory();
2726 sendMouseMoveEvent(25, 60);
2728 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2730 pointerEvent
.resetHistory();
2733 it('should not send waiting move events if disconnected', function () {
2734 sendMouseMoveEvent(88, 99);
2736 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2738 pointerEvent
.resetHistory();
2740 sendMouseMoveEvent(66, 77);
2741 client
.disconnect();
2744 expect(pointerEvent
).to
.not
.have
.been
.called
;
2748 it
.skip('should block click events', function () {
2752 it
.skip('should block contextmenu events', function () {
2757 describe('Wheel Events', function () {
2758 function sendWheelEvent(x
, y
, dx
, dy
, mode
=0) {
2759 let pos
= elementToClient(x
, y
);
2762 ev
= new WheelEvent('wheel',
2763 { 'screenX': pos
.x
+ window
.screenX
,
2764 'screenY': pos
.y
+ window
.screenY
,
2769 'deltaMode': mode
});
2770 client
._canvas
.dispatchEvent(ev
);
2773 it('should handle wheel up event', function () {
2774 sendWheelEvent(10, 10, 0, -50);
2776 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2777 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2779 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2783 it('should handle wheel down event', function () {
2784 sendWheelEvent(10, 10, 0, 50);
2786 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2787 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2789 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2793 it('should handle wheel left event', function () {
2794 sendWheelEvent(10, 10, -50, 0);
2796 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2797 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2799 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2803 it('should handle wheel right event', function () {
2804 sendWheelEvent(10, 10, 50, 0);
2806 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2807 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2809 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2813 it('should ignore wheel when in view only', function () {
2814 client
._viewOnly
= true;
2816 sendWheelEvent(10, 10, 50, 0);
2818 expect(pointerEvent
).to
.not
.have
.been
.called
;
2821 it('should accumulate wheel events if small enough', function () {
2822 sendWheelEvent(10, 10, 0, 20);
2823 sendWheelEvent(10, 10, 0, 20);
2825 expect(pointerEvent
).to
.not
.have
.been
.called
;
2827 sendWheelEvent(10, 10, 0, 20);
2829 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2830 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2832 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2836 it('should not accumulate large wheel events', function () {
2837 sendWheelEvent(10, 10, 0, 400);
2839 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2840 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2842 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2846 it('should handle line based wheel event', function () {
2847 sendWheelEvent(10, 10, 0, 3, 1);
2849 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2850 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2852 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2856 it('should handle page based wheel event', function () {
2857 sendWheelEvent(10, 10, 0, 3, 2);
2859 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2860 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2862 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2867 describe('Keyboard Events', function () {
2868 it('should send a key message on a key press', function () {
2869 client
._handleKeyEvent(0x41, 'KeyA', true);
2870 const keyMsg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
2871 RFB
.messages
.keyEvent(keyMsg
, 0x41, 1);
2872 expect(client
._sock
).to
.have
.sent(keyMsg
._sQ
);
2875 it('should not send messages in view-only mode', function () {
2876 client
._viewOnly
= true;
2877 sinon
.spy(client
._sock
, 'flush');
2878 client
._handleKeyEvent('a', 'KeyA', true);
2879 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2883 describe('Gesture event handlers', function () {
2884 function gestureStart(gestureType
, x
, y
,
2885 magnitudeX
= 0, magnitudeY
= 0) {
2886 let pos
= elementToClient(x
, y
);
2887 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2889 detail
.magnitudeX
= magnitudeX
;
2890 detail
.magnitudeY
= magnitudeY
;
2892 let ev
= new CustomEvent('gesturestart', { detail
: detail
});
2893 client
._canvas
.dispatchEvent(ev
);
2896 function gestureMove(gestureType
, x
, y
,
2897 magnitudeX
= 0, magnitudeY
= 0) {
2898 let pos
= elementToClient(x
, y
);
2899 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2901 detail
.magnitudeX
= magnitudeX
;
2902 detail
.magnitudeY
= magnitudeY
;
2904 let ev
= new CustomEvent('gesturemove', { detail
: detail
});
2905 client
._canvas
.dispatchEvent(ev
);
2908 function gestureEnd(gestureType
, x
, y
) {
2909 let pos
= elementToClient(x
, y
);
2910 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2911 let ev
= new CustomEvent('gestureend', { detail
: detail
});
2912 client
._canvas
.dispatchEvent(ev
);
2915 describe('Gesture onetap', function () {
2916 it('should handle onetap events', function () {
2919 gestureStart('onetap', 20, 40);
2920 gestureEnd('onetap', 20, 40);
2922 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2923 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2925 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2927 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2931 it('should keep same position for multiple onetap events', function () {
2934 gestureStart('onetap', 20, 40);
2935 gestureEnd('onetap', 20, 40);
2937 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2938 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2940 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2942 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2945 pointerEvent
.resetHistory();
2947 gestureStart('onetap', 20, 50);
2948 gestureEnd('onetap', 20, 50);
2950 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2951 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2953 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2955 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2958 pointerEvent
.resetHistory();
2960 gestureStart('onetap', 30, 50);
2961 gestureEnd('onetap', 30, 50);
2963 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2964 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2966 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2968 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2972 it('should not keep same position for onetap events when too far apart', function () {
2975 gestureStart('onetap', 20, 40);
2976 gestureEnd('onetap', 20, 40);
2978 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2979 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2981 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2983 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2986 pointerEvent
.resetHistory();
2988 gestureStart('onetap', 80, 95);
2989 gestureEnd('onetap', 80, 95);
2991 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2992 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2994 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2996 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3000 it('should not keep same position for onetap events when enough time inbetween', function () {
3003 gestureStart('onetap', 10, 20);
3004 gestureEnd('onetap', 10, 20);
3006 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3007 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3009 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3011 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3014 pointerEvent
.resetHistory();
3015 this.clock
.tick(1500);
3017 gestureStart('onetap', 15, 20);
3018 gestureEnd('onetap', 15, 20);
3020 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3021 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3023 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3025 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3028 pointerEvent
.resetHistory();
3032 describe('Gesture twotap', function () {
3033 it('should handle gesture twotap events', function () {
3036 gestureStart("twotap", 20, 40);
3038 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3039 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3041 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3043 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3047 it('should keep same position for multiple twotap events', function () {
3050 for (let offset
= 0;offset
< 30;offset
+= 10) {
3051 pointerEvent
.resetHistory();
3053 gestureStart('twotap', 20, 40 + offset
);
3054 gestureEnd('twotap', 20, 40 + offset
);
3056 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3057 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3059 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3061 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3067 describe('Gesture threetap', function () {
3068 it('should handle gesture start for threetap events', function () {
3071 gestureStart("threetap", 20, 40);
3073 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3074 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3076 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3078 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3082 it('should keep same position for multiple threetap events', function () {
3085 for (let offset
= 0;offset
< 30;offset
+= 10) {
3086 pointerEvent
.resetHistory();
3088 gestureStart('threetap', 20, 40 + offset
);
3089 gestureEnd('threetap', 20, 40 + offset
);
3091 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3092 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3094 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3096 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3102 describe('Gesture drag', function () {
3103 it('should handle gesture drag events', function () {
3106 gestureStart('drag', 20, 40);
3108 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3109 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3111 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3114 pointerEvent
.resetHistory();
3116 gestureMove('drag', 30, 50);
3119 expect(pointerEvent
).to
.have
.been
.calledOnce
;
3120 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3123 pointerEvent
.resetHistory();
3125 gestureEnd('drag', 30, 50);
3127 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3128 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3130 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3135 describe('Gesture long press', function () {
3136 it('should handle long press events', function () {
3139 gestureStart('longpress', 20, 40);
3141 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3142 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3144 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3146 pointerEvent
.resetHistory();
3148 gestureMove('longpress', 40, 60);
3151 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3154 pointerEvent
.resetHistory();
3156 gestureEnd('longpress', 40, 60);
3158 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3159 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3161 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3166 describe('Gesture twodrag', function () {
3167 it('should handle gesture twodrag up events', function () {
3168 let bmask
= 0x10; // Button mask for scroll down
3170 gestureStart('twodrag', 20, 40, 0, 0);
3172 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3175 pointerEvent
.resetHistory();
3177 gestureMove('twodrag', 20, 40, 0, -60);
3179 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3180 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3182 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3184 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3188 it('should handle gesture twodrag down events', function () {
3189 let bmask
= 0x8; // Button mask for scroll up
3191 gestureStart('twodrag', 20, 40, 0, 0);
3193 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3196 pointerEvent
.resetHistory();
3198 gestureMove('twodrag', 20, 40, 0, 60);
3200 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3201 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3203 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3205 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3209 it('should handle gesture twodrag right events', function () {
3210 let bmask
= 0x20; // Button mask for scroll right
3212 gestureStart('twodrag', 20, 40, 0, 0);
3214 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3217 pointerEvent
.resetHistory();
3219 gestureMove('twodrag', 20, 40, 60, 0);
3221 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3222 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3224 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3226 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3230 it('should handle gesture twodrag left events', function () {
3231 let bmask
= 0x40; // Button mask for scroll left
3233 gestureStart('twodrag', 20, 40, 0, 0);
3235 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3238 pointerEvent
.resetHistory();
3240 gestureMove('twodrag', 20, 40, -60, 0);
3242 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3243 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3245 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3247 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3251 it('should handle gesture twodrag diag events', function () {
3252 let scrlUp
= 0x8; // Button mask for scroll up
3253 let scrlRight
= 0x20; // Button mask for scroll right
3255 gestureStart('twodrag', 20, 40, 0, 0);
3257 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3260 pointerEvent
.resetHistory();
3262 gestureMove('twodrag', 20, 40, 60, 60);
3264 expect(pointerEvent
).to
.have
.been
.callCount(5);
3265 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3267 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3269 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3271 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3273 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3277 it('should handle multiple small gesture twodrag events', function () {
3278 let bmask
= 0x8; // Button mask for scroll up
3280 gestureStart('twodrag', 20, 40, 0, 0);
3282 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3285 pointerEvent
.resetHistory();
3287 gestureMove('twodrag', 20, 40, 0, 10);
3290 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3293 pointerEvent
.resetHistory();
3295 gestureMove('twodrag', 20, 40, 0, 20);
3298 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3301 pointerEvent
.resetHistory();
3303 gestureMove('twodrag', 20, 40, 0, 60);
3305 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3306 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3308 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3310 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3314 it('should handle large gesture twodrag events', function () {
3315 let bmask
= 0x8; // Button mask for scroll up
3317 gestureStart('twodrag', 30, 50, 0, 0);
3319 expect(pointerEvent
).
3320 to
.have
.been
.calledOnceWith(client
._sock
, 30, 50, 0x0);
3322 pointerEvent
.resetHistory();
3324 gestureMove('twodrag', 30, 50, 0, 200);
3326 expect(pointerEvent
).to
.have
.callCount(7);
3327 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3329 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3331 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3333 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3335 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3337 expect(pointerEvent
.getCall(5)).to
.have
.been
.calledWith(client
._sock
,
3339 expect(pointerEvent
.getCall(6)).to
.have
.been
.calledWith(client
._sock
,
3344 describe('Gesture pinch', function () {
3345 it('should handle gesture pinch in events', function () {
3346 let keysym
= KeyTable
.XK_Control_L
;
3347 let bmask
= 0x10; // Button mask for scroll down
3349 gestureStart('pinch', 20, 40, 90, 90);
3351 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3353 expect(keyEvent
).to
.not
.have
.been
.called
;
3355 pointerEvent
.resetHistory();
3357 gestureMove('pinch', 20, 40, 30, 30);
3359 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3360 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3362 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3364 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3367 expect(keyEvent
).to
.have
.been
.calledTwice
;
3368 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3370 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3373 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3374 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3376 pointerEvent
.resetHistory();
3377 keyEvent
.resetHistory();
3379 gestureEnd('pinch', 20, 40);
3381 expect(pointerEvent
).to
.not
.have
.been
.called
;
3382 expect(keyEvent
).to
.not
.have
.been
.called
;
3385 it('should handle gesture pinch out events', function () {
3386 let keysym
= KeyTable
.XK_Control_L
;
3387 let bmask
= 0x8; // Button mask for scroll up
3389 gestureStart('pinch', 10, 20, 10, 20);
3391 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3393 expect(keyEvent
).to
.not
.have
.been
.called
;
3395 pointerEvent
.resetHistory();
3397 gestureMove('pinch', 10, 20, 70, 80);
3399 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3400 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3402 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3404 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3407 expect(keyEvent
).to
.have
.been
.calledTwice
;
3408 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3410 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3413 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3414 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3416 pointerEvent
.resetHistory();
3417 keyEvent
.resetHistory();
3419 gestureEnd('pinch', 10, 20);
3421 expect(pointerEvent
).to
.not
.have
.been
.called
;
3422 expect(keyEvent
).to
.not
.have
.been
.called
;
3425 it('should handle large gesture pinch', function () {
3426 let keysym
= KeyTable
.XK_Control_L
;
3427 let bmask
= 0x10; // Button mask for scroll down
3429 gestureStart('pinch', 20, 40, 150, 150);
3431 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3433 expect(keyEvent
).to
.not
.have
.been
.called
;
3435 pointerEvent
.resetHistory();
3437 gestureMove('pinch', 20, 40, 30, 30);
3439 expect(pointerEvent
).to
.have
.been
.callCount(5);
3440 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3442 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3444 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3446 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3448 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3451 expect(keyEvent
).to
.have
.been
.calledTwice
;
3452 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3454 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3457 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3458 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3460 pointerEvent
.resetHistory();
3461 keyEvent
.resetHistory();
3463 gestureEnd('pinch', 20, 40);
3465 expect(pointerEvent
).to
.not
.have
.been
.called
;
3466 expect(keyEvent
).to
.not
.have
.been
.called
;
3469 it('should handle multiple small gesture pinch out events', function () {
3470 let keysym
= KeyTable
.XK_Control_L
;
3471 let bmask
= 0x8; // Button mask for scroll down
3473 gestureStart('pinch', 20, 40, 0, 10);
3475 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3477 expect(keyEvent
).to
.not
.have
.been
.called
;
3479 pointerEvent
.resetHistory();
3481 gestureMove('pinch', 20, 40, 0, 30);
3484 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3487 pointerEvent
.resetHistory();
3489 gestureMove('pinch', 20, 40, 0, 60);
3492 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3495 pointerEvent
.resetHistory();
3496 keyEvent
.resetHistory();
3498 gestureMove('pinch', 20, 40, 0, 90);
3500 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3501 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3503 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3505 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3508 expect(keyEvent
).to
.have
.been
.calledTwice
;
3509 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3511 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3514 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3515 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3517 pointerEvent
.resetHistory();
3518 keyEvent
.resetHistory();
3520 gestureEnd('pinch', 20, 40);
3522 expect(keyEvent
).to
.not
.have
.been
.called
;
3525 it('should send correct key control code', function () {
3526 let keysym
= KeyTable
.XK_Control_L
;
3528 let bmask
= 0x10; // Button mask for scroll down
3530 client
._qemuExtKeyEventSupported
= true;
3532 gestureStart('pinch', 20, 40, 90, 90);
3534 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3536 expect(qemuKeyEvent
).to
.not
.have
.been
.called
;
3538 pointerEvent
.resetHistory();
3540 gestureMove('pinch', 20, 40, 30, 30);
3542 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3543 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3545 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3547 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3550 expect(qemuKeyEvent
).to
.have
.been
.calledTwice
;
3551 expect(qemuKeyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3555 expect(qemuKeyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3560 expect(qemuKeyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3561 expect(qemuKeyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3563 pointerEvent
.resetHistory();
3564 qemuKeyEvent
.resetHistory();
3566 gestureEnd('pinch', 20, 40);
3568 expect(pointerEvent
).to
.not
.have
.been
.called
;
3569 expect(qemuKeyEvent
).to
.not
.have
.been
.called
;
3574 describe('WebSocket Events', function () {
3576 it('should do nothing if we receive an empty message and have nothing in the queue', function () {
3577 client
._normalMsg
= sinon
.spy();
3578 client
._sock
._websocket
._receiveData(new Uint8Array([]));
3579 expect(client
._normalMsg
).to
.not
.have
.been
.called
;
3582 it('should handle a message in the connected state as a normal message', function () {
3583 client
._normalMsg
= sinon
.spy();
3584 client
._sock
._websocket
._receiveData(new Uint8Array([1, 2, 3]));
3585 expect(client
._normalMsg
).to
.have
.been
.called
;
3588 it('should handle a message in any non-disconnected/failed state like an init message', function () {
3589 client
._rfbConnectionState
= 'connecting';
3590 client
._rfbInitState
= 'ProtocolVersion';
3591 client
._initMsg
= sinon
.spy();
3592 client
._sock
._websocket
._receiveData(new Uint8Array([1, 2, 3]));
3593 expect(client
._initMsg
).to
.have
.been
.called
;
3596 it('should process all normal messages directly', function () {
3597 const spy
= sinon
.spy();
3598 client
.addEventListener("bell", spy
);
3599 client
._sock
._websocket
._receiveData(new Uint8Array([0x02, 0x02]));
3600 expect(spy
).to
.have
.been
.calledTwice
;
3604 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
3605 client
= new RFB(document
.createElement('div'), 'wss://host:8675');
3607 client
._sock
._websocket
._open();
3608 expect(client
._rfbInitState
).to
.equal('ProtocolVersion');
3611 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
3612 sinon
.spy(client
, "_fail");
3613 client
._rfbConnectionState
= 'connected';
3614 client
._sock
._websocket
._open();
3615 expect(client
._fail
).to
.have
.been
.calledOnce
;
3619 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
3620 const real
= client
._sock
._websocket
.close
;
3621 client
._sock
._websocket
.close
= () => {};
3622 client
.disconnect();
3623 expect(client
._rfbConnectionState
).to
.equal('disconnecting');
3624 client
._sock
._websocket
.close
= real
;
3625 client
._sock
._websocket
.close();
3626 expect(client
._rfbConnectionState
).to
.equal('disconnected');
3629 it('should fail if we get a close event while connecting', function () {
3630 sinon
.spy(client
, "_fail");
3631 client
._rfbConnectionState
= 'connecting';
3632 client
._sock
._websocket
.close();
3633 expect(client
._fail
).to
.have
.been
.calledOnce
;
3636 it('should unregister close event handler', function () {
3637 sinon
.spy(client
._sock
, 'off');
3638 client
.disconnect();
3639 client
._sock
._websocket
.close();
3640 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
3643 // error events do nothing
3647 describe('Quality level setting', function () {
3648 const defaultQuality
= 6;
3652 beforeEach(function () {
3654 sinon
.spy(RFB
.messages
, "clientEncodings");
3657 afterEach(function () {
3658 RFB
.messages
.clientEncodings
.restore();
3661 it(`should equal ${defaultQuality} by default`, function () {
3662 expect(client
._qualityLevel
).to
.equal(defaultQuality
);
3665 it('should ignore non-integers when set', function () {
3666 client
.qualityLevel
= '1';
3667 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3669 RFB
.messages
.clientEncodings
.resetHistory();
3671 client
.qualityLevel
= 1.5;
3672 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3674 RFB
.messages
.clientEncodings
.resetHistory();
3676 client
.qualityLevel
= null;
3677 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3679 RFB
.messages
.clientEncodings
.resetHistory();
3681 client
.qualityLevel
= undefined;
3682 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3684 RFB
.messages
.clientEncodings
.resetHistory();
3686 client
.qualityLevel
= {};
3687 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3690 it('should ignore integers out of range [0, 9]', function () {
3691 client
.qualityLevel
= -1;
3692 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3694 RFB
.messages
.clientEncodings
.resetHistory();
3696 client
.qualityLevel
= 10;
3697 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3700 it('should send clientEncodings with new quality value', function () {
3704 client
.qualityLevel
= newQuality
;
3705 expect(client
.qualityLevel
).to
.equal(newQuality
);
3706 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3707 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3710 it('should not send clientEncodings if quality is the same', function () {
3714 client
.qualityLevel
= newQuality
;
3715 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3716 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3718 RFB
.messages
.clientEncodings
.resetHistory();
3720 client
.qualityLevel
= newQuality
;
3721 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3724 it('should not send clientEncodings if not in connected state', function () {
3727 client
._rfbConnectionState
= '';
3729 client
.qualityLevel
= newQuality
;
3730 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3732 RFB
.messages
.clientEncodings
.resetHistory();
3734 client
._rfbConnectionState
= 'connnecting';
3736 client
.qualityLevel
= newQuality
;
3737 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3739 RFB
.messages
.clientEncodings
.resetHistory();
3741 client
._rfbConnectionState
= 'connected';
3743 client
.qualityLevel
= newQuality
;
3744 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3745 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3749 describe('Compression level setting', function () {
3750 const defaultCompression
= 2;
3754 beforeEach(function () {
3756 sinon
.spy(RFB
.messages
, "clientEncodings");
3759 afterEach(function () {
3760 RFB
.messages
.clientEncodings
.restore();
3763 it(`should equal ${defaultCompression} by default`, function () {
3764 expect(client
._compressionLevel
).to
.equal(defaultCompression
);
3767 it('should ignore non-integers when set', function () {
3768 client
.compressionLevel
= '1';
3769 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3771 RFB
.messages
.clientEncodings
.resetHistory();
3773 client
.compressionLevel
= 1.5;
3774 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3776 RFB
.messages
.clientEncodings
.resetHistory();
3778 client
.compressionLevel
= null;
3779 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3781 RFB
.messages
.clientEncodings
.resetHistory();
3783 client
.compressionLevel
= undefined;
3784 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3786 RFB
.messages
.clientEncodings
.resetHistory();
3788 client
.compressionLevel
= {};
3789 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3792 it('should ignore integers out of range [0, 9]', function () {
3793 client
.compressionLevel
= -1;
3794 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3796 RFB
.messages
.clientEncodings
.resetHistory();
3798 client
.compressionLevel
= 10;
3799 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3802 it('should send clientEncodings with new compression value', function () {
3806 client
.compressionLevel
= newCompression
;
3807 expect(client
.compressionLevel
).to
.equal(newCompression
);
3808 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3809 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3812 it('should not send clientEncodings if compression is the same', function () {
3816 client
.compressionLevel
= newCompression
;
3817 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3818 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3820 RFB
.messages
.clientEncodings
.resetHistory();
3822 client
.compressionLevel
= newCompression
;
3823 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3826 it('should not send clientEncodings if not in connected state', function () {
3829 client
._rfbConnectionState
= '';
3831 client
.compressionLevel
= newCompression
;
3832 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3834 RFB
.messages
.clientEncodings
.resetHistory();
3836 client
._rfbConnectionState
= 'connnecting';
3838 client
.compressionLevel
= newCompression
;
3839 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3841 RFB
.messages
.clientEncodings
.resetHistory();
3843 client
._rfbConnectionState
= 'connected';
3845 client
.compressionLevel
= newCompression
;
3846 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3847 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3852 describe('RFB messages', function () {
3855 before(function () {
3856 FakeWebSocket
.replace();
3857 sock
= new Websock();
3862 FakeWebSocket
.restore();
3865 describe('Extended Clipboard Handling Send', function () {
3866 beforeEach(function () {
3867 sinon
.spy(RFB
.messages
, 'clientCutText');
3870 afterEach(function () {
3871 RFB
.messages
.clientCutText
.restore();
3874 it('should call clientCutText with correct Caps data', function () {
3879 let expectedData
= new Uint8Array([0x1F, 0x00, 0x00, 0x05,
3880 0x00, 0x00, 0x00, 0x02,
3881 0x00, 0x00, 0x10, 0x19]);
3890 RFB
.messages
.extendedClipboardCaps(sock
, actions
, formats
);
3891 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3892 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3895 it('should call clientCutText with correct Request data', function () {
3896 let formats
= new Uint8Array([0x01]);
3897 let expectedData
= new Uint8Array([0x02, 0x00, 0x00, 0x01]);
3899 RFB
.messages
.extendedClipboardRequest(sock
, formats
);
3900 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3901 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3904 it('should call clientCutText with correct Notify data', function () {
3905 let formats
= new Uint8Array([0x01]);
3906 let expectedData
= new Uint8Array([0x08, 0x00, 0x00, 0x01]);
3908 RFB
.messages
.extendedClipboardNotify(sock
, formats
);
3909 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3910 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3913 it('should call clientCutText with correct Provide data', function () {
3914 let testText
= "Test string";
3915 let expectedText
= encodeUTF8(testText
+ "\0");
3917 let deflatedData
= deflateWithSize(expectedText
);
3919 // Build Expected with flags and deflated data
3920 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3921 expectedData
[0] = 0x10; // The client capabilities
3922 expectedData
[1] = 0x00; // Reserved flags
3923 expectedData
[2] = 0x00; // Reserved flags
3924 expectedData
[3] = 0x01; // The formats client supports
3925 expectedData
.set(deflatedData
, 4);
3927 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3928 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3929 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3933 describe('End of line characters', function () {
3934 it('Carriage return', function () {
3936 let testText
= "Hello\rworld\r\r!";
3937 let expectedText
= encodeUTF8("Hello\r\nworld\r\n\r\n!\0");
3939 let deflatedData
= deflateWithSize(expectedText
);
3941 // Build Expected with flags and deflated data
3942 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3943 expectedData
[0] = 0x10; // The client capabilities
3944 expectedData
[1] = 0x00; // Reserved flags
3945 expectedData
[2] = 0x00; // Reserved flags
3946 expectedData
[3] = 0x01; // The formats client supports
3947 expectedData
.set(deflatedData
, 4);
3949 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3950 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3951 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3954 it('Carriage return Line feed', function () {
3956 let testText
= "Hello\r\n\r\nworld\r\n!";
3957 let expectedText
= encodeUTF8(testText
+ "\0");
3959 let deflatedData
= deflateWithSize(expectedText
);
3961 // Build Expected with flags and deflated data
3962 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3963 expectedData
[0] = 0x10; // The client capabilities
3964 expectedData
[1] = 0x00; // Reserved flags
3965 expectedData
[2] = 0x00; // Reserved flags
3966 expectedData
[3] = 0x01; // The formats client supports
3967 expectedData
.set(deflatedData
, 4);
3969 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3970 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3971 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3974 it('Line feed', function () {
3975 let testText
= "Hello\n\n\nworld\n!";
3976 let expectedText
= encodeUTF8("Hello\r\n\r\n\r\nworld\r\n!\0");
3978 let deflatedData
= deflateWithSize(expectedText
);
3980 // Build Expected with flags and deflated data
3981 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3982 expectedData
[0] = 0x10; // The client capabilities
3983 expectedData
[1] = 0x00; // Reserved flags
3984 expectedData
[2] = 0x00; // Reserved flags
3985 expectedData
[3] = 0x01; // The formats client supports
3986 expectedData
.set(deflatedData
, 4);
3988 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3989 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3990 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3993 it('Carriage return and Line feed mixed', function () {
3994 let testText
= "\rHello\r\n\rworld\n\n!";
3995 let expectedText
= encodeUTF8("\r\nHello\r\n\r\nworld\r\n\r\n!\0");
3997 let deflatedData
= deflateWithSize(expectedText
);
3999 // Build Expected with flags and deflated data
4000 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
4001 expectedData
[0] = 0x10; // The client capabilities
4002 expectedData
[1] = 0x00; // Reserved flags
4003 expectedData
[2] = 0x00; // Reserved flags
4004 expectedData
[3] = 0x01; // The formats client supports
4005 expectedData
.set(deflatedData
, 4);
4007 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
4008 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
4009 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);