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 (constructor)', function () {
145 beforeEach(function () {
146 open
= sinon
.spy(Websock
.prototype, 'open');
147 attach
= sinon
.spy(Websock
.prototype, 'attach');
149 afterEach(function () {
154 it('should not connect from constructor', function () {
155 new RFB(document
.createElement('div'), 'wss://host:8675');
156 expect(open
).to
.not
.have
.been
.called
;
157 this.clock
.tick(); // Flush the pending connection
160 it('should actually connect to the websocket', function () {
161 new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
163 expect(open
).to
.have
.been
.calledOnceWithExactly('ws://HOST:8675/PATH', []);
166 it('should report connection problems via event', function () {
168 open
= sinon
.stub(Websock
.prototype, 'open');
169 open
.throws(Error('Failure'));
170 const client
= new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
171 let callback
= sinon
.spy();
172 client
.addEventListener('disconnect', callback
);
174 expect(callback
).to
.have
.been
.calledOnce
;
175 expect(callback
.args
[0][0].detail
.clean
).to
.be
.false;
178 it('should handle WebSocket/RTCDataChannel objects', function () {
179 let sock
= new FakeWebSocket('ws://HOST:8675/PATH', []);
180 new RFB(document
.createElement('div'), sock
);
182 expect(open
).to
.not
.have
.been
.called
;
183 expect(attach
).to
.have
.been
.calledOnceWithExactly(sock
);
186 it('should handle already open WebSocket/RTCDataChannel objects', function () {
187 let sock
= new FakeWebSocket('ws://HOST:8675/PATH', []);
189 const client
= new RFB(document
.createElement('div'), sock
);
190 let callback
= sinon
.spy();
191 client
.addEventListener('disconnect', callback
);
193 expect(open
).to
.not
.have
.been
.called
;
194 expect(attach
).to
.have
.been
.calledOnceWithExactly(sock
);
195 // Check if it is ready for some data
196 sock
._receiveData(new Uint8Array(['R', 'F', 'B', '0', '0', '3', '0', '0', '8']));
197 expect(callback
).to
.not
.have
.been
.called
;
200 it('should refuse closed WebSocket/RTCDataChannel objects', function () {
201 let sock
= new FakeWebSocket('ws://HOST:8675/PATH', []);
202 sock
.readyState
= WebSocket
.CLOSED
;
203 const client
= new RFB(document
.createElement('div'), sock
);
204 let callback
= sinon
.spy();
205 client
.addEventListener('disconnect', callback
);
207 expect(callback
).to
.have
.been
.calledOnce
;
208 expect(callback
.args
[0][0].detail
.clean
).to
.be
.false;
211 it('should report attach problems via event', function () {
213 attach
= sinon
.stub(Websock
.prototype, 'attach');
214 attach
.throws(Error('Failure'));
215 let sock
= new FakeWebSocket('ws://HOST:8675/PATH', []);
216 const client
= new RFB(document
.createElement('div'), sock
);
217 let callback
= sinon
.spy();
218 client
.addEventListener('disconnect', callback
);
220 expect(callback
).to
.have
.been
.calledOnce
;
221 expect(callback
.args
[0][0].detail
.clean
).to
.be
.false;
225 describe('#disconnect', function () {
229 beforeEach(function () {
231 close
= sinon
.stub(Websock
.prototype, "close");
233 afterEach(function () {
237 it('should start closing WebSocket', function () {
238 let callback
= sinon
.spy();
239 client
.addEventListener('disconnect', callback
);
241 expect(close
).to
.have
.been
.calledOnceWithExactly();
242 expect(callback
).to
.not
.have
.been
.called
;
245 it('should send disconnect event', function () {
246 let callback
= sinon
.spy();
247 client
.addEventListener('disconnect', callback
);
249 close
.thisValues
[0]._eventHandlers
.close(new CloseEvent("close", { 'code': 1000, 'reason': "", 'wasClean': true }));
250 expect(callback
).to
.have
.been
.calledOnce
;
251 expect(callback
.args
[0][0].detail
.clean
).to
.be
.true;
254 it('should force disconnect if disconnecting takes too long', function () {
255 let callback
= sinon
.spy();
256 client
.addEventListener('disconnect', callback
);
258 this.clock
.tick(3 * 1000);
259 expect(callback
).to
.have
.been
.calledOnce
;
260 expect(callback
.args
[0][0].detail
.clean
).to
.be
.true;
263 it('should not fail if disconnect completes before timeout', function () {
264 let callback
= sinon
.spy();
265 client
.addEventListener('disconnect', callback
);
267 client
._updateConnectionState('disconnecting');
268 this.clock
.tick(3 * 1000 / 2);
269 close
.thisValues
[0]._eventHandlers
.close(new CloseEvent("close", { 'code': 1000, 'reason': "", 'wasClean': true }));
270 this.clock
.tick(3 * 1000 / 2 + 1);
271 expect(callback
).to
.have
.been
.calledOnce
;
272 expect(callback
.args
[0][0].detail
.clean
).to
.be
.true;
275 it('should unregister error event handler', function () {
276 sinon
.spy(client
._sock
, 'off');
278 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
281 it('should unregister message event handler', function () {
282 sinon
.spy(client
._sock
, 'off');
284 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
287 it('should unregister open event handler', function () {
288 sinon
.spy(client
._sock
, 'off');
290 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
294 describe('#sendCredentials', function () {
296 beforeEach(function () {
298 client
._rfbConnectionState
= 'connecting';
301 it('should set the rfb credentials properly"', function () {
302 client
.sendCredentials({ password
: 'pass' });
303 expect(client
._rfbCredentials
).to
.deep
.equal({ password
: 'pass' });
306 it('should call initMsg "soon"', function () {
307 client
._initMsg
= sinon
.spy();
308 client
.sendCredentials({ password
: 'pass' });
310 expect(client
._initMsg
).to
.have
.been
.calledOnce
;
315 describe('Public API Basic Behavior', function () {
317 beforeEach(function () {
321 describe('#sendCtrlAlDel', function () {
322 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
323 const expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush
: () => {}};
324 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
325 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
326 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
327 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
328 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
329 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
331 client
.sendCtrlAltDel();
332 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
335 it('should not send the keys if we are not in a normal state', function () {
336 sinon
.spy(client
._sock
, 'flush');
337 client
._rfbConnectionState
= "connecting";
338 client
.sendCtrlAltDel();
339 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
342 it('should not send the keys if we are set as view_only', function () {
343 sinon
.spy(client
._sock
, 'flush');
344 client
._viewOnly
= true;
345 client
.sendCtrlAltDel();
346 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
350 describe('#sendKey', function () {
351 it('should send a single key with the given code and state (down = true)', function () {
352 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
353 RFB
.messages
.keyEvent(expected
, 123, 1);
354 client
.sendKey(123, 'Key123', true);
355 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
358 it('should send both a down and up event if the state is not specified', function () {
359 const expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
360 RFB
.messages
.keyEvent(expected
, 123, 1);
361 RFB
.messages
.keyEvent(expected
, 123, 0);
362 client
.sendKey(123, 'Key123');
363 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
366 it('should not send the key if we are not in a normal state', function () {
367 sinon
.spy(client
._sock
, 'flush');
368 client
._rfbConnectionState
= "connecting";
369 client
.sendKey(123, 'Key123');
370 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
373 it('should not send the key if we are set as view_only', function () {
374 sinon
.spy(client
._sock
, 'flush');
375 client
._viewOnly
= true;
376 client
.sendKey(123, 'Key123');
377 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
380 it('should send QEMU extended events if supported', function () {
381 client
._qemuExtKeyEventSupported
= true;
382 const expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush
: () => {}};
383 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
384 client
.sendKey(0x20, 'Space', true);
385 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
388 it('should not send QEMU extended events if unknown key code', function () {
389 client
._qemuExtKeyEventSupported
= true;
390 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
391 RFB
.messages
.keyEvent(expected
, 123, 1);
392 client
.sendKey(123, 'FooBar', true);
393 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
397 describe('#focus', function () {
398 it('should move focus to canvas object', function () {
399 client
._canvas
.focus
= sinon
.spy();
401 expect(client
._canvas
.focus
).to
.have
.been
.calledOnce
;
405 describe('#blur', function () {
406 it('should remove focus from canvas object', function () {
407 client
._canvas
.blur
= sinon
.spy();
409 expect(client
._canvas
.blur
).to
.have
.been
.calledOnce
;
413 describe('#clipboardPasteFrom', function () {
414 describe('Clipboard update handling', function () {
415 beforeEach(function () {
416 sinon
.spy(RFB
.messages
, 'clientCutText');
417 sinon
.spy(RFB
.messages
, 'extendedClipboardNotify');
420 afterEach(function () {
421 RFB
.messages
.clientCutText
.restore();
422 RFB
.messages
.extendedClipboardNotify
.restore();
425 it('should send the given text in an clipboard update', function () {
426 client
.clipboardPasteFrom('abc');
428 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
429 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(client
._sock
,
430 new Uint8Array([97, 98, 99]));
433 it('should send an notify if extended clipboard is supported by server', function () {
434 // Send our capabilities
435 let data
= [3, 0, 0, 0];
436 const flags
= [0x1F, 0x00, 0x00, 0x01];
437 let fileSizes
= [0x00, 0x00, 0x00, 0x1E];
439 push32(data
, toUnsigned32bit(-8));
440 data
= data
.concat(flags
);
441 data
= data
.concat(fileSizes
);
442 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
444 client
.clipboardPasteFrom('extended test');
445 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
449 it('should flush multiple times for large clipboards', function () {
450 sinon
.spy(client
._sock
, 'flush');
452 for (let i
= 0; i
< client
._sock
._sQbufferSize
+ 100; i
++) {
455 client
.clipboardPasteFrom(longText
);
456 expect(client
._sock
.flush
).to
.have
.been
.calledTwice
;
459 it('should not send the text if we are not in a normal state', function () {
460 sinon
.spy(client
._sock
, 'flush');
461 client
._rfbConnectionState
= "connecting";
462 client
.clipboardPasteFrom('abc');
463 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
467 describe("XVP operations", function () {
468 beforeEach(function () {
469 client
._rfbXvpVer
= 1;
472 it('should send the shutdown signal on #machineShutdown', function () {
473 client
.machineShutdown();
474 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
477 it('should send the reboot signal on #machineReboot', function () {
478 client
.machineReboot();
479 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
482 it('should send the reset signal on #machineReset', function () {
483 client
.machineReset();
484 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
487 it('should not send XVP operations with higher versions than we support', function () {
488 sinon
.spy(client
._sock
, 'flush');
490 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
495 describe('Clipping', function () {
497 beforeEach(function () {
499 container
.style
.width
= '70px';
500 container
.style
.height
= '80px';
501 client
.clipViewport
= true;
504 it('should update display clip state when changing the property', function () {
505 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
507 client
.clipViewport
= false;
508 expect(spy
.set).to
.have
.been
.calledOnce
;
509 expect(spy
.set).to
.have
.been
.calledWith(false);
510 spy
.set.resetHistory();
512 client
.clipViewport
= true;
513 expect(spy
.set).to
.have
.been
.calledOnce
;
514 expect(spy
.set).to
.have
.been
.calledWith(true);
517 it('should update the viewport when the container size changes', function () {
518 sinon
.spy(client
._display
, "viewportChangeSize");
520 container
.style
.width
= '40px';
521 container
.style
.height
= '50px';
522 const event
= new UIEvent('resize');
523 window
.dispatchEvent(event
);
526 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledOnce
;
527 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(40, 50);
530 it('should update the viewport when the remote session resizes', function () {
531 // Simple ExtendedDesktopSize FBU message
532 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
533 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
534 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
535 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
536 0x00, 0x00, 0x00, 0x00 ];
538 sinon
.spy(client
._display
, "viewportChangeSize");
540 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
542 // FIXME: Display implicitly calls viewportChangeSize() when
543 // resizing the framebuffer, hence calledTwice.
544 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledTwice
;
545 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(70, 80);
548 it('should not update the viewport if not clipping', function () {
549 client
.clipViewport
= false;
550 sinon
.spy(client
._display
, "viewportChangeSize");
552 container
.style
.width
= '40px';
553 container
.style
.height
= '50px';
554 const event
= new UIEvent('resize');
555 window
.dispatchEvent(event
);
558 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
561 it('should not update the viewport if scaling', function () {
562 client
.scaleViewport
= true;
563 sinon
.spy(client
._display
, "viewportChangeSize");
565 container
.style
.width
= '40px';
566 container
.style
.height
= '50px';
567 const event
= new UIEvent('resize');
568 window
.dispatchEvent(event
);
571 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
574 describe('Dragging', function () {
575 beforeEach(function () {
576 client
.dragViewport
= true;
577 sinon
.spy(RFB
.messages
, "pointerEvent");
580 afterEach(function () {
581 RFB
.messages
.pointerEvent
.restore();
584 it('should not send button messages when initiating viewport dragging', function () {
585 client
._handleMouseButton(13, 9, 0x001);
586 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
589 it('should send button messages when release without movement', function () {
591 client
._handleMouseButton(13, 9, 0x001);
592 client
._handleMouseButton(13, 9, 0x000);
593 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
595 RFB
.messages
.pointerEvent
.resetHistory();
598 client
._handleMouseButton(13, 9, 0x001);
599 client
._handleMouseMove(15, 14);
600 client
._handleMouseButton(15, 14, 0x000);
601 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
604 it('should not send button messages when in view only', function () {
605 client
._viewOnly
= true;
606 client
._handleMouseButton(13, 9, 0x001);
607 client
._handleMouseButton(13, 9, 0x000);
608 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
611 it('should send button message directly when drag is disabled', function () {
612 client
.dragViewport
= false;
613 client
._handleMouseButton(13, 9, 0x001);
614 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
617 it('should be initiate viewport dragging on sufficient movement', function () {
618 sinon
.spy(client
._display
, "viewportChangePos");
620 // Too small movement
622 client
._handleMouseButton(13, 9, 0x001);
623 client
._handleMouseMove(18, 9);
625 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
626 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
628 // Sufficient movement
630 client
._handleMouseMove(43, 9);
632 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
633 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
634 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(-30, 0);
636 client
._display
.viewportChangePos
.resetHistory();
638 // Now a small movement should move right away
640 client
._handleMouseMove(43, 14);
642 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
643 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
644 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(0, -5);
647 it('should not send button messages when dragging ends', function () {
648 // First the movement
650 client
._handleMouseButton(13, 9, 0x001);
651 client
._handleMouseMove(43, 9);
652 client
._handleMouseButton(43, 9, 0x000);
654 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
657 it('should terminate viewport dragging on a button up event', function () {
658 // First the dragging movement
660 client
._handleMouseButton(13, 9, 0x001);
661 client
._handleMouseMove(43, 9);
662 client
._handleMouseButton(43, 9, 0x000);
664 // Another movement now should not move the viewport
666 sinon
.spy(client
._display
, "viewportChangePos");
668 client
._handleMouseMove(43, 59);
670 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
675 describe('Scaling', function () {
677 beforeEach(function () {
679 container
.style
.width
= '70px';
680 container
.style
.height
= '80px';
681 client
.scaleViewport
= true;
684 it('should update display scale factor when changing the property', function () {
685 const spy
= sinon
.spy(client
._display
, "scale", ["set"]);
686 sinon
.spy(client
._display
, "autoscale");
688 client
.scaleViewport
= false;
689 expect(spy
.set).to
.have
.been
.calledOnce
;
690 expect(spy
.set).to
.have
.been
.calledWith(1.0);
691 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
693 client
.scaleViewport
= true;
694 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
695 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
698 it('should update the clipping setting when changing the property', function () {
699 client
.clipViewport
= true;
701 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
703 client
.scaleViewport
= false;
704 expect(spy
.set).to
.have
.been
.calledOnce
;
705 expect(spy
.set).to
.have
.been
.calledWith(true);
707 spy
.set.resetHistory();
709 client
.scaleViewport
= true;
710 expect(spy
.set).to
.have
.been
.calledOnce
;
711 expect(spy
.set).to
.have
.been
.calledWith(false);
714 it('should update the scaling when the container size changes', function () {
715 sinon
.spy(client
._display
, "autoscale");
717 container
.style
.width
= '40px';
718 container
.style
.height
= '50px';
719 const event
= new UIEvent('resize');
720 window
.dispatchEvent(event
);
723 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
724 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(40, 50);
727 it('should update the scaling when the remote session resizes', function () {
728 // Simple ExtendedDesktopSize FBU message
729 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
730 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
731 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
732 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
733 0x00, 0x00, 0x00, 0x00 ];
735 sinon
.spy(client
._display
, "autoscale");
737 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
739 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
740 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
743 it('should not update the display scale factor if not scaling', function () {
744 client
.scaleViewport
= false;
746 sinon
.spy(client
._display
, "autoscale");
748 container
.style
.width
= '40px';
749 container
.style
.height
= '50px';
750 const event
= new UIEvent('resize');
751 window
.dispatchEvent(event
);
754 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
758 describe('Remote resize', function () {
760 beforeEach(function () {
762 client
._supportsSetDesktopSize
= true;
763 client
.resizeSession
= true;
764 container
.style
.width
= '70px';
765 container
.style
.height
= '80px';
766 sinon
.spy(RFB
.messages
, "setDesktopSize");
769 afterEach(function () {
770 RFB
.messages
.setDesktopSize
.restore();
773 it('should only request a resize when turned on', function () {
774 client
.resizeSession
= false;
775 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
776 client
.resizeSession
= true;
777 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
780 it('should request a resize when initially connecting', function () {
781 // Simple ExtendedDesktopSize FBU message
782 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
783 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
784 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
785 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
786 0x00, 0x00, 0x00, 0x00 ];
788 // First message should trigger a resize
790 client
._supportsSetDesktopSize
= false;
792 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
794 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
795 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 70, 80, 0, 0);
797 RFB
.messages
.setDesktopSize
.resetHistory();
799 // Second message should not trigger a resize
801 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
803 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
806 it('should request a resize when the container resizes', function () {
807 container
.style
.width
= '40px';
808 container
.style
.height
= '50px';
809 const event
= new UIEvent('resize');
810 window
.dispatchEvent(event
);
813 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
814 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
817 it('should not resize until the container size is stable', function () {
818 container
.style
.width
= '20px';
819 container
.style
.height
= '30px';
820 const event1
= new UIEvent('resize');
821 window
.dispatchEvent(event1
);
824 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
826 container
.style
.width
= '40px';
827 container
.style
.height
= '50px';
828 const event2
= new UIEvent('resize');
829 window
.dispatchEvent(event2
);
832 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
836 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
837 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
840 it('should not resize when resize is disabled', function () {
841 client
._resizeSession
= false;
843 container
.style
.width
= '40px';
844 container
.style
.height
= '50px';
845 const event
= new UIEvent('resize');
846 window
.dispatchEvent(event
);
849 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
852 it('should not resize when resize is not supported', function () {
853 client
._supportsSetDesktopSize
= false;
855 container
.style
.width
= '40px';
856 container
.style
.height
= '50px';
857 const event
= new UIEvent('resize');
858 window
.dispatchEvent(event
);
861 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
864 it('should not resize when in view only mode', function () {
865 client
._viewOnly
= true;
867 container
.style
.width
= '40px';
868 container
.style
.height
= '50px';
869 const event
= new UIEvent('resize');
870 window
.dispatchEvent(event
);
873 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
876 it('should not try to override a server resize', function () {
877 // Simple ExtendedDesktopSize FBU message
878 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
879 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
880 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
881 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
882 0x00, 0x00, 0x00, 0x00 ];
884 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
886 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
890 describe('Misc Internals', function () {
891 describe('#_fail', function () {
893 beforeEach(function () {
897 it('should close the WebSocket connection', function () {
898 sinon
.spy(client
._sock
, 'close');
900 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
903 it('should transition to disconnected', function () {
904 sinon
.spy(client
, '_updateConnectionState');
906 this.clock
.tick(2000);
907 expect(client
._updateConnectionState
).to
.have
.been
.called
;
908 expect(client
._rfbConnectionState
).to
.equal('disconnected');
911 it('should set clean_disconnect variable', function () {
912 client
._rfbCleanDisconnect
= true;
913 client
._rfbConnectionState
= 'connected';
915 expect(client
._rfbCleanDisconnect
).to
.be
.false;
918 it('should result in disconnect event with clean set to false', function () {
919 client
._rfbConnectionState
= 'connected';
920 const spy
= sinon
.spy();
921 client
.addEventListener("disconnect", spy
);
923 this.clock
.tick(2000);
924 expect(spy
).to
.have
.been
.calledOnce
;
925 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
931 describe('Protocol Initialization States', function () {
933 beforeEach(function () {
935 client
._rfbConnectionState
= 'connecting';
938 describe('ProtocolVersion', function () {
939 function sendVer(ver
, client
) {
940 const arr
= new Uint8Array(12);
941 for (let i
= 0; i
< ver
.length
; i
++) {
942 arr
[i
+4] = ver
.charCodeAt(i
);
944 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
946 client
._sock
._websocket
._receiveData(arr
);
949 describe('version parsing', function () {
950 it('should interpret version 003.003 as version 3.3', function () {
951 sendVer('003.003', client
);
952 expect(client
._rfbVersion
).to
.equal(3.3);
955 it('should interpret version 003.006 as version 3.3', function () {
956 sendVer('003.006', client
);
957 expect(client
._rfbVersion
).to
.equal(3.3);
960 it('should interpret version 003.889 as version 3.3', function () {
961 sendVer('003.889', client
);
962 expect(client
._rfbVersion
).to
.equal(3.3);
965 it('should interpret version 003.007 as version 3.7', function () {
966 sendVer('003.007', client
);
967 expect(client
._rfbVersion
).to
.equal(3.7);
970 it('should interpret version 003.008 as version 3.8', function () {
971 sendVer('003.008', client
);
972 expect(client
._rfbVersion
).to
.equal(3.8);
975 it('should interpret version 004.000 as version 3.8', function () {
976 sendVer('004.000', client
);
977 expect(client
._rfbVersion
).to
.equal(3.8);
980 it('should interpret version 004.001 as version 3.8', function () {
981 sendVer('004.001', client
);
982 expect(client
._rfbVersion
).to
.equal(3.8);
985 it('should interpret version 005.000 as version 3.8', function () {
986 sendVer('005.000', client
);
987 expect(client
._rfbVersion
).to
.equal(3.8);
990 it('should fail on an invalid version', function () {
991 sinon
.spy(client
, "_fail");
992 sendVer('002.000', client
);
993 expect(client
._fail
).to
.have
.been
.calledOnce
;
997 it('should send back the interpreted version', function () {
998 sendVer('004.000', client
);
1000 const expectedStr
= 'RFB 003.008\n';
1001 const expected
= [];
1002 for (let i
= 0; i
< expectedStr
.length
; i
++) {
1003 expected
[i
] = expectedStr
.charCodeAt(i
);
1006 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1009 it('should transition to the Security state on successful negotiation', function () {
1010 sendVer('003.008', client
);
1011 expect(client
._rfbInitState
).to
.equal('Security');
1014 describe('Repeater', function () {
1015 beforeEach(function () {
1016 client
= makeRFB('wss://host:8675', { repeaterID
: "12345" });
1017 client
._rfbConnectionState
= 'connecting';
1020 it('should interpret version 000.000 as a repeater', function () {
1021 sendVer('000.000', client
);
1022 expect(client
._rfbVersion
).to
.equal(0);
1024 const sentData
= client
._sock
._websocket
._getSentData();
1025 expect(new Uint8Array(sentData
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
1026 expect(sentData
).to
.have
.length(250);
1029 it('should handle two step repeater negotiation', function () {
1030 sendVer('000.000', client
);
1031 sendVer('003.008', client
);
1032 expect(client
._rfbVersion
).to
.equal(3.8);
1037 describe('Security', function () {
1038 beforeEach(function () {
1039 client
._rfbInitState
= 'Security';
1042 it('should simply receive the auth scheme when for versions < 3.7', function () {
1043 client
._rfbVersion
= 3.6;
1044 const authSchemeRaw
= [1, 2, 3, 4];
1045 const authScheme
= (authSchemeRaw
[0] << 24) + (authSchemeRaw
[1] << 16) +
1046 (authSchemeRaw
[2] << 8) + authSchemeRaw
[3];
1047 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemeRaw
));
1048 expect(client
._rfbAuthScheme
).to
.equal(authScheme
);
1051 it('should prefer no authentication is possible', function () {
1052 client
._rfbVersion
= 3.7;
1053 const authSchemes
= [2, 1, 3];
1054 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1055 expect(client
._rfbAuthScheme
).to
.equal(1);
1056 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
1059 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
1060 client
._rfbVersion
= 3.7;
1061 const authSchemes
= [2, 22, 16];
1062 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1063 expect(client
._rfbAuthScheme
).to
.equal(22);
1064 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
1067 it('should fail if there are no supported schemes for versions >= 3.7', function () {
1068 sinon
.spy(client
, "_fail");
1069 client
._rfbVersion
= 3.7;
1070 const authSchemes
= [1, 32];
1071 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1072 expect(client
._fail
).to
.have
.been
.calledOnce
;
1075 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
1076 client
._rfbVersion
= 3.7;
1077 const failureData
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1078 sinon
.spy(client
, '_fail');
1079 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1081 expect(client
._fail
).to
.have
.been
.calledOnce
;
1082 expect(client
._fail
).to
.have
.been
.calledWith(
1083 'Security negotiation failed on no security types (reason: whoops)');
1086 it('should transition to the Authentication state and continue on successful negotiation', function () {
1087 client
._rfbVersion
= 3.7;
1088 const authSchemes
= [1, 1];
1089 client
._negotiateAuthentication
= sinon
.spy();
1090 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1091 expect(client
._rfbInitState
).to
.equal('Authentication');
1092 expect(client
._negotiateAuthentication
).to
.have
.been
.calledOnce
;
1096 describe('Authentication', function () {
1097 beforeEach(function () {
1098 client
._rfbInitState
= 'Security';
1101 function sendSecurity(type
, cl
) {
1102 cl
._sock
._websocket
._receiveData(new Uint8Array([1, type
]));
1105 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
1106 client
._rfbVersion
= 3.6;
1107 const errMsg
= "Whoopsies";
1108 const data
= [0, 0, 0, 0];
1109 const errLen
= errMsg
.length
;
1110 push32(data
, errLen
);
1111 for (let i
= 0; i
< errLen
; i
++) {
1112 data
.push(errMsg
.charCodeAt(i
));
1115 sinon
.spy(client
, '_fail');
1116 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1117 expect(client
._fail
).to
.have
.been
.calledWith(
1118 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
1121 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
1122 client
._rfbVersion
= 3.8;
1123 sendSecurity(1, client
);
1124 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1127 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
1128 client
._rfbVersion
= 3.7;
1129 sendSecurity(1, client
);
1130 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1133 it('should fail on an unknown auth scheme', function () {
1134 sinon
.spy(client
, "_fail");
1135 client
._rfbVersion
= 3.8;
1136 sendSecurity(57, client
);
1137 expect(client
._fail
).to
.have
.been
.calledOnce
;
1140 describe('VNC Authentication (type 2) Handler', function () {
1141 beforeEach(function () {
1142 client
._rfbInitState
= 'Security';
1143 client
._rfbVersion
= 3.8;
1146 it('should fire the credentialsrequired event if missing a password', function () {
1147 const spy
= sinon
.spy();
1148 client
.addEventListener("credentialsrequired", spy
);
1149 sendSecurity(2, client
);
1151 const challenge
= [];
1152 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1153 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1155 expect(client
._rfbCredentials
).to
.be
.empty
;
1156 expect(spy
).to
.have
.been
.calledOnce
;
1157 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
1160 it('should encrypt the password with DES and then send it back', function () {
1161 client
._rfbCredentials
= { password
: 'passwd' };
1162 sendSecurity(2, client
);
1163 client
._sock
._websocket
._getSentData(); // skip the choice of auth reply
1165 const challenge
= [];
1166 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1167 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1169 const desPass
= RFB
.genDES('passwd', challenge
);
1170 expect(client
._sock
).to
.have
.sent(new Uint8Array(desPass
));
1173 it('should transition to SecurityResult immediately after sending the password', function () {
1174 client
._rfbCredentials
= { password
: 'passwd' };
1175 sendSecurity(2, client
);
1177 const challenge
= [];
1178 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1179 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1181 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1185 describe('XVP Authentication (type 22) Handler', function () {
1186 beforeEach(function () {
1187 client
._rfbInitState
= 'Security';
1188 client
._rfbVersion
= 3.8;
1191 it('should fall through to standard VNC authentication upon completion', function () {
1192 client
._rfbCredentials
= { username
: 'user',
1194 password
: 'password' };
1195 client
._negotiateStdVNCAuth
= sinon
.spy();
1196 sendSecurity(22, client
);
1197 expect(client
._negotiateStdVNCAuth
).to
.have
.been
.calledOnce
;
1200 it('should fire the credentialsrequired event if all credentials are missing', function () {
1201 const spy
= sinon
.spy();
1202 client
.addEventListener("credentialsrequired", spy
);
1203 client
._rfbCredentials
= {};
1204 sendSecurity(22, client
);
1206 expect(client
._rfbCredentials
).to
.be
.empty
;
1207 expect(spy
).to
.have
.been
.calledOnce
;
1208 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1211 it('should fire the credentialsrequired event if some credentials are missing', function () {
1212 const spy
= sinon
.spy();
1213 client
.addEventListener("credentialsrequired", spy
);
1214 client
._rfbCredentials
= { username
: 'user',
1216 sendSecurity(22, client
);
1218 expect(spy
).to
.have
.been
.calledOnce
;
1219 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1222 it('should send user and target separately', function () {
1223 client
._rfbCredentials
= { username
: 'user',
1225 password
: 'password' };
1226 client
._negotiateStdVNCAuth
= sinon
.spy();
1228 sendSecurity(22, client
);
1230 const expected
= [22, 4, 6]; // auth selection, len user, len target
1231 for (let i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
1233 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1237 describe('TightVNC Authentication (type 16) Handler', function () {
1238 beforeEach(function () {
1239 client
._rfbInitState
= 'Security';
1240 client
._rfbVersion
= 3.8;
1241 sendSecurity(16, client
);
1242 client
._sock
._websocket
._getSentData(); // skip the security reply
1245 function sendNumStrPairs(pairs
, client
) {
1247 push32(data
, pairs
.length
);
1249 for (let i
= 0; i
< pairs
.length
; i
++) {
1250 push32(data
, pairs
[i
][0]);
1251 for (let j
= 0; j
< 4; j
++) {
1252 data
.push(pairs
[i
][1].charCodeAt(j
));
1254 for (let j
= 0; j
< 8; j
++) {
1255 data
.push(pairs
[i
][2].charCodeAt(j
));
1259 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1262 it('should skip tunnel negotiation if no tunnels are requested', function () {
1263 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1264 expect(client
._rfbTightVNC
).to
.be
.true;
1267 it('should fail if no supported tunnels are listed', function () {
1268 sinon
.spy(client
, "_fail");
1269 sendNumStrPairs([[123, 'OTHR', 'SOMETHNG']], client
);
1270 expect(client
._fail
).to
.have
.been
.calledOnce
;
1273 it('should choose the notunnel tunnel type', function () {
1274 sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
1275 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1278 it('should choose the notunnel tunnel type for Siemens devices', function () {
1279 sendNumStrPairs([[1, 'SICR', 'SCHANNEL'], [2, 'SICR', 'SCHANLPW']], client
);
1280 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1283 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
1284 sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client
);
1285 client
._sock
._websocket
._getSentData(); // skip the tunnel choice here
1286 sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client
);
1287 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1288 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1291 /*it('should attempt to use VNC auth over no auth when possible', function () {
1292 client._rfbTightVNC = true;
1293 client._negotiateStdVNCAuth = sinon.spy();
1294 sendNumStrPairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
1295 expect(client._sock).to.have.sent([0, 0, 0, 1]);
1296 expect(client._negotiateStdVNCAuth).to.have.been.calledOnce;
1297 expect(client._rfbAuthScheme).to.equal(2);
1298 });*/ // while this would make sense, the original code doesn't actually do this
1300 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
1301 client
._rfbTightVNC
= true;
1302 sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client
);
1303 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1304 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1307 it('should accept VNC authentication and transition to that', function () {
1308 client
._rfbTightVNC
= true;
1309 client
._negotiateStdVNCAuth
= sinon
.spy();
1310 sendNumStrPairs([[2, 'STDV', 'VNCAUTH__']], client
);
1311 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1312 expect(client
._negotiateStdVNCAuth
).to
.have
.been
.calledOnce
;
1313 expect(client
._rfbAuthScheme
).to
.equal(2);
1316 it('should fail if there are no supported auth types', function () {
1317 sinon
.spy(client
, "_fail");
1318 client
._rfbTightVNC
= true;
1319 sendNumStrPairs([[23, 'stdv', 'badval__']], client
);
1320 expect(client
._fail
).to
.have
.been
.calledOnce
;
1324 describe('VeNCrypt Authentication (type 19) Handler', function () {
1325 beforeEach(function () {
1326 client
._rfbInitState
= 'Security';
1327 client
._rfbVersion
= 3.8;
1328 sendSecurity(19, client
);
1329 expect(client
._sock
).to
.have
.sent(new Uint8Array([19]));
1332 it('should fail with non-0.2 versions', function () {
1333 sinon
.spy(client
, "_fail");
1334 client
._sock
._websocket
._receiveData(new Uint8Array([0, 1]));
1335 expect(client
._fail
).to
.have
.been
.calledOnce
;
1338 it('should fail if the Plain authentication is not present', function () {
1340 client
._sock
._websocket
._receiveData(new Uint8Array([0, 2]));
1341 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 2]));
1343 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1344 // Subtype list, only list subtype 1.
1345 sinon
.spy(client
, "_fail");
1346 client
._sock
._websocket
._receiveData(new Uint8Array([1, 0, 0, 0, 1]));
1347 expect(client
._fail
).to
.have
.been
.calledOnce
;
1350 it('should support Plain authentication', function () {
1351 client
._rfbCredentials
= { username
: 'username', password
: 'password' };
1353 client
._sock
._websocket
._receiveData(new Uint8Array([0, 2]));
1354 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 2]));
1356 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1358 client
._sock
._websocket
._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
1360 const expectedResponse
= [];
1361 push32(expectedResponse
, 256); // Chosen subtype.
1362 push32(expectedResponse
, client
._rfbCredentials
.username
.length
);
1363 push32(expectedResponse
, client
._rfbCredentials
.password
.length
);
1364 pushString(expectedResponse
, client
._rfbCredentials
.username
);
1365 pushString(expectedResponse
, client
._rfbCredentials
.password
);
1366 expect(client
._sock
).to
.have
.sent(new Uint8Array(expectedResponse
));
1368 client
._initMsg
= sinon
.spy();
1369 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1370 expect(client
._initMsg
).to
.have
.been
.called
;
1373 it('should support Plain authentication with an empty password', function () {
1374 client
._rfbCredentials
= { username
: 'username', password
: '' };
1376 client
._sock
._websocket
._receiveData(new Uint8Array([0, 2]));
1377 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 2]));
1379 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1381 client
._sock
._websocket
._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
1383 const expectedResponse
= [];
1384 push32(expectedResponse
, 256); // Chosen subtype.
1385 push32(expectedResponse
, client
._rfbCredentials
.username
.length
);
1386 push32(expectedResponse
, client
._rfbCredentials
.password
.length
);
1387 pushString(expectedResponse
, client
._rfbCredentials
.username
);
1388 pushString(expectedResponse
, client
._rfbCredentials
.password
);
1389 expect(client
._sock
).to
.have
.sent(new Uint8Array(expectedResponse
));
1391 client
._initMsg
= sinon
.spy();
1392 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1393 expect(client
._initMsg
).to
.have
.been
.called
;
1396 it('should support Plain authentication with a very long username and password', function () {
1397 client
._rfbCredentials
= { username
: 'a'.repeat(300), password
: 'a'.repeat(300) };
1399 client
._sock
._websocket
._receiveData(new Uint8Array([0, 2]));
1400 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 2]));
1402 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1404 client
._sock
._websocket
._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
1406 const expectedResponse
= [];
1407 push32(expectedResponse
, 256); // Chosen subtype.
1408 push32(expectedResponse
, client
._rfbCredentials
.username
.length
);
1409 push32(expectedResponse
, client
._rfbCredentials
.password
.length
);
1410 pushString(expectedResponse
, client
._rfbCredentials
.username
);
1411 pushString(expectedResponse
, client
._rfbCredentials
.password
);
1412 expect(client
._sock
).to
.have
.sent(new Uint8Array(expectedResponse
));
1414 client
._initMsg
= sinon
.spy();
1415 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1416 expect(client
._initMsg
).to
.have
.been
.called
;
1421 describe('SecurityResult', function () {
1422 beforeEach(function () {
1423 client
._rfbInitState
= 'SecurityResult';
1426 it('should fall through to ServerInitialisation on a response code of 0', function () {
1427 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1428 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1431 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1432 client
._rfbVersion
= 3.8;
1433 sinon
.spy(client
, '_fail');
1434 const failureData
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1435 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1436 expect(client
._fail
).to
.have
.been
.calledWith(
1437 'Security negotiation failed on security result (reason: whoops)');
1440 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1441 sinon
.spy(client
, '_fail');
1442 client
._rfbVersion
= 3.7;
1443 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 1]));
1444 expect(client
._fail
).to
.have
.been
.calledWith(
1445 'Security handshake failed');
1448 it('should result in securityfailure event when receiving a non zero status', function () {
1449 const spy
= sinon
.spy();
1450 client
.addEventListener("securityfailure", spy
);
1451 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 2]));
1452 expect(spy
).to
.have
.been
.calledOnce
;
1453 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1456 it('should include reason when provided in securityfailure event', function () {
1457 client
._rfbVersion
= 3.8;
1458 const spy
= sinon
.spy();
1459 client
.addEventListener("securityfailure", spy
);
1460 const failureData
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
1461 32, 102, 97, 105, 108, 117, 114, 101];
1462 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1463 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1464 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
1467 it('should not include reason when length is zero in securityfailure event', function () {
1468 client
._rfbVersion
= 3.9;
1469 const spy
= sinon
.spy();
1470 client
.addEventListener("securityfailure", spy
);
1471 const failureData
= [0, 0, 0, 1, 0, 0, 0, 0];
1472 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1473 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1474 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1477 it('should not include reason in securityfailure event for version < 3.8', function () {
1478 client
._rfbVersion
= 3.6;
1479 const spy
= sinon
.spy();
1480 client
.addEventListener("securityfailure", spy
);
1481 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 2]));
1482 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1483 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1487 describe('ClientInitialisation', function () {
1488 it('should transition to the ServerInitialisation state', function () {
1489 const client
= makeRFB();
1490 client
._rfbConnectionState
= 'connecting';
1491 client
._rfbInitState
= 'SecurityResult';
1492 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1493 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1496 it('should send 1 if we are in shared mode', function () {
1497 const client
= makeRFB('wss://host:8675', { shared
: true });
1498 client
._rfbConnectionState
= 'connecting';
1499 client
._rfbInitState
= 'SecurityResult';
1500 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1501 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1504 it('should send 0 if we are not in shared mode', function () {
1505 const client
= makeRFB('wss://host:8675', { shared
: false });
1506 client
._rfbConnectionState
= 'connecting';
1507 client
._rfbInitState
= 'SecurityResult';
1508 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1509 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1513 describe('ServerInitialisation', function () {
1514 beforeEach(function () {
1515 client
._rfbInitState
= 'ServerInitialisation';
1518 function sendServerInit(opts
, client
) {
1519 const fullOpts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, bigEndian
: 0,
1520 trueColor
: 1, redMax
: 255, greenMax
: 255, blueMax
: 255,
1521 redShift
: 16, greenShift
: 8, blueShift
: 0, name
: 'a name' };
1522 for (let opt
in opts
) {
1523 fullOpts
[opt
] = opts
[opt
];
1527 push16(data
, fullOpts
.width
);
1528 push16(data
, fullOpts
.height
);
1530 data
.push(fullOpts
.bpp
);
1531 data
.push(fullOpts
.depth
);
1532 data
.push(fullOpts
.bigEndian
);
1533 data
.push(fullOpts
.trueColor
);
1535 push16(data
, fullOpts
.redMax
);
1536 push16(data
, fullOpts
.greenMax
);
1537 push16(data
, fullOpts
.blueMax
);
1538 push8(data
, fullOpts
.redShift
);
1539 push8(data
, fullOpts
.greenShift
);
1540 push8(data
, fullOpts
.blueShift
);
1547 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1549 const nameData
= [];
1551 pushString(nameData
, fullOpts
.name
);
1552 push32(nameLen
, nameData
.length
);
1554 client
._sock
._websocket
._receiveData(new Uint8Array(nameLen
));
1555 client
._sock
._websocket
._receiveData(new Uint8Array(nameData
));
1558 it('should set the framebuffer width and height', function () {
1559 sendServerInit({ width
: 32, height
: 84 }, client
);
1560 expect(client
._fbWidth
).to
.equal(32);
1561 expect(client
._fbHeight
).to
.equal(84);
1564 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1566 it('should set the framebuffer name and call the callback', function () {
1567 const spy
= sinon
.spy();
1568 client
.addEventListener("desktopname", spy
);
1569 sendServerInit({ name
: 'som€ nam€' }, client
);
1571 expect(client
._fbName
).to
.equal('som€ nam€');
1572 expect(spy
).to
.have
.been
.calledOnce
;
1573 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
1576 it('should handle the extended init message of the tight encoding', function () {
1577 // NB(sross): we don't actually do anything with it, so just test that we can
1578 // read it w/o throwing an error
1579 client
._rfbTightVNC
= true;
1580 sendServerInit({}, client
);
1582 const tightData
= [];
1583 push16(tightData
, 1);
1584 push16(tightData
, 2);
1585 push16(tightData
, 3);
1586 push16(tightData
, 0);
1587 for (let i
= 0; i
< 16 + 32 + 48; i
++) {
1590 client
._sock
._websocket
._receiveData(new Uint8Array(tightData
));
1592 expect(client
._rfbConnectionState
).to
.equal('connected');
1595 it('should resize the display', function () {
1596 sinon
.spy(client
._display
, 'resize');
1597 sendServerInit({ width
: 27, height
: 32 }, client
);
1599 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1600 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1603 it('should grab the keyboard', function () {
1604 sinon
.spy(client
._keyboard
, 'grab');
1605 sendServerInit({}, client
);
1606 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1609 describe('Initial Update Request', function () {
1610 beforeEach(function () {
1611 sinon
.spy(RFB
.messages
, "pixelFormat");
1612 sinon
.spy(RFB
.messages
, "clientEncodings");
1613 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1616 afterEach(function () {
1617 RFB
.messages
.pixelFormat
.restore();
1618 RFB
.messages
.clientEncodings
.restore();
1619 RFB
.messages
.fbUpdateRequest
.restore();
1622 // TODO(directxman12): test the various options in this configuration matrix
1623 it('should reply with the pixel format, client encodings, and initial update request', function () {
1624 sendServerInit({ width
: 27, height
: 32 }, client
);
1626 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1627 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1628 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1629 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1630 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1631 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1632 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1633 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1636 it('should reply with restricted settings for Intel AMT servers', function () {
1637 sendServerInit({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1639 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1640 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1641 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1642 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1643 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1644 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1645 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1646 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1647 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1651 it('should send the "connect" event', function () {
1652 let spy
= sinon
.spy();
1653 client
.addEventListener('connect', spy
);
1654 sendServerInit({}, client
);
1655 expect(spy
).to
.have
.been
.calledOnce
;
1660 describe('Protocol Message Processing After Completing Initialization', function () {
1663 beforeEach(function () {
1665 client
._fbName
= 'some device';
1666 client
._fbWidth
= 640;
1667 client
._fbHeight
= 20;
1670 describe('Framebuffer Update Handling', function () {
1671 function sendFbuMsg(rectInfo
, rectData
, client
, rectCnt
) {
1674 if (!rectCnt
|| rectCnt
> -1) {
1676 data
.push(0); // msg type
1677 data
.push(0); // padding
1678 push16(data
, rectCnt
|| rectData
.length
);
1681 for (let i
= 0; i
< rectData
.length
; i
++) {
1683 push16(data
, rectInfo
[i
].x
);
1684 push16(data
, rectInfo
[i
].y
);
1685 push16(data
, rectInfo
[i
].width
);
1686 push16(data
, rectInfo
[i
].height
);
1687 push32(data
, rectInfo
[i
].encoding
);
1689 data
= data
.concat(rectData
[i
]);
1692 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1695 it('should send an update request if there is sufficient data', function () {
1696 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1697 RFB
.messages
.fbUpdateRequest(expectedMsg
, true, 0, 0, 640, 20);
1699 client
._framebufferUpdate
= () => true;
1700 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1702 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
1705 it('should not send an update request if we need more data', function () {
1706 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1707 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1710 it('should resume receiving an update if we previously did not have enough data', function () {
1711 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1712 RFB
.messages
.fbUpdateRequest(expectedMsg
, true, 0, 0, 640, 20);
1714 // just enough to set FBU.rects
1715 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 3]));
1716 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1718 client
._framebufferUpdate = function () { this._sock
.rQskipBytes(1); return true; }; // we magically have enough data
1719 // 247 should *not* be used as the message type here
1720 client
._sock
._websocket
._receiveData(new Uint8Array([247]));
1721 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
1724 it('should not send a request in continuous updates mode', function () {
1725 client
._enabledContinuousUpdates
= true;
1726 client
._framebufferUpdate
= () => true;
1727 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1729 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1732 it('should fail on an unsupported encoding', function () {
1733 sinon
.spy(client
, "_fail");
1734 const rectInfo
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1735 sendFbuMsg([rectInfo
], [[]], client
);
1736 expect(client
._fail
).to
.have
.been
.calledOnce
;
1739 describe('Message Encoding Handlers', function () {
1740 beforeEach(function () {
1741 // a really small frame
1742 client
._fbWidth
= 4;
1743 client
._fbHeight
= 4;
1744 client
._fbDepth
= 24;
1745 client
._display
.resize(4, 4);
1748 it('should handle the DesktopSize pseduo-encoding', function () {
1749 sinon
.spy(client
._display
, 'resize');
1750 sendFbuMsg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], 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 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1760 beforeEach(function () {
1761 // a really small frame
1762 client
._fbWidth
= 4;
1763 client
._fbHeight
= 4;
1764 client
._display
.resize(4, 4);
1765 sinon
.spy(client
._display
, 'resize');
1768 function makeScreenData(nrOfScreens
) {
1770 push8(data
, nrOfScreens
); // number-of-screens
1771 push8(data
, 0); // padding
1772 push16(data
, 0); // padding
1773 for (let i
=0; i
<nrOfScreens
; i
+= 1) {
1774 push32(data
, 0); // id
1775 push16(data
, 0); // x-position
1776 push16(data
, 0); // y-position
1777 push16(data
, 20); // width
1778 push16(data
, 50); // height
1779 push32(data
, 0); // flags
1784 it('should handle a resize requested by this client', function () {
1785 const reasonForChange
= 1; // requested by this client
1786 const statusCode
= 0; // No error
1788 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1789 width
: 20, height
: 50, encoding
: -308 }],
1790 makeScreenData(1), client
);
1792 expect(client
._fbWidth
).to
.equal(20);
1793 expect(client
._fbHeight
).to
.equal(50);
1795 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1796 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1799 it('should handle a resize requested by another client', function () {
1800 const reasonForChange
= 2; // requested by another client
1801 const statusCode
= 0; // No error
1803 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1804 width
: 20, height
: 50, encoding
: -308 }],
1805 makeScreenData(1), client
);
1807 expect(client
._fbWidth
).to
.equal(20);
1808 expect(client
._fbHeight
).to
.equal(50);
1810 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1811 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1814 it('should be able to recieve requests which contain data for multiple screens', function () {
1815 const reasonForChange
= 2; // requested by another client
1816 const statusCode
= 0; // No error
1818 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1819 width
: 60, height
: 50, encoding
: -308 }],
1820 makeScreenData(3), client
);
1822 expect(client
._fbWidth
).to
.equal(60);
1823 expect(client
._fbHeight
).to
.equal(50);
1825 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1826 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1829 it('should not handle a failed request', function () {
1830 const reasonForChange
= 1; // requested by this client
1831 const statusCode
= 1; // Resize is administratively prohibited
1833 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1834 width
: 20, height
: 50, encoding
: -308 }],
1835 makeScreenData(1), client
);
1837 expect(client
._fbWidth
).to
.equal(4);
1838 expect(client
._fbHeight
).to
.equal(4);
1840 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1844 describe('the Cursor pseudo-encoding handler', function () {
1845 beforeEach(function () {
1846 sinon
.spy(client
._cursor
, 'change');
1849 it('should handle a standard cursor', function () {
1850 const info
= { x
: 5, y
: 7,
1851 width
: 4, height
: 4,
1856 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1857 push32(rect
, 0x11223300);
1859 push32(rect
, 0xa0a0a0a0);
1861 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
1862 push32(expected
, 0x332211ff);
1863 push32(expected
, 0x33221100);
1865 expected
= new Uint8Array(expected
);
1867 sendFbuMsg([info
], [rect
], client
);
1869 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1870 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1873 it('should handle an empty cursor', function () {
1874 const info
= { x
: 0, y
: 0,
1875 width
: 0, height
: 0,
1879 sendFbuMsg([info
], [rect
], client
);
1881 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1882 expect(client
._cursor
.change
).to
.have
.been
.calledWith(new Uint8Array
, 0, 0, 0, 0);
1885 it('should handle a transparent cursor', function () {
1886 const info
= { x
: 5, y
: 7,
1887 width
: 4, height
: 4,
1892 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1893 push32(rect
, 0x11223300);
1895 push32(rect
, 0x00000000);
1897 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1898 push32(expected
, 0x33221100);
1900 expected
= new Uint8Array(expected
);
1902 sendFbuMsg([info
], [rect
], client
);
1904 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1905 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1908 describe('dot for empty cursor', function () {
1909 beforeEach(function () {
1910 client
.showDotCursor
= true;
1911 // Was called when we enabled dot cursor
1912 client
._cursor
.change
.resetHistory();
1915 it('should show a standard cursor', function () {
1916 const info
= { x
: 5, y
: 7,
1917 width
: 4, height
: 4,
1922 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1923 push32(rect
, 0x11223300);
1925 push32(rect
, 0xa0a0a0a0);
1927 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
1928 push32(expected
, 0x332211ff);
1929 push32(expected
, 0x33221100);
1931 expected
= new Uint8Array(expected
);
1933 sendFbuMsg([info
], [rect
], client
);
1935 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1936 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1939 it('should handle an empty cursor', function () {
1940 const info
= { x
: 0, y
: 0,
1941 width
: 0, height
: 0,
1944 const dot
= RFB
.cursors
.dot
;
1946 sendFbuMsg([info
], [rect
], client
);
1948 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1949 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
1956 it('should handle a transparent cursor', function () {
1957 const info
= { x
: 5, y
: 7,
1958 width
: 4, height
: 4,
1961 const dot
= RFB
.cursors
.dot
;
1963 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1964 push32(rect
, 0x11223300);
1966 push32(rect
, 0x00000000);
1968 sendFbuMsg([info
], [rect
], client
);
1970 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1971 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
1980 describe('the VMware Cursor pseudo-encoding handler', function () {
1981 beforeEach(function () {
1982 sinon
.spy(client
._cursor
, 'change');
1984 afterEach(function () {
1985 client
._cursor
.change
.resetHistory();
1988 it('should handle the VMware cursor pseudo-encoding', function () {
1989 let data
= [0x00, 0x00, 0xff, 0,
1990 0x00, 0xff, 0x00, 0,
1991 0x00, 0xff, 0x00, 0,
1992 0x00, 0x00, 0xff, 0];
1998 for (let i
= 0; i
< data
.length
; i
++) {
1999 push8(rect
, data
[i
]);
2002 for (let i
= 0; i
< data
.length
; i
++) {
2003 push8(rect
, data
[i
]);
2006 sendFbuMsg([{ x
: 0, y
: 0, width
: 2, height
: 2,
2007 encoding
: 0x574d5664}],
2009 expect(client
._FBU
.rects
).to
.equal(0);
2012 it('should handle insufficient cursor pixel data', function () {
2014 // Specified 14x23 pixels for the cursor,
2015 // but only send 2x2 pixels worth of data
2018 let data
= [0x00, 0x00, 0xff, 0,
2019 0x00, 0xff, 0x00, 0];
2026 for (let i
= 0; i
< data
.length
; i
++) {
2027 push8(rect
, data
[i
]);
2030 for (let i
= 0; i
< data
.length
; i
++) {
2031 push8(rect
, data
[i
]);
2034 sendFbuMsg([{ x
: 0, y
: 0, width
: w
, height
: h
,
2035 encoding
: 0x574d5664}],
2038 // expect one FBU to remain unhandled
2039 expect(client
._FBU
.rects
).to
.equal(1);
2042 it('should update the cursor when type is classic', function () {
2044 [0xff, 0xff, 0xff, 0xff, //Transparent
2045 0xff, 0xff, 0xff, 0xff, //Transparent
2046 0x00, 0x00, 0x00, 0x00, //Opaque
2047 0xff, 0xff, 0xff, 0xff]; //Inverted
2050 [0x00, 0x00, 0x00, 0x00, //Transparent
2051 0x00, 0x00, 0x00, 0x00, //Transparent
2052 0x11, 0x22, 0x33, 0x44, //Opaque
2053 0xff, 0xff, 0xff, 0x44]; //Inverted
2056 push8(rect
, 0); //cursor_type
2057 push8(rect
, 0); //padding
2064 for (let i
= 0; i
< andMask
.length
; i
++) {
2065 push8(rect
, andMask
[i
]);
2068 for (let i
= 0; i
< xorMask
.length
; i
++) {
2069 push8(rect
, xorMask
[i
]);
2072 let expectedRgba
= [0x00, 0x00, 0x00, 0x00,
2073 0x00, 0x00, 0x00, 0x00,
2074 0x33, 0x22, 0x11, 0xff,
2075 0x00, 0x00, 0x00, 0xff];
2077 sendFbuMsg([{ x
: hotx
, y
: hoty
,
2078 width
: w
, height
: h
,
2079 encoding
: 0x574d5664}],
2082 expect(client
._cursor
.change
)
2083 .to
.have
.been
.calledOnce
;
2084 expect(client
._cursor
.change
)
2085 .to
.have
.been
.calledWith(expectedRgba
,
2090 it('should update the cursor when type is alpha', function () {
2091 let data
= [0xee, 0x55, 0xff, 0x00, // rgba
2092 0x00, 0xff, 0x00, 0xff,
2093 0x00, 0xff, 0x00, 0x22,
2094 0x00, 0xff, 0x00, 0x22,
2095 0x00, 0xff, 0x00, 0x22,
2096 0x00, 0x00, 0xff, 0xee];
2098 push8(rect
, 1); //cursor_type
2099 push8(rect
, 0); //padding
2105 for (let i
= 0; i
< data
.length
; i
++) {
2106 push8(rect
, data
[i
]);
2109 let expectedRgba
= [0xee, 0x55, 0xff, 0x00,
2110 0x00, 0xff, 0x00, 0xff,
2111 0x00, 0xff, 0x00, 0x22,
2112 0x00, 0xff, 0x00, 0x22,
2113 0x00, 0xff, 0x00, 0x22,
2114 0x00, 0x00, 0xff, 0xee];
2116 sendFbuMsg([{ x
: hotx
, y
: hoty
,
2117 width
: w
, height
: h
,
2118 encoding
: 0x574d5664}],
2121 expect(client
._cursor
.change
)
2122 .to
.have
.been
.calledOnce
;
2123 expect(client
._cursor
.change
)
2124 .to
.have
.been
.calledWith(expectedRgba
,
2129 it('should not update cursor when incorrect cursor type given', function () {
2131 push8(rect
, 3); // invalid cursor type
2132 push8(rect
, 0); // padding
2134 client
._cursor
.change
.resetHistory();
2135 sendFbuMsg([{ x
: 0, y
: 0, width
: 2, height
: 2,
2136 encoding
: 0x574d5664}],
2139 expect(client
._cursor
.change
)
2140 .to
.not
.have
.been
.called
;
2144 it('should handle the last_rect pseudo-encoding', function () {
2145 sendFbuMsg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
2146 expect(client
._FBU
.rects
).to
.equal(0);
2149 it('should handle the DesktopName pseudo-encoding', function () {
2152 pushString(data
, "som€ nam€");
2154 const spy
= sinon
.spy();
2155 client
.addEventListener("desktopname", spy
);
2157 sendFbuMsg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -307 }], [data
], client
);
2159 expect(client
._fbName
).to
.equal('som€ nam€');
2160 expect(spy
).to
.have
.been
.calledOnce
;
2161 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
2166 describe('XVP Message Handling', function () {
2167 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
2168 const spy
= sinon
.spy();
2169 client
.addEventListener("capabilities", spy
);
2170 client
._sock
._websocket
._receiveData(new Uint8Array([250, 0, 10, 1]));
2171 expect(client
._rfbXvpVer
).to
.equal(10);
2172 expect(spy
).to
.have
.been
.calledOnce
;
2173 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
2174 expect(client
.capabilities
.power
).to
.be
.true;
2177 it('should fail on unknown XVP message types', function () {
2178 sinon
.spy(client
, "_fail");
2179 client
._sock
._websocket
._receiveData(new Uint8Array([250, 0, 10, 237]));
2180 expect(client
._fail
).to
.have
.been
.calledOnce
;
2184 describe('Normal Clipboard Handling Receive', function () {
2185 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
2186 const expectedStr
= 'cheese!';
2187 const data
= [3, 0, 0, 0];
2188 push32(data
, expectedStr
.length
);
2189 for (let i
= 0; i
< expectedStr
.length
; i
++) { data
.push(expectedStr
.charCodeAt(i
)); }
2190 const spy
= sinon
.spy();
2191 client
.addEventListener("clipboard", spy
);
2193 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2194 expect(spy
).to
.have
.been
.calledOnce
;
2195 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedStr
);
2199 describe('Extended clipboard Handling', function () {
2201 describe('Extended clipboard initialization', function () {
2202 beforeEach(function () {
2203 sinon
.spy(RFB
.messages
, 'extendedClipboardCaps');
2206 afterEach(function () {
2207 RFB
.messages
.extendedClipboardCaps
.restore();
2210 it('should update capabilities when receiving a Caps message', function () {
2211 let data
= [3, 0, 0, 0];
2212 const flags
= [0x1F, 0x00, 0x00, 0x03];
2213 let fileSizes
= [0x00, 0x00, 0x00, 0x1E,
2214 0x00, 0x00, 0x00, 0x3C];
2216 push32(data
, toUnsigned32bit(-12));
2217 data
= data
.concat(flags
);
2218 data
= data
.concat(fileSizes
);
2219 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2221 // Check that we give an response caps when we receive one
2222 expect(RFB
.messages
.extendedClipboardCaps
).to
.have
.been
.calledOnce
;
2224 // FIXME: Can we avoid checking internal variables?
2225 expect(client
._clipboardServerCapabilitiesFormats
[0]).to
.not
.equal(true);
2226 expect(client
._clipboardServerCapabilitiesFormats
[1]).to
.equal(true);
2227 expect(client
._clipboardServerCapabilitiesFormats
[2]).to
.equal(true);
2228 expect(client
._clipboardServerCapabilitiesActions
[(1 << 24)]).to
.equal(true);
2234 describe('Extended Clipboard Handling Receive', function () {
2236 beforeEach(function () {
2237 // Send our capabilities
2238 let data
= [3, 0, 0, 0];
2239 const flags
= [0x1F, 0x00, 0x00, 0x01];
2240 let fileSizes
= [0x00, 0x00, 0x00, 0x1E];
2242 push32(data
, toUnsigned32bit(-8));
2243 data
= data
.concat(flags
);
2244 data
= data
.concat(fileSizes
);
2245 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2248 describe('Handle Provide', function () {
2249 it('should update clipboard with correct Unicode data from a Provide message', function () {
2250 let expectedData
= "Aå漢å—!";
2251 let data
= [3, 0, 0, 0];
2252 const flags
= [0x10, 0x00, 0x00, 0x01];
2254 /* The size 10 (utf8 encoded string size) and the
2255 string "Aå漢å—!" utf8 encoded and deflated. */
2256 let deflatedData
= [120, 94, 99, 96, 96, 224, 114, 60,
2257 188, 244, 217, 158, 69, 79, 215,
2258 78, 87, 4, 0, 35, 207, 6, 66];
2260 // How much data we are sending.
2261 push32(data
, toUnsigned32bit(-(4 + deflatedData
.length
)));
2263 data
= data
.concat(flags
);
2264 data
= data
.concat(deflatedData
);
2266 const spy
= sinon
.spy();
2267 client
.addEventListener("clipboard", spy
);
2269 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2270 expect(spy
).to
.have
.been
.calledOnce
;
2271 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2272 client
.removeEventListener("clipboard", spy
);
2275 it('should update clipboard with correct escape characters from a Provide message ', function () {
2276 let expectedData
= "Oh\nmy!";
2277 let data
= [3, 0, 0, 0];
2278 const flags
= [0x10, 0x00, 0x00, 0x01];
2280 let text
= encodeUTF8("Oh\r\nmy!\0");
2282 let deflatedText
= deflateWithSize(text
);
2284 // How much data we are sending.
2285 push32(data
, toUnsigned32bit(-(4 + deflatedText
.length
)));
2287 data
= data
.concat(flags
);
2289 let sendData
= new Uint8Array(data
.length
+ deflatedText
.length
);
2291 sendData
.set(deflatedText
, data
.length
);
2293 const spy
= sinon
.spy();
2294 client
.addEventListener("clipboard", spy
);
2296 client
._sock
._websocket
._receiveData(sendData
);
2297 expect(spy
).to
.have
.been
.calledOnce
;
2298 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2299 client
.removeEventListener("clipboard", spy
);
2302 it('should be able to handle large Provide messages', function () {
2303 let expectedData
= "hello".repeat(100000);
2304 let data
= [3, 0, 0, 0];
2305 const flags
= [0x10, 0x00, 0x00, 0x01];
2307 let text
= encodeUTF8(expectedData
+ "\0");
2309 let deflatedText
= deflateWithSize(text
);
2311 // How much data we are sending.
2312 push32(data
, toUnsigned32bit(-(4 + deflatedText
.length
)));
2314 data
= data
.concat(flags
);
2316 let sendData
= new Uint8Array(data
.length
+ deflatedText
.length
);
2318 sendData
.set(deflatedText
, data
.length
);
2320 const spy
= sinon
.spy();
2321 client
.addEventListener("clipboard", spy
);
2323 client
._sock
._websocket
._receiveData(sendData
);
2324 expect(spy
).to
.have
.been
.calledOnce
;
2325 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2326 client
.removeEventListener("clipboard", spy
);
2331 describe('Handle Notify', function () {
2332 beforeEach(function () {
2333 sinon
.spy(RFB
.messages
, 'extendedClipboardRequest');
2336 afterEach(function () {
2337 RFB
.messages
.extendedClipboardRequest
.restore();
2340 it('should make a request with supported formats when receiving a notify message', function () {
2341 let data
= [3, 0, 0, 0];
2342 const flags
= [0x08, 0x00, 0x00, 0x07];
2343 push32(data
, toUnsigned32bit(-4));
2344 data
= data
.concat(flags
);
2345 let expectedData
= [0x01];
2347 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2349 expect(RFB
.messages
.extendedClipboardRequest
).to
.have
.been
.calledOnce
;
2350 expect(RFB
.messages
.extendedClipboardRequest
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2354 describe('Handle Peek', function () {
2355 beforeEach(function () {
2356 sinon
.spy(RFB
.messages
, 'extendedClipboardNotify');
2359 afterEach(function () {
2360 RFB
.messages
.extendedClipboardNotify
.restore();
2363 it('should send an empty Notify when receiving a Peek and no excisting clipboard data', function () {
2364 let data
= [3, 0, 0, 0];
2365 const flags
= [0x04, 0x00, 0x00, 0x00];
2366 push32(data
, toUnsigned32bit(-4));
2367 data
= data
.concat(flags
);
2368 let expectedData
= [];
2370 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2372 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
2373 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2376 it('should send a Notify message with supported formats when receiving a Peek', function () {
2377 let data
= [3, 0, 0, 0];
2378 const flags
= [0x04, 0x00, 0x00, 0x00];
2379 push32(data
, toUnsigned32bit(-4));
2380 data
= data
.concat(flags
);
2381 let expectedData
= [0x01];
2383 // Needed to have clipboard data to read.
2384 // This will trigger a call to Notify, reset history
2385 client
.clipboardPasteFrom("HejHej");
2386 RFB
.messages
.extendedClipboardNotify
.resetHistory();
2388 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2390 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
2391 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2395 describe('Handle Request', function () {
2396 beforeEach(function () {
2397 sinon
.spy(RFB
.messages
, 'extendedClipboardProvide');
2400 afterEach(function () {
2401 RFB
.messages
.extendedClipboardProvide
.restore();
2404 it('should send a Provide message with supported formats when receiving a Request', function () {
2405 let data
= [3, 0, 0, 0];
2406 const flags
= [0x02, 0x00, 0x00, 0x01];
2407 push32(data
, toUnsigned32bit(-4));
2408 data
= data
.concat(flags
);
2409 let expectedData
= [0x01];
2411 client
.clipboardPasteFrom("HejHej");
2412 expect(RFB
.messages
.extendedClipboardProvide
).to
.not
.have
.been
.called
;
2414 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2416 expect(RFB
.messages
.extendedClipboardProvide
).to
.have
.been
.calledOnce
;
2417 expect(RFB
.messages
.extendedClipboardProvide
).to
.have
.been
.calledWith(client
._sock
, expectedData
, ["HejHej"]);
2424 it('should fire the bell callback on Bell', function () {
2425 const spy
= sinon
.spy();
2426 client
.addEventListener("bell", spy
);
2427 client
._sock
._websocket
._receiveData(new Uint8Array([2]));
2428 expect(spy
).to
.have
.been
.calledOnce
;
2431 it('should respond correctly to ServerFence', function () {
2432 const expectedMsg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2433 const incomingMsg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2435 const payload
= "foo\x00ab9";
2437 // ClientFence and ServerFence are identical in structure
2438 RFB
.messages
.clientFence(expectedMsg
, (1<<0) | (1<<1), payload
);
2439 RFB
.messages
.clientFence(incomingMsg
, 0xffffffff, payload
);
2441 client
._sock
._websocket
._receiveData(incomingMsg
._sQ
);
2443 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2445 expectedMsg
._sQlen
= 0;
2446 incomingMsg
._sQlen
= 0;
2448 RFB
.messages
.clientFence(expectedMsg
, (1<<0), payload
);
2449 RFB
.messages
.clientFence(incomingMsg
, (1<<0) | (1<<31), payload
);
2451 client
._sock
._websocket
._receiveData(incomingMsg
._sQ
);
2453 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2456 it('should enable continuous updates on first EndOfContinousUpdates', function () {
2457 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2459 RFB
.messages
.enableContinuousUpdates(expectedMsg
, true, 0, 0, 640, 20);
2461 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2463 client
._sock
._websocket
._receiveData(new Uint8Array([150]));
2465 expect(client
._enabledContinuousUpdates
).to
.be
.true;
2466 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2469 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
2470 client
._enabledContinuousUpdates
= true;
2471 client
._supportsContinuousUpdates
= true;
2473 client
._sock
._websocket
._receiveData(new Uint8Array([150]));
2475 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2478 it('should update continuous updates on resize', function () {
2479 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2480 RFB
.messages
.enableContinuousUpdates(expectedMsg
, true, 0, 0, 90, 700);
2482 client
._resize(450, 160);
2484 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
2486 client
._enabledContinuousUpdates
= true;
2488 client
._resize(90, 700);
2490 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2493 it('should fail on an unknown message type', function () {
2494 sinon
.spy(client
, "_fail");
2495 client
._sock
._websocket
._receiveData(new Uint8Array([87]));
2496 expect(client
._fail
).to
.have
.been
.calledOnce
;
2500 describe('Asynchronous Events', function () {
2506 beforeEach(function () {
2508 client
._display
.resize(100, 100);
2510 // We need to disable this as focusing the canvas will
2511 // cause the browser to scoll to it, messing up our
2512 // client coordinate calculations
2513 client
.focusOnClick
= false;
2515 pointerEvent
= sinon
.spy(RFB
.messages
, 'pointerEvent');
2516 keyEvent
= sinon
.spy(RFB
.messages
, 'keyEvent');
2517 qemuKeyEvent
= sinon
.spy(RFB
.messages
, 'QEMUExtendedKeyEvent');
2520 afterEach(function () {
2521 pointerEvent
.restore();
2523 qemuKeyEvent
.restore();
2526 function elementToClient(x
, y
) {
2527 let res
= { x
: 0, y
: 0 };
2529 let bounds
= client
._canvas
.getBoundingClientRect();
2532 * If the canvas is on a fractional position we will calculate
2533 * a fractional mouse position. But that gets truncated when we
2534 * send the event, AND the same thing happens in RFB when it
2535 * generates the PointerEvent message. To compensate for that
2536 * fact we round the value upwards here.
2538 res
.x
= Math
.ceil(bounds
.left
+ x
);
2539 res
.y
= Math
.ceil(bounds
.top
+ y
);
2544 describe('Mouse Events', function () {
2545 function sendMouseMoveEvent(x
, y
) {
2546 let pos
= elementToClient(x
, y
);
2549 ev
= new MouseEvent('mousemove',
2550 { 'screenX': pos
.x
+ window
.screenX
,
2551 'screenY': pos
.y
+ window
.screenY
,
2553 'clientY': pos
.y
});
2554 client
._canvas
.dispatchEvent(ev
);
2557 function sendMouseButtonEvent(x
, y
, down
, button
) {
2558 let pos
= elementToClient(x
, y
);
2561 ev
= new MouseEvent(down
? 'mousedown' : 'mouseup',
2562 { 'screenX': pos
.x
+ window
.screenX
,
2563 'screenY': pos
.y
+ window
.screenY
,
2567 'buttons': 1 << button
});
2568 client
._canvas
.dispatchEvent(ev
);
2571 it('should not send button messages in view-only mode', function () {
2572 client
._viewOnly
= true;
2573 sendMouseButtonEvent(10, 10, true, 0);
2575 expect(pointerEvent
).to
.not
.have
.been
.called
;
2578 it('should not send movement messages in view-only mode', function () {
2579 client
._viewOnly
= true;
2580 sendMouseMoveEvent(10, 10);
2582 expect(pointerEvent
).to
.not
.have
.been
.called
;
2585 it('should handle left mouse button', function () {
2586 sendMouseButtonEvent(10, 10, true, 0);
2588 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2590 pointerEvent
.resetHistory();
2592 sendMouseButtonEvent(10, 10, false, 0);
2594 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2598 it('should handle middle mouse button', function () {
2599 sendMouseButtonEvent(10, 10, true, 1);
2601 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2603 pointerEvent
.resetHistory();
2605 sendMouseButtonEvent(10, 10, false, 1);
2607 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2611 it('should handle right mouse button', function () {
2612 sendMouseButtonEvent(10, 10, true, 2);
2614 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2616 pointerEvent
.resetHistory();
2618 sendMouseButtonEvent(10, 10, false, 2);
2620 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2624 it('should handle multiple mouse buttons', function () {
2625 sendMouseButtonEvent(10, 10, true, 0);
2626 sendMouseButtonEvent(10, 10, true, 2);
2628 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2629 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2631 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2634 pointerEvent
.resetHistory();
2636 sendMouseButtonEvent(10, 10, false, 0);
2637 sendMouseButtonEvent(10, 10, false, 2);
2639 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2640 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2642 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2646 it('should handle mouse movement', function () {
2647 sendMouseMoveEvent(50, 70);
2648 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2652 it('should handle click and drag', function () {
2653 sendMouseButtonEvent(10, 10, true, 0);
2654 sendMouseMoveEvent(50, 70);
2656 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2657 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2659 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2662 pointerEvent
.resetHistory();
2664 sendMouseButtonEvent(50, 70, false, 0);
2666 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2670 describe('Event Aggregation', function () {
2671 it('should send a single pointer event on mouse movement', function () {
2672 sendMouseMoveEvent(50, 70);
2674 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2678 it('should delay one move if two events are too close', function () {
2679 sendMouseMoveEvent(18, 30);
2680 sendMouseMoveEvent(20, 50);
2682 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2684 pointerEvent
.resetHistory();
2688 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2692 it('should only send first and last move of many close events', function () {
2693 sendMouseMoveEvent(18, 30);
2694 sendMouseMoveEvent(20, 50);
2695 sendMouseMoveEvent(21, 55);
2697 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2699 pointerEvent
.resetHistory();
2703 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2707 // We selected the 17ms since that is ~60 FPS
2708 it('should send move events every 17 ms', function () {
2709 sendMouseMoveEvent(1, 10); // instant send
2712 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2714 pointerEvent
.resetHistory();
2716 sendMouseMoveEvent(2, 20); // delayed
2717 clock
.tick(10); // timeout send
2719 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2721 pointerEvent
.resetHistory();
2723 sendMouseMoveEvent(3, 30); // delayed
2725 sendMouseMoveEvent(4, 40); // delayed
2726 clock
.tick(10); // timeout send
2728 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2730 pointerEvent
.resetHistory();
2732 sendMouseMoveEvent(5, 50); // delayed
2734 expect(pointerEvent
).to
.not
.have
.been
.called
;
2737 it('should send waiting move events before a button press', function () {
2738 sendMouseMoveEvent(13, 9);
2740 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2742 pointerEvent
.resetHistory();
2744 sendMouseMoveEvent(20, 70);
2746 expect(pointerEvent
).to
.not
.have
.been
.called
;
2748 sendMouseButtonEvent(20, 70, true, 0);
2750 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2751 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2753 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2757 it('should send move events with enough time apart normally', function () {
2758 sendMouseMoveEvent(58, 60);
2760 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2762 pointerEvent
.resetHistory();
2766 sendMouseMoveEvent(25, 60);
2768 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2770 pointerEvent
.resetHistory();
2773 it('should not send waiting move events if disconnected', function () {
2774 sendMouseMoveEvent(88, 99);
2776 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2778 pointerEvent
.resetHistory();
2780 sendMouseMoveEvent(66, 77);
2781 client
.disconnect();
2784 expect(pointerEvent
).to
.not
.have
.been
.called
;
2788 it
.skip('should block click events', function () {
2792 it
.skip('should block contextmenu events', function () {
2797 describe('Wheel Events', function () {
2798 function sendWheelEvent(x
, y
, dx
, dy
, mode
=0) {
2799 let pos
= elementToClient(x
, y
);
2802 ev
= new WheelEvent('wheel',
2803 { 'screenX': pos
.x
+ window
.screenX
,
2804 'screenY': pos
.y
+ window
.screenY
,
2809 'deltaMode': mode
});
2810 client
._canvas
.dispatchEvent(ev
);
2813 it('should handle wheel up event', function () {
2814 sendWheelEvent(10, 10, 0, -50);
2816 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2817 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2819 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2823 it('should handle wheel down event', function () {
2824 sendWheelEvent(10, 10, 0, 50);
2826 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2827 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2829 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2833 it('should handle wheel left event', function () {
2834 sendWheelEvent(10, 10, -50, 0);
2836 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2837 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2839 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2843 it('should handle wheel right event', function () {
2844 sendWheelEvent(10, 10, 50, 0);
2846 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2847 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2849 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2853 it('should ignore wheel when in view only', function () {
2854 client
._viewOnly
= true;
2856 sendWheelEvent(10, 10, 50, 0);
2858 expect(pointerEvent
).to
.not
.have
.been
.called
;
2861 it('should accumulate wheel events if small enough', function () {
2862 sendWheelEvent(10, 10, 0, 20);
2863 sendWheelEvent(10, 10, 0, 20);
2865 expect(pointerEvent
).to
.not
.have
.been
.called
;
2867 sendWheelEvent(10, 10, 0, 20);
2869 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2870 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2872 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2876 it('should not accumulate large wheel events', function () {
2877 sendWheelEvent(10, 10, 0, 400);
2879 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2880 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2882 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2886 it('should handle line based wheel event', function () {
2887 sendWheelEvent(10, 10, 0, 3, 1);
2889 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2890 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2892 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2896 it('should handle page based wheel event', function () {
2897 sendWheelEvent(10, 10, 0, 3, 2);
2899 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2900 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2902 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2907 describe('Keyboard Events', function () {
2908 it('should send a key message on a key press', function () {
2909 client
._handleKeyEvent(0x41, 'KeyA', true);
2910 const keyMsg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
2911 RFB
.messages
.keyEvent(keyMsg
, 0x41, 1);
2912 expect(client
._sock
).to
.have
.sent(keyMsg
._sQ
);
2915 it('should not send messages in view-only mode', function () {
2916 client
._viewOnly
= true;
2917 sinon
.spy(client
._sock
, 'flush');
2918 client
._handleKeyEvent('a', 'KeyA', true);
2919 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2923 describe('Gesture event handlers', function () {
2924 function gestureStart(gestureType
, x
, y
,
2925 magnitudeX
= 0, magnitudeY
= 0) {
2926 let pos
= elementToClient(x
, y
);
2927 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2929 detail
.magnitudeX
= magnitudeX
;
2930 detail
.magnitudeY
= magnitudeY
;
2932 let ev
= new CustomEvent('gesturestart', { detail
: detail
});
2933 client
._canvas
.dispatchEvent(ev
);
2936 function gestureMove(gestureType
, x
, y
,
2937 magnitudeX
= 0, magnitudeY
= 0) {
2938 let pos
= elementToClient(x
, y
);
2939 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2941 detail
.magnitudeX
= magnitudeX
;
2942 detail
.magnitudeY
= magnitudeY
;
2944 let ev
= new CustomEvent('gesturemove', { detail
: detail
});
2945 client
._canvas
.dispatchEvent(ev
);
2948 function gestureEnd(gestureType
, x
, y
) {
2949 let pos
= elementToClient(x
, y
);
2950 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2951 let ev
= new CustomEvent('gestureend', { detail
: detail
});
2952 client
._canvas
.dispatchEvent(ev
);
2955 describe('Gesture onetap', function () {
2956 it('should handle onetap events', function () {
2959 gestureStart('onetap', 20, 40);
2960 gestureEnd('onetap', 20, 40);
2962 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2963 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2965 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2967 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2971 it('should keep same position for multiple onetap events', function () {
2974 gestureStart('onetap', 20, 40);
2975 gestureEnd('onetap', 20, 40);
2977 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2978 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2980 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2982 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2985 pointerEvent
.resetHistory();
2987 gestureStart('onetap', 20, 50);
2988 gestureEnd('onetap', 20, 50);
2990 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2991 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2993 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2995 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2998 pointerEvent
.resetHistory();
3000 gestureStart('onetap', 30, 50);
3001 gestureEnd('onetap', 30, 50);
3003 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3004 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3006 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3008 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3012 it('should not keep same position for onetap events when too far apart', function () {
3015 gestureStart('onetap', 20, 40);
3016 gestureEnd('onetap', 20, 40);
3018 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3019 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3021 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3023 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3026 pointerEvent
.resetHistory();
3028 gestureStart('onetap', 80, 95);
3029 gestureEnd('onetap', 80, 95);
3031 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3032 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3034 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3036 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3040 it('should not keep same position for onetap events when enough time inbetween', function () {
3043 gestureStart('onetap', 10, 20);
3044 gestureEnd('onetap', 10, 20);
3046 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3047 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3049 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3051 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3054 pointerEvent
.resetHistory();
3055 this.clock
.tick(1500);
3057 gestureStart('onetap', 15, 20);
3058 gestureEnd('onetap', 15, 20);
3060 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3061 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3063 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3065 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3068 pointerEvent
.resetHistory();
3072 describe('Gesture twotap', function () {
3073 it('should handle gesture twotap events', function () {
3076 gestureStart("twotap", 20, 40);
3078 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3079 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3081 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3083 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3087 it('should keep same position for multiple twotap events', function () {
3090 for (let offset
= 0;offset
< 30;offset
+= 10) {
3091 pointerEvent
.resetHistory();
3093 gestureStart('twotap', 20, 40 + offset
);
3094 gestureEnd('twotap', 20, 40 + offset
);
3096 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3097 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3099 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3101 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3107 describe('Gesture threetap', function () {
3108 it('should handle gesture start for threetap events', function () {
3111 gestureStart("threetap", 20, 40);
3113 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3114 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3116 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3118 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3122 it('should keep same position for multiple threetap events', function () {
3125 for (let offset
= 0;offset
< 30;offset
+= 10) {
3126 pointerEvent
.resetHistory();
3128 gestureStart('threetap', 20, 40 + offset
);
3129 gestureEnd('threetap', 20, 40 + offset
);
3131 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3132 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3134 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3136 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3142 describe('Gesture drag', function () {
3143 it('should handle gesture drag events', function () {
3146 gestureStart('drag', 20, 40);
3148 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3149 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3151 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3154 pointerEvent
.resetHistory();
3156 gestureMove('drag', 30, 50);
3159 expect(pointerEvent
).to
.have
.been
.calledOnce
;
3160 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3163 pointerEvent
.resetHistory();
3165 gestureEnd('drag', 30, 50);
3167 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3168 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3170 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3175 describe('Gesture long press', function () {
3176 it('should handle long press events', function () {
3179 gestureStart('longpress', 20, 40);
3181 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3182 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3184 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3186 pointerEvent
.resetHistory();
3188 gestureMove('longpress', 40, 60);
3191 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3194 pointerEvent
.resetHistory();
3196 gestureEnd('longpress', 40, 60);
3198 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3199 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3201 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3206 describe('Gesture twodrag', function () {
3207 it('should handle gesture twodrag up events', function () {
3208 let bmask
= 0x10; // Button mask for scroll down
3210 gestureStart('twodrag', 20, 40, 0, 0);
3212 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3215 pointerEvent
.resetHistory();
3217 gestureMove('twodrag', 20, 40, 0, -60);
3219 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3220 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3222 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3224 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3228 it('should handle gesture twodrag down events', function () {
3229 let bmask
= 0x8; // Button mask for scroll up
3231 gestureStart('twodrag', 20, 40, 0, 0);
3233 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3236 pointerEvent
.resetHistory();
3238 gestureMove('twodrag', 20, 40, 0, 60);
3240 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3241 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3243 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3245 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3249 it('should handle gesture twodrag right events', function () {
3250 let bmask
= 0x20; // Button mask for scroll right
3252 gestureStart('twodrag', 20, 40, 0, 0);
3254 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3257 pointerEvent
.resetHistory();
3259 gestureMove('twodrag', 20, 40, 60, 0);
3261 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3262 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3264 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3266 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3270 it('should handle gesture twodrag left events', function () {
3271 let bmask
= 0x40; // Button mask for scroll left
3273 gestureStart('twodrag', 20, 40, 0, 0);
3275 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3278 pointerEvent
.resetHistory();
3280 gestureMove('twodrag', 20, 40, -60, 0);
3282 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3283 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3285 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3287 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3291 it('should handle gesture twodrag diag events', function () {
3292 let scrlUp
= 0x8; // Button mask for scroll up
3293 let scrlRight
= 0x20; // Button mask for scroll right
3295 gestureStart('twodrag', 20, 40, 0, 0);
3297 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3300 pointerEvent
.resetHistory();
3302 gestureMove('twodrag', 20, 40, 60, 60);
3304 expect(pointerEvent
).to
.have
.been
.callCount(5);
3305 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3307 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3309 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3311 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3313 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3317 it('should handle multiple small gesture twodrag events', function () {
3318 let bmask
= 0x8; // Button mask for scroll up
3320 gestureStart('twodrag', 20, 40, 0, 0);
3322 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3325 pointerEvent
.resetHistory();
3327 gestureMove('twodrag', 20, 40, 0, 10);
3330 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3333 pointerEvent
.resetHistory();
3335 gestureMove('twodrag', 20, 40, 0, 20);
3338 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3341 pointerEvent
.resetHistory();
3343 gestureMove('twodrag', 20, 40, 0, 60);
3345 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3346 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3348 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3350 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3354 it('should handle large gesture twodrag events', function () {
3355 let bmask
= 0x8; // Button mask for scroll up
3357 gestureStart('twodrag', 30, 50, 0, 0);
3359 expect(pointerEvent
).
3360 to
.have
.been
.calledOnceWith(client
._sock
, 30, 50, 0x0);
3362 pointerEvent
.resetHistory();
3364 gestureMove('twodrag', 30, 50, 0, 200);
3366 expect(pointerEvent
).to
.have
.callCount(7);
3367 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3369 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3371 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3373 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3375 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3377 expect(pointerEvent
.getCall(5)).to
.have
.been
.calledWith(client
._sock
,
3379 expect(pointerEvent
.getCall(6)).to
.have
.been
.calledWith(client
._sock
,
3384 describe('Gesture pinch', function () {
3385 it('should handle gesture pinch in events', function () {
3386 let keysym
= KeyTable
.XK_Control_L
;
3387 let bmask
= 0x10; // Button mask for scroll down
3389 gestureStart('pinch', 20, 40, 90, 90);
3391 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3393 expect(keyEvent
).to
.not
.have
.been
.called
;
3395 pointerEvent
.resetHistory();
3397 gestureMove('pinch', 20, 40, 30, 30);
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', 20, 40);
3421 expect(pointerEvent
).to
.not
.have
.been
.called
;
3422 expect(keyEvent
).to
.not
.have
.been
.called
;
3425 it('should handle gesture pinch out events', function () {
3426 let keysym
= KeyTable
.XK_Control_L
;
3427 let bmask
= 0x8; // Button mask for scroll up
3429 gestureStart('pinch', 10, 20, 10, 20);
3431 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3433 expect(keyEvent
).to
.not
.have
.been
.called
;
3435 pointerEvent
.resetHistory();
3437 gestureMove('pinch', 10, 20, 70, 80);
3439 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3440 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3442 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3444 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3447 expect(keyEvent
).to
.have
.been
.calledTwice
;
3448 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3450 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3453 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3454 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3456 pointerEvent
.resetHistory();
3457 keyEvent
.resetHistory();
3459 gestureEnd('pinch', 10, 20);
3461 expect(pointerEvent
).to
.not
.have
.been
.called
;
3462 expect(keyEvent
).to
.not
.have
.been
.called
;
3465 it('should handle large gesture pinch', function () {
3466 let keysym
= KeyTable
.XK_Control_L
;
3467 let bmask
= 0x10; // Button mask for scroll down
3469 gestureStart('pinch', 20, 40, 150, 150);
3471 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3473 expect(keyEvent
).to
.not
.have
.been
.called
;
3475 pointerEvent
.resetHistory();
3477 gestureMove('pinch', 20, 40, 30, 30);
3479 expect(pointerEvent
).to
.have
.been
.callCount(5);
3480 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3482 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3484 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3486 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3488 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3491 expect(keyEvent
).to
.have
.been
.calledTwice
;
3492 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3494 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3497 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3498 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3500 pointerEvent
.resetHistory();
3501 keyEvent
.resetHistory();
3503 gestureEnd('pinch', 20, 40);
3505 expect(pointerEvent
).to
.not
.have
.been
.called
;
3506 expect(keyEvent
).to
.not
.have
.been
.called
;
3509 it('should handle multiple small gesture pinch out events', function () {
3510 let keysym
= KeyTable
.XK_Control_L
;
3511 let bmask
= 0x8; // Button mask for scroll down
3513 gestureStart('pinch', 20, 40, 0, 10);
3515 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3517 expect(keyEvent
).to
.not
.have
.been
.called
;
3519 pointerEvent
.resetHistory();
3521 gestureMove('pinch', 20, 40, 0, 30);
3524 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3527 pointerEvent
.resetHistory();
3529 gestureMove('pinch', 20, 40, 0, 60);
3532 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3535 pointerEvent
.resetHistory();
3536 keyEvent
.resetHistory();
3538 gestureMove('pinch', 20, 40, 0, 90);
3540 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3541 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3543 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3545 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3548 expect(keyEvent
).to
.have
.been
.calledTwice
;
3549 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3551 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3554 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3555 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3557 pointerEvent
.resetHistory();
3558 keyEvent
.resetHistory();
3560 gestureEnd('pinch', 20, 40);
3562 expect(keyEvent
).to
.not
.have
.been
.called
;
3565 it('should send correct key control code', function () {
3566 let keysym
= KeyTable
.XK_Control_L
;
3568 let bmask
= 0x10; // Button mask for scroll down
3570 client
._qemuExtKeyEventSupported
= true;
3572 gestureStart('pinch', 20, 40, 90, 90);
3574 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3576 expect(qemuKeyEvent
).to
.not
.have
.been
.called
;
3578 pointerEvent
.resetHistory();
3580 gestureMove('pinch', 20, 40, 30, 30);
3582 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3583 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3585 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3587 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3590 expect(qemuKeyEvent
).to
.have
.been
.calledTwice
;
3591 expect(qemuKeyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3595 expect(qemuKeyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3600 expect(qemuKeyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3601 expect(qemuKeyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3603 pointerEvent
.resetHistory();
3604 qemuKeyEvent
.resetHistory();
3606 gestureEnd('pinch', 20, 40);
3608 expect(pointerEvent
).to
.not
.have
.been
.called
;
3609 expect(qemuKeyEvent
).to
.not
.have
.been
.called
;
3614 describe('WebSocket Events', function () {
3616 it('should do nothing if we receive an empty message and have nothing in the queue', function () {
3617 client
._normalMsg
= sinon
.spy();
3618 client
._sock
._websocket
._receiveData(new Uint8Array([]));
3619 expect(client
._normalMsg
).to
.not
.have
.been
.called
;
3622 it('should handle a message in the connected state as a normal message', function () {
3623 client
._normalMsg
= sinon
.spy();
3624 client
._sock
._websocket
._receiveData(new Uint8Array([1, 2, 3]));
3625 expect(client
._normalMsg
).to
.have
.been
.called
;
3628 it('should handle a message in any non-disconnected/failed state like an init message', function () {
3629 client
._rfbConnectionState
= 'connecting';
3630 client
._rfbInitState
= 'ProtocolVersion';
3631 client
._initMsg
= sinon
.spy();
3632 client
._sock
._websocket
._receiveData(new Uint8Array([1, 2, 3]));
3633 expect(client
._initMsg
).to
.have
.been
.called
;
3636 it('should process all normal messages directly', function () {
3637 const spy
= sinon
.spy();
3638 client
.addEventListener("bell", spy
);
3639 client
._sock
._websocket
._receiveData(new Uint8Array([0x02, 0x02]));
3640 expect(spy
).to
.have
.been
.calledTwice
;
3644 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
3645 client
= new RFB(document
.createElement('div'), 'wss://host:8675');
3647 client
._sock
._websocket
._open();
3648 expect(client
._rfbInitState
).to
.equal('ProtocolVersion');
3651 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
3652 sinon
.spy(client
, "_fail");
3653 client
._rfbConnectionState
= 'connected';
3654 client
._sock
._websocket
._open();
3655 expect(client
._fail
).to
.have
.been
.calledOnce
;
3659 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
3660 const real
= client
._sock
._websocket
.close
;
3661 client
._sock
._websocket
.close
= () => {};
3662 client
.disconnect();
3663 expect(client
._rfbConnectionState
).to
.equal('disconnecting');
3664 client
._sock
._websocket
.close
= real
;
3665 client
._sock
._websocket
.close();
3666 expect(client
._rfbConnectionState
).to
.equal('disconnected');
3669 it('should fail if we get a close event while connecting', function () {
3670 sinon
.spy(client
, "_fail");
3671 client
._rfbConnectionState
= 'connecting';
3672 client
._sock
._websocket
.close();
3673 expect(client
._fail
).to
.have
.been
.calledOnce
;
3676 it('should unregister close event handler', function () {
3677 sinon
.spy(client
._sock
, 'off');
3678 client
.disconnect();
3679 client
._sock
._websocket
.close();
3680 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
3683 // error events do nothing
3687 describe('Quality level setting', function () {
3688 const defaultQuality
= 6;
3692 beforeEach(function () {
3694 sinon
.spy(RFB
.messages
, "clientEncodings");
3697 afterEach(function () {
3698 RFB
.messages
.clientEncodings
.restore();
3701 it(`should equal ${defaultQuality} by default`, function () {
3702 expect(client
._qualityLevel
).to
.equal(defaultQuality
);
3705 it('should ignore non-integers when set', function () {
3706 client
.qualityLevel
= '1';
3707 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3709 RFB
.messages
.clientEncodings
.resetHistory();
3711 client
.qualityLevel
= 1.5;
3712 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3714 RFB
.messages
.clientEncodings
.resetHistory();
3716 client
.qualityLevel
= null;
3717 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3719 RFB
.messages
.clientEncodings
.resetHistory();
3721 client
.qualityLevel
= undefined;
3722 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3724 RFB
.messages
.clientEncodings
.resetHistory();
3726 client
.qualityLevel
= {};
3727 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3730 it('should ignore integers out of range [0, 9]', function () {
3731 client
.qualityLevel
= -1;
3732 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3734 RFB
.messages
.clientEncodings
.resetHistory();
3736 client
.qualityLevel
= 10;
3737 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3740 it('should send clientEncodings with new quality value', function () {
3744 client
.qualityLevel
= newQuality
;
3745 expect(client
.qualityLevel
).to
.equal(newQuality
);
3746 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3747 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3750 it('should not send clientEncodings if quality is the same', function () {
3754 client
.qualityLevel
= newQuality
;
3755 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3756 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3758 RFB
.messages
.clientEncodings
.resetHistory();
3760 client
.qualityLevel
= newQuality
;
3761 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3764 it('should not send clientEncodings if not in connected state', function () {
3767 client
._rfbConnectionState
= '';
3769 client
.qualityLevel
= newQuality
;
3770 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3772 RFB
.messages
.clientEncodings
.resetHistory();
3774 client
._rfbConnectionState
= 'connnecting';
3776 client
.qualityLevel
= newQuality
;
3777 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3779 RFB
.messages
.clientEncodings
.resetHistory();
3781 client
._rfbConnectionState
= 'connected';
3783 client
.qualityLevel
= newQuality
;
3784 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3785 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3789 describe('Compression level setting', function () {
3790 const defaultCompression
= 2;
3794 beforeEach(function () {
3796 sinon
.spy(RFB
.messages
, "clientEncodings");
3799 afterEach(function () {
3800 RFB
.messages
.clientEncodings
.restore();
3803 it(`should equal ${defaultCompression} by default`, function () {
3804 expect(client
._compressionLevel
).to
.equal(defaultCompression
);
3807 it('should ignore non-integers when set', function () {
3808 client
.compressionLevel
= '1';
3809 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3811 RFB
.messages
.clientEncodings
.resetHistory();
3813 client
.compressionLevel
= 1.5;
3814 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3816 RFB
.messages
.clientEncodings
.resetHistory();
3818 client
.compressionLevel
= null;
3819 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3821 RFB
.messages
.clientEncodings
.resetHistory();
3823 client
.compressionLevel
= undefined;
3824 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3826 RFB
.messages
.clientEncodings
.resetHistory();
3828 client
.compressionLevel
= {};
3829 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3832 it('should ignore integers out of range [0, 9]', function () {
3833 client
.compressionLevel
= -1;
3834 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3836 RFB
.messages
.clientEncodings
.resetHistory();
3838 client
.compressionLevel
= 10;
3839 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3842 it('should send clientEncodings with new compression value', function () {
3846 client
.compressionLevel
= newCompression
;
3847 expect(client
.compressionLevel
).to
.equal(newCompression
);
3848 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3849 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3852 it('should not send clientEncodings if compression is the same', function () {
3856 client
.compressionLevel
= newCompression
;
3857 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3858 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3860 RFB
.messages
.clientEncodings
.resetHistory();
3862 client
.compressionLevel
= newCompression
;
3863 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3866 it('should not send clientEncodings if not in connected state', function () {
3869 client
._rfbConnectionState
= '';
3871 client
.compressionLevel
= newCompression
;
3872 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3874 RFB
.messages
.clientEncodings
.resetHistory();
3876 client
._rfbConnectionState
= 'connnecting';
3878 client
.compressionLevel
= newCompression
;
3879 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3881 RFB
.messages
.clientEncodings
.resetHistory();
3883 client
._rfbConnectionState
= 'connected';
3885 client
.compressionLevel
= newCompression
;
3886 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3887 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3892 describe('RFB messages', function () {
3895 before(function () {
3896 FakeWebSocket
.replace();
3897 sock
= new Websock();
3902 FakeWebSocket
.restore();
3905 describe('Extended Clipboard Handling Send', function () {
3906 beforeEach(function () {
3907 sinon
.spy(RFB
.messages
, 'clientCutText');
3910 afterEach(function () {
3911 RFB
.messages
.clientCutText
.restore();
3914 it('should call clientCutText with correct Caps data', function () {
3919 let expectedData
= new Uint8Array([0x1F, 0x00, 0x00, 0x05,
3920 0x00, 0x00, 0x00, 0x02,
3921 0x00, 0x00, 0x10, 0x19]);
3930 RFB
.messages
.extendedClipboardCaps(sock
, actions
, formats
);
3931 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3932 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3935 it('should call clientCutText with correct Request data', function () {
3936 let formats
= new Uint8Array([0x01]);
3937 let expectedData
= new Uint8Array([0x02, 0x00, 0x00, 0x01]);
3939 RFB
.messages
.extendedClipboardRequest(sock
, formats
);
3940 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3941 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3944 it('should call clientCutText with correct Notify data', function () {
3945 let formats
= new Uint8Array([0x01]);
3946 let expectedData
= new Uint8Array([0x08, 0x00, 0x00, 0x01]);
3948 RFB
.messages
.extendedClipboardNotify(sock
, formats
);
3949 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3950 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3953 it('should call clientCutText with correct Provide data', function () {
3954 let testText
= "Test string";
3955 let expectedText
= encodeUTF8(testText
+ "\0");
3957 let deflatedData
= deflateWithSize(expectedText
);
3959 // Build Expected with flags and deflated data
3960 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3961 expectedData
[0] = 0x10; // The client capabilities
3962 expectedData
[1] = 0x00; // Reserved flags
3963 expectedData
[2] = 0x00; // Reserved flags
3964 expectedData
[3] = 0x01; // The formats client supports
3965 expectedData
.set(deflatedData
, 4);
3967 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3968 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3969 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3973 describe('End of line characters', function () {
3974 it('Carriage return', function () {
3976 let testText
= "Hello\rworld\r\r!";
3977 let expectedText
= encodeUTF8("Hello\r\nworld\r\n\r\n!\0");
3979 let deflatedData
= deflateWithSize(expectedText
);
3981 // Build Expected with flags and deflated data
3982 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3983 expectedData
[0] = 0x10; // The client capabilities
3984 expectedData
[1] = 0x00; // Reserved flags
3985 expectedData
[2] = 0x00; // Reserved flags
3986 expectedData
[3] = 0x01; // The formats client supports
3987 expectedData
.set(deflatedData
, 4);
3989 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3990 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3991 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3994 it('Carriage return Line feed', function () {
3996 let testText
= "Hello\r\n\r\nworld\r\n!";
3997 let expectedText
= encodeUTF8(testText
+ "\0");
3999 let deflatedData
= deflateWithSize(expectedText
);
4001 // Build Expected with flags and deflated data
4002 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
4003 expectedData
[0] = 0x10; // The client capabilities
4004 expectedData
[1] = 0x00; // Reserved flags
4005 expectedData
[2] = 0x00; // Reserved flags
4006 expectedData
[3] = 0x01; // The formats client supports
4007 expectedData
.set(deflatedData
, 4);
4009 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
4010 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
4011 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
4014 it('Line feed', function () {
4015 let testText
= "Hello\n\n\nworld\n!";
4016 let expectedText
= encodeUTF8("Hello\r\n\r\n\r\nworld\r\n!\0");
4018 let deflatedData
= deflateWithSize(expectedText
);
4020 // Build Expected with flags and deflated data
4021 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
4022 expectedData
[0] = 0x10; // The client capabilities
4023 expectedData
[1] = 0x00; // Reserved flags
4024 expectedData
[2] = 0x00; // Reserved flags
4025 expectedData
[3] = 0x01; // The formats client supports
4026 expectedData
.set(deflatedData
, 4);
4028 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
4029 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
4030 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
4033 it('Carriage return and Line feed mixed', function () {
4034 let testText
= "\rHello\r\n\rworld\n\n!";
4035 let expectedText
= encodeUTF8("\r\nHello\r\n\r\nworld\r\n\r\n!\0");
4037 let deflatedData
= deflateWithSize(expectedText
);
4039 // Build Expected with flags and deflated data
4040 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
4041 expectedData
[0] = 0x10; // The client capabilities
4042 expectedData
[1] = 0x00; // Reserved flags
4043 expectedData
[2] = 0x00; // Reserved flags
4044 expectedData
[3] = 0x01; // The formats client supports
4045 expectedData
.set(deflatedData
, 4);
4047 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
4048 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
4049 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);