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 actually connect to the websocket', function () {
155 new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH');
156 expect(open
).to
.have
.been
.calledOnceWithExactly('ws://HOST:8675/PATH', []);
159 it('should pass on connection problems', function () {
161 open
= sinon
.stub(Websock
.prototype, 'open');
162 open
.throws(new Error('Failure'));
163 expect(() => new RFB(document
.createElement('div'), 'ws://HOST:8675/PATH')).to
.throw('Failure');
166 it('should handle WebSocket/RTCDataChannel objects', function () {
167 let sock
= new FakeWebSocket('ws://HOST:8675/PATH', []);
168 new RFB(document
.createElement('div'), sock
);
169 expect(open
).to
.not
.have
.been
.called
;
170 expect(attach
).to
.have
.been
.calledOnceWithExactly(sock
);
173 it('should handle already open WebSocket/RTCDataChannel objects', function () {
174 let sock
= new FakeWebSocket('ws://HOST:8675/PATH', []);
176 const client
= new RFB(document
.createElement('div'), sock
);
177 let callback
= sinon
.spy();
178 client
.addEventListener('disconnect', callback
);
179 expect(open
).to
.not
.have
.been
.called
;
180 expect(attach
).to
.have
.been
.calledOnceWithExactly(sock
);
181 // Check if it is ready for some data
182 sock
._receiveData(new Uint8Array(['R', 'F', 'B', '0', '0', '3', '0', '0', '8']));
183 expect(callback
).to
.not
.have
.been
.called
;
186 it('should refuse closed WebSocket/RTCDataChannel objects', function () {
187 let sock
= new FakeWebSocket('ws://HOST:8675/PATH', []);
188 sock
.readyState
= WebSocket
.CLOSED
;
189 expect(() => new RFB(document
.createElement('div'), sock
)).to
.throw();
192 it('should pass on attach problems', function () {
194 attach
= sinon
.stub(Websock
.prototype, 'attach');
195 attach
.throws(new Error('Failure'));
196 let sock
= new FakeWebSocket('ws://HOST:8675/PATH', []);
197 expect(() => new RFB(document
.createElement('div'), sock
)).to
.throw('Failure');
201 describe('#disconnect', function () {
205 beforeEach(function () {
207 close
= sinon
.stub(Websock
.prototype, "close");
209 afterEach(function () {
213 it('should start closing WebSocket', function () {
214 let callback
= sinon
.spy();
215 client
.addEventListener('disconnect', callback
);
217 expect(close
).to
.have
.been
.calledOnceWithExactly();
218 expect(callback
).to
.not
.have
.been
.called
;
221 it('should send disconnect event', function () {
222 let callback
= sinon
.spy();
223 client
.addEventListener('disconnect', callback
);
225 close
.thisValues
[0]._eventHandlers
.close(new CloseEvent("close", { 'code': 1000, 'reason': "", 'wasClean': true }));
226 expect(callback
).to
.have
.been
.calledOnce
;
227 expect(callback
.args
[0][0].detail
.clean
).to
.be
.true;
230 it('should force disconnect if disconnecting takes too long', function () {
231 let callback
= sinon
.spy();
232 client
.addEventListener('disconnect', callback
);
234 this.clock
.tick(3 * 1000);
235 expect(callback
).to
.have
.been
.calledOnce
;
236 expect(callback
.args
[0][0].detail
.clean
).to
.be
.true;
239 it('should not fail if disconnect completes before timeout', function () {
240 let callback
= sinon
.spy();
241 client
.addEventListener('disconnect', callback
);
243 client
._updateConnectionState('disconnecting');
244 this.clock
.tick(3 * 1000 / 2);
245 close
.thisValues
[0]._eventHandlers
.close(new CloseEvent("close", { 'code': 1000, 'reason': "", 'wasClean': true }));
246 this.clock
.tick(3 * 1000 / 2 + 1);
247 expect(callback
).to
.have
.been
.calledOnce
;
248 expect(callback
.args
[0][0].detail
.clean
).to
.be
.true;
251 it('should unregister error event handler', function () {
252 sinon
.spy(client
._sock
, 'off');
254 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
257 it('should unregister message event handler', function () {
258 sinon
.spy(client
._sock
, 'off');
260 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
263 it('should unregister open event handler', function () {
264 sinon
.spy(client
._sock
, 'off');
266 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
270 describe('#sendCredentials', function () {
272 beforeEach(function () {
274 client
._rfbConnectionState
= 'connecting';
277 it('should set the rfb credentials properly"', function () {
278 client
.sendCredentials({ password
: 'pass' });
279 expect(client
._rfbCredentials
).to
.deep
.equal({ password
: 'pass' });
282 it('should call initMsg "soon"', function () {
283 client
._initMsg
= sinon
.spy();
284 client
.sendCredentials({ password
: 'pass' });
286 expect(client
._initMsg
).to
.have
.been
.calledOnce
;
291 describe('Public API Basic Behavior', function () {
293 beforeEach(function () {
297 describe('#sendCtrlAlDel', function () {
298 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
299 const expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush
: () => {}};
300 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
301 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
302 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
303 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
304 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
305 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
307 client
.sendCtrlAltDel();
308 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
311 it('should not send the keys if we are not in a normal state', function () {
312 sinon
.spy(client
._sock
, 'flush');
313 client
._rfbConnectionState
= "connecting";
314 client
.sendCtrlAltDel();
315 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
318 it('should not send the keys if we are set as view_only', function () {
319 sinon
.spy(client
._sock
, 'flush');
320 client
._viewOnly
= true;
321 client
.sendCtrlAltDel();
322 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
326 describe('#sendKey', function () {
327 it('should send a single key with the given code and state (down = true)', function () {
328 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
329 RFB
.messages
.keyEvent(expected
, 123, 1);
330 client
.sendKey(123, 'Key123', true);
331 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
334 it('should send both a down and up event if the state is not specified', function () {
335 const expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
336 RFB
.messages
.keyEvent(expected
, 123, 1);
337 RFB
.messages
.keyEvent(expected
, 123, 0);
338 client
.sendKey(123, 'Key123');
339 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
342 it('should not send the key if we are not in a normal state', function () {
343 sinon
.spy(client
._sock
, 'flush');
344 client
._rfbConnectionState
= "connecting";
345 client
.sendKey(123, 'Key123');
346 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
349 it('should not send the key if we are set as view_only', function () {
350 sinon
.spy(client
._sock
, 'flush');
351 client
._viewOnly
= true;
352 client
.sendKey(123, 'Key123');
353 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
356 it('should send QEMU extended events if supported', function () {
357 client
._qemuExtKeyEventSupported
= true;
358 const expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush
: () => {}};
359 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
360 client
.sendKey(0x20, 'Space', true);
361 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
364 it('should not send QEMU extended events if unknown key code', function () {
365 client
._qemuExtKeyEventSupported
= true;
366 const expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
367 RFB
.messages
.keyEvent(expected
, 123, 1);
368 client
.sendKey(123, 'FooBar', true);
369 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
373 describe('#focus', function () {
374 it('should move focus to canvas object', function () {
375 client
._canvas
.focus
= sinon
.spy();
377 expect(client
._canvas
.focus
).to
.have
.been
.calledOnce
;
381 describe('#blur', function () {
382 it('should remove focus from canvas object', function () {
383 client
._canvas
.blur
= sinon
.spy();
385 expect(client
._canvas
.blur
).to
.have
.been
.calledOnce
;
389 describe('#clipboardPasteFrom', function () {
390 describe('Clipboard update handling', function () {
391 beforeEach(function () {
392 sinon
.spy(RFB
.messages
, 'clientCutText');
393 sinon
.spy(RFB
.messages
, 'extendedClipboardNotify');
396 afterEach(function () {
397 RFB
.messages
.clientCutText
.restore();
398 RFB
.messages
.extendedClipboardNotify
.restore();
401 it('should send the given text in an clipboard update', function () {
402 client
.clipboardPasteFrom('abc');
404 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
405 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(client
._sock
,
406 new Uint8Array([97, 98, 99]));
409 it('should send an notify if extended clipboard is supported by server', function () {
410 // Send our capabilities
411 let data
= [3, 0, 0, 0];
412 const flags
= [0x1F, 0x00, 0x00, 0x01];
413 let fileSizes
= [0x00, 0x00, 0x00, 0x1E];
415 push32(data
, toUnsigned32bit(-8));
416 data
= data
.concat(flags
);
417 data
= data
.concat(fileSizes
);
418 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
420 client
.clipboardPasteFrom('extended test');
421 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
425 it('should flush multiple times for large clipboards', function () {
426 sinon
.spy(client
._sock
, 'flush');
428 for (let i
= 0; i
< client
._sock
._sQbufferSize
+ 100; i
++) {
431 client
.clipboardPasteFrom(longText
);
432 expect(client
._sock
.flush
).to
.have
.been
.calledTwice
;
435 it('should not send the text if we are not in a normal state', function () {
436 sinon
.spy(client
._sock
, 'flush');
437 client
._rfbConnectionState
= "connecting";
438 client
.clipboardPasteFrom('abc');
439 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
443 describe("XVP operations", function () {
444 beforeEach(function () {
445 client
._rfbXvpVer
= 1;
448 it('should send the shutdown signal on #machineShutdown', function () {
449 client
.machineShutdown();
450 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
453 it('should send the reboot signal on #machineReboot', function () {
454 client
.machineReboot();
455 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
458 it('should send the reset signal on #machineReset', function () {
459 client
.machineReset();
460 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
463 it('should not send XVP operations with higher versions than we support', function () {
464 sinon
.spy(client
._sock
, 'flush');
466 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
471 describe('Clipping', function () {
473 beforeEach(function () {
475 container
.style
.width
= '70px';
476 container
.style
.height
= '80px';
477 client
.clipViewport
= true;
480 it('should update display clip state when changing the property', function () {
481 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
483 client
.clipViewport
= false;
484 expect(spy
.set).to
.have
.been
.calledOnce
;
485 expect(spy
.set).to
.have
.been
.calledWith(false);
486 spy
.set.resetHistory();
488 client
.clipViewport
= true;
489 expect(spy
.set).to
.have
.been
.calledOnce
;
490 expect(spy
.set).to
.have
.been
.calledWith(true);
493 it('should update the viewport when the container size changes', function () {
494 sinon
.spy(client
._display
, "viewportChangeSize");
496 container
.style
.width
= '40px';
497 container
.style
.height
= '50px';
498 const event
= new UIEvent('resize');
499 window
.dispatchEvent(event
);
502 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledOnce
;
503 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(40, 50);
506 it('should update the viewport when the remote session resizes', function () {
507 // Simple ExtendedDesktopSize FBU message
508 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
509 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
510 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
511 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
512 0x00, 0x00, 0x00, 0x00 ];
514 sinon
.spy(client
._display
, "viewportChangeSize");
516 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
518 // FIXME: Display implicitly calls viewportChangeSize() when
519 // resizing the framebuffer, hence calledTwice.
520 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledTwice
;
521 expect(client
._display
.viewportChangeSize
).to
.have
.been
.calledWith(70, 80);
524 it('should not update the viewport if not clipping', function () {
525 client
.clipViewport
= false;
526 sinon
.spy(client
._display
, "viewportChangeSize");
528 container
.style
.width
= '40px';
529 container
.style
.height
= '50px';
530 const event
= new UIEvent('resize');
531 window
.dispatchEvent(event
);
534 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
537 it('should not update the viewport if scaling', function () {
538 client
.scaleViewport
= true;
539 sinon
.spy(client
._display
, "viewportChangeSize");
541 container
.style
.width
= '40px';
542 container
.style
.height
= '50px';
543 const event
= new UIEvent('resize');
544 window
.dispatchEvent(event
);
547 expect(client
._display
.viewportChangeSize
).to
.not
.have
.been
.called
;
550 describe('Dragging', function () {
551 beforeEach(function () {
552 client
.dragViewport
= true;
553 sinon
.spy(RFB
.messages
, "pointerEvent");
556 afterEach(function () {
557 RFB
.messages
.pointerEvent
.restore();
560 it('should not send button messages when initiating viewport dragging', function () {
561 client
._handleMouseButton(13, 9, 0x001);
562 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
565 it('should send button messages when release without movement', function () {
567 client
._handleMouseButton(13, 9, 0x001);
568 client
._handleMouseButton(13, 9, 0x000);
569 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
571 RFB
.messages
.pointerEvent
.resetHistory();
574 client
._handleMouseButton(13, 9, 0x001);
575 client
._handleMouseMove(15, 14);
576 client
._handleMouseButton(15, 14, 0x000);
577 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledTwice
;
580 it('should not send button messages when in view only', function () {
581 client
._viewOnly
= true;
582 client
._handleMouseButton(13, 9, 0x001);
583 client
._handleMouseButton(13, 9, 0x000);
584 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
587 it('should send button message directly when drag is disabled', function () {
588 client
.dragViewport
= false;
589 client
._handleMouseButton(13, 9, 0x001);
590 expect(RFB
.messages
.pointerEvent
).to
.have
.been
.calledOnce
;
593 it('should be initiate viewport dragging on sufficient movement', function () {
594 sinon
.spy(client
._display
, "viewportChangePos");
596 // Too small movement
598 client
._handleMouseButton(13, 9, 0x001);
599 client
._handleMouseMove(18, 9);
601 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
602 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
604 // Sufficient movement
606 client
._handleMouseMove(43, 9);
608 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
609 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
610 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(-30, 0);
612 client
._display
.viewportChangePos
.resetHistory();
614 // Now a small movement should move right away
616 client
._handleMouseMove(43, 14);
618 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
619 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
620 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(0, -5);
623 it('should not send button messages when dragging ends', function () {
624 // First the movement
626 client
._handleMouseButton(13, 9, 0x001);
627 client
._handleMouseMove(43, 9);
628 client
._handleMouseButton(43, 9, 0x000);
630 expect(RFB
.messages
.pointerEvent
).to
.not
.have
.been
.called
;
633 it('should terminate viewport dragging on a button up event', function () {
634 // First the dragging movement
636 client
._handleMouseButton(13, 9, 0x001);
637 client
._handleMouseMove(43, 9);
638 client
._handleMouseButton(43, 9, 0x000);
640 // Another movement now should not move the viewport
642 sinon
.spy(client
._display
, "viewportChangePos");
644 client
._handleMouseMove(43, 59);
646 expect(client
._display
.viewportChangePos
).to
.not
.have
.been
.called
;
651 describe('Scaling', function () {
653 beforeEach(function () {
655 container
.style
.width
= '70px';
656 container
.style
.height
= '80px';
657 client
.scaleViewport
= true;
660 it('should update display scale factor when changing the property', function () {
661 const spy
= sinon
.spy(client
._display
, "scale", ["set"]);
662 sinon
.spy(client
._display
, "autoscale");
664 client
.scaleViewport
= false;
665 expect(spy
.set).to
.have
.been
.calledOnce
;
666 expect(spy
.set).to
.have
.been
.calledWith(1.0);
667 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
669 client
.scaleViewport
= true;
670 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
671 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
674 it('should update the clipping setting when changing the property', function () {
675 client
.clipViewport
= true;
677 const spy
= sinon
.spy(client
._display
, "clipViewport", ["set"]);
679 client
.scaleViewport
= false;
680 expect(spy
.set).to
.have
.been
.calledOnce
;
681 expect(spy
.set).to
.have
.been
.calledWith(true);
683 spy
.set.resetHistory();
685 client
.scaleViewport
= true;
686 expect(spy
.set).to
.have
.been
.calledOnce
;
687 expect(spy
.set).to
.have
.been
.calledWith(false);
690 it('should update the scaling when the container size changes', function () {
691 sinon
.spy(client
._display
, "autoscale");
693 container
.style
.width
= '40px';
694 container
.style
.height
= '50px';
695 const event
= new UIEvent('resize');
696 window
.dispatchEvent(event
);
699 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
700 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(40, 50);
703 it('should update the scaling when the remote session resizes', function () {
704 // Simple ExtendedDesktopSize FBU message
705 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
706 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
707 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
708 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
709 0x00, 0x00, 0x00, 0x00 ];
711 sinon
.spy(client
._display
, "autoscale");
713 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
715 expect(client
._display
.autoscale
).to
.have
.been
.calledOnce
;
716 expect(client
._display
.autoscale
).to
.have
.been
.calledWith(70, 80);
719 it('should not update the display scale factor if not scaling', function () {
720 client
.scaleViewport
= false;
722 sinon
.spy(client
._display
, "autoscale");
724 container
.style
.width
= '40px';
725 container
.style
.height
= '50px';
726 const event
= new UIEvent('resize');
727 window
.dispatchEvent(event
);
730 expect(client
._display
.autoscale
).to
.not
.have
.been
.called
;
734 describe('Remote resize', function () {
736 beforeEach(function () {
738 client
._supportsSetDesktopSize
= true;
739 client
.resizeSession
= true;
740 container
.style
.width
= '70px';
741 container
.style
.height
= '80px';
742 sinon
.spy(RFB
.messages
, "setDesktopSize");
745 afterEach(function () {
746 RFB
.messages
.setDesktopSize
.restore();
749 it('should only request a resize when turned on', function () {
750 client
.resizeSession
= false;
751 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
752 client
.resizeSession
= true;
753 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
756 it('should request a resize when initially connecting', function () {
757 // Simple ExtendedDesktopSize FBU message
758 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
759 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
760 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
761 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
762 0x00, 0x00, 0x00, 0x00 ];
764 // First message should trigger a resize
766 client
._supportsSetDesktopSize
= false;
768 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
770 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
771 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 70, 80, 0, 0);
773 RFB
.messages
.setDesktopSize
.resetHistory();
775 // Second message should not trigger a resize
777 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
779 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
782 it('should request a resize when the container resizes', function () {
783 container
.style
.width
= '40px';
784 container
.style
.height
= '50px';
785 const event
= new UIEvent('resize');
786 window
.dispatchEvent(event
);
789 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
790 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
793 it('should not resize until the container size is stable', function () {
794 container
.style
.width
= '20px';
795 container
.style
.height
= '30px';
796 const event1
= new UIEvent('resize');
797 window
.dispatchEvent(event1
);
800 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
802 container
.style
.width
= '40px';
803 container
.style
.height
= '50px';
804 const event2
= new UIEvent('resize');
805 window
.dispatchEvent(event2
);
808 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
812 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledOnce
;
813 expect(RFB
.messages
.setDesktopSize
).to
.have
.been
.calledWith(sinon
.match
.object
, 40, 50, 0, 0);
816 it('should not resize when resize is disabled', function () {
817 client
._resizeSession
= false;
819 container
.style
.width
= '40px';
820 container
.style
.height
= '50px';
821 const event
= new UIEvent('resize');
822 window
.dispatchEvent(event
);
825 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
828 it('should not resize when resize is not supported', function () {
829 client
._supportsSetDesktopSize
= false;
831 container
.style
.width
= '40px';
832 container
.style
.height
= '50px';
833 const event
= new UIEvent('resize');
834 window
.dispatchEvent(event
);
837 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
840 it('should not resize when in view only mode', function () {
841 client
._viewOnly
= true;
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 try to override a server resize', function () {
853 // Simple ExtendedDesktopSize FBU message
854 const incoming
= [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
855 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
856 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
857 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
858 0x00, 0x00, 0x00, 0x00 ];
860 client
._sock
._websocket
._receiveData(new Uint8Array(incoming
));
862 expect(RFB
.messages
.setDesktopSize
).to
.not
.have
.been
.called
;
866 describe('Misc Internals', function () {
867 describe('#_fail', function () {
869 beforeEach(function () {
873 it('should close the WebSocket connection', function () {
874 sinon
.spy(client
._sock
, 'close');
876 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
879 it('should transition to disconnected', function () {
880 sinon
.spy(client
, '_updateConnectionState');
882 this.clock
.tick(2000);
883 expect(client
._updateConnectionState
).to
.have
.been
.called
;
884 expect(client
._rfbConnectionState
).to
.equal('disconnected');
887 it('should set clean_disconnect variable', function () {
888 client
._rfbCleanDisconnect
= true;
889 client
._rfbConnectionState
= 'connected';
891 expect(client
._rfbCleanDisconnect
).to
.be
.false;
894 it('should result in disconnect event with clean set to false', function () {
895 client
._rfbConnectionState
= 'connected';
896 const spy
= sinon
.spy();
897 client
.addEventListener("disconnect", spy
);
899 this.clock
.tick(2000);
900 expect(spy
).to
.have
.been
.calledOnce
;
901 expect(spy
.args
[0][0].detail
.clean
).to
.be
.false;
907 describe('Protocol Initialization States', function () {
909 beforeEach(function () {
911 client
._rfbConnectionState
= 'connecting';
914 describe('ProtocolVersion', function () {
915 function sendVer(ver
, client
) {
916 const arr
= new Uint8Array(12);
917 for (let i
= 0; i
< ver
.length
; i
++) {
918 arr
[i
+4] = ver
.charCodeAt(i
);
920 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
922 client
._sock
._websocket
._receiveData(arr
);
925 describe('version parsing', function () {
926 it('should interpret version 003.003 as version 3.3', function () {
927 sendVer('003.003', client
);
928 expect(client
._rfbVersion
).to
.equal(3.3);
931 it('should interpret version 003.006 as version 3.3', function () {
932 sendVer('003.006', client
);
933 expect(client
._rfbVersion
).to
.equal(3.3);
936 it('should interpret version 003.889 as version 3.3', function () {
937 sendVer('003.889', client
);
938 expect(client
._rfbVersion
).to
.equal(3.3);
941 it('should interpret version 003.007 as version 3.7', function () {
942 sendVer('003.007', client
);
943 expect(client
._rfbVersion
).to
.equal(3.7);
946 it('should interpret version 003.008 as version 3.8', function () {
947 sendVer('003.008', client
);
948 expect(client
._rfbVersion
).to
.equal(3.8);
951 it('should interpret version 004.000 as version 3.8', function () {
952 sendVer('004.000', client
);
953 expect(client
._rfbVersion
).to
.equal(3.8);
956 it('should interpret version 004.001 as version 3.8', function () {
957 sendVer('004.001', client
);
958 expect(client
._rfbVersion
).to
.equal(3.8);
961 it('should interpret version 005.000 as version 3.8', function () {
962 sendVer('005.000', client
);
963 expect(client
._rfbVersion
).to
.equal(3.8);
966 it('should fail on an invalid version', function () {
967 sinon
.spy(client
, "_fail");
968 sendVer('002.000', client
);
969 expect(client
._fail
).to
.have
.been
.calledOnce
;
973 it('should send back the interpreted version', function () {
974 sendVer('004.000', client
);
976 const expectedStr
= 'RFB 003.008\n';
978 for (let i
= 0; i
< expectedStr
.length
; i
++) {
979 expected
[i
] = expectedStr
.charCodeAt(i
);
982 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
985 it('should transition to the Security state on successful negotiation', function () {
986 sendVer('003.008', client
);
987 expect(client
._rfbInitState
).to
.equal('Security');
990 describe('Repeater', function () {
991 beforeEach(function () {
992 client
= makeRFB('wss://host:8675', { repeaterID
: "12345" });
993 client
._rfbConnectionState
= 'connecting';
996 it('should interpret version 000.000 as a repeater', function () {
997 sendVer('000.000', client
);
998 expect(client
._rfbVersion
).to
.equal(0);
1000 const sentData
= client
._sock
._websocket
._getSentData();
1001 expect(new Uint8Array(sentData
.buffer
, 0, 9)).to
.array
.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
1002 expect(sentData
).to
.have
.length(250);
1005 it('should handle two step repeater negotiation', function () {
1006 sendVer('000.000', client
);
1007 sendVer('003.008', client
);
1008 expect(client
._rfbVersion
).to
.equal(3.8);
1013 describe('Security', function () {
1014 beforeEach(function () {
1015 client
._rfbInitState
= 'Security';
1018 it('should simply receive the auth scheme when for versions < 3.7', function () {
1019 client
._rfbVersion
= 3.6;
1020 const authSchemeRaw
= [1, 2, 3, 4];
1021 const authScheme
= (authSchemeRaw
[0] << 24) + (authSchemeRaw
[1] << 16) +
1022 (authSchemeRaw
[2] << 8) + authSchemeRaw
[3];
1023 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemeRaw
));
1024 expect(client
._rfbAuthScheme
).to
.equal(authScheme
);
1027 it('should prefer no authentication is possible', function () {
1028 client
._rfbVersion
= 3.7;
1029 const authSchemes
= [2, 1, 3];
1030 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1031 expect(client
._rfbAuthScheme
).to
.equal(1);
1032 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
1035 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
1036 client
._rfbVersion
= 3.7;
1037 const authSchemes
= [2, 22, 16];
1038 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1039 expect(client
._rfbAuthScheme
).to
.equal(22);
1040 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
1043 it('should fail if there are no supported schemes for versions >= 3.7', function () {
1044 sinon
.spy(client
, "_fail");
1045 client
._rfbVersion
= 3.7;
1046 const authSchemes
= [1, 32];
1047 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1048 expect(client
._fail
).to
.have
.been
.calledOnce
;
1051 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
1052 client
._rfbVersion
= 3.7;
1053 const failureData
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1054 sinon
.spy(client
, '_fail');
1055 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1057 expect(client
._fail
).to
.have
.been
.calledOnce
;
1058 expect(client
._fail
).to
.have
.been
.calledWith(
1059 'Security negotiation failed on no security types (reason: whoops)');
1062 it('should transition to the Authentication state and continue on successful negotiation', function () {
1063 client
._rfbVersion
= 3.7;
1064 const authSchemes
= [1, 1];
1065 client
._negotiateAuthentication
= sinon
.spy();
1066 client
._sock
._websocket
._receiveData(new Uint8Array(authSchemes
));
1067 expect(client
._rfbInitState
).to
.equal('Authentication');
1068 expect(client
._negotiateAuthentication
).to
.have
.been
.calledOnce
;
1072 describe('Authentication', function () {
1073 beforeEach(function () {
1074 client
._rfbInitState
= 'Security';
1077 function sendSecurity(type
, cl
) {
1078 cl
._sock
._websocket
._receiveData(new Uint8Array([1, type
]));
1081 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
1082 client
._rfbVersion
= 3.6;
1083 const errMsg
= "Whoopsies";
1084 const data
= [0, 0, 0, 0];
1085 const errLen
= errMsg
.length
;
1086 push32(data
, errLen
);
1087 for (let i
= 0; i
< errLen
; i
++) {
1088 data
.push(errMsg
.charCodeAt(i
));
1091 sinon
.spy(client
, '_fail');
1092 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1093 expect(client
._fail
).to
.have
.been
.calledWith(
1094 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
1097 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
1098 client
._rfbVersion
= 3.8;
1099 sendSecurity(1, client
);
1100 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1103 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
1104 client
._rfbVersion
= 3.7;
1105 sendSecurity(1, client
);
1106 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1109 it('should fail on an unknown auth scheme', function () {
1110 sinon
.spy(client
, "_fail");
1111 client
._rfbVersion
= 3.8;
1112 sendSecurity(57, client
);
1113 expect(client
._fail
).to
.have
.been
.calledOnce
;
1116 describe('VNC Authentication (type 2) Handler', function () {
1117 beforeEach(function () {
1118 client
._rfbInitState
= 'Security';
1119 client
._rfbVersion
= 3.8;
1122 it('should fire the credentialsrequired event if missing a password', function () {
1123 const spy
= sinon
.spy();
1124 client
.addEventListener("credentialsrequired", spy
);
1125 sendSecurity(2, client
);
1127 const challenge
= [];
1128 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1129 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1131 expect(client
._rfbCredentials
).to
.be
.empty
;
1132 expect(spy
).to
.have
.been
.calledOnce
;
1133 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["password"]);
1136 it('should encrypt the password with DES and then send it back', function () {
1137 client
._rfbCredentials
= { password
: 'passwd' };
1138 sendSecurity(2, client
);
1139 client
._sock
._websocket
._getSentData(); // skip the choice of auth reply
1141 const challenge
= [];
1142 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1143 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1145 const desPass
= RFB
.genDES('passwd', challenge
);
1146 expect(client
._sock
).to
.have
.sent(new Uint8Array(desPass
));
1149 it('should transition to SecurityResult immediately after sending the password', function () {
1150 client
._rfbCredentials
= { password
: 'passwd' };
1151 sendSecurity(2, client
);
1153 const challenge
= [];
1154 for (let i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
1155 client
._sock
._websocket
._receiveData(new Uint8Array(challenge
));
1157 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1161 describe('XVP Authentication (type 22) Handler', function () {
1162 beforeEach(function () {
1163 client
._rfbInitState
= 'Security';
1164 client
._rfbVersion
= 3.8;
1167 it('should fall through to standard VNC authentication upon completion', function () {
1168 client
._rfbCredentials
= { username
: 'user',
1170 password
: 'password' };
1171 client
._negotiateStdVNCAuth
= sinon
.spy();
1172 sendSecurity(22, client
);
1173 expect(client
._negotiateStdVNCAuth
).to
.have
.been
.calledOnce
;
1176 it('should fire the credentialsrequired event if all credentials are missing', function () {
1177 const spy
= sinon
.spy();
1178 client
.addEventListener("credentialsrequired", spy
);
1179 client
._rfbCredentials
= {};
1180 sendSecurity(22, client
);
1182 expect(client
._rfbCredentials
).to
.be
.empty
;
1183 expect(spy
).to
.have
.been
.calledOnce
;
1184 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1187 it('should fire the credentialsrequired event if some credentials are missing', function () {
1188 const spy
= sinon
.spy();
1189 client
.addEventListener("credentialsrequired", spy
);
1190 client
._rfbCredentials
= { username
: 'user',
1192 sendSecurity(22, client
);
1194 expect(spy
).to
.have
.been
.calledOnce
;
1195 expect(spy
.args
[0][0].detail
.types
).to
.have
.members(["username", "password", "target"]);
1198 it('should send user and target separately', function () {
1199 client
._rfbCredentials
= { username
: 'user',
1201 password
: 'password' };
1202 client
._negotiateStdVNCAuth
= sinon
.spy();
1204 sendSecurity(22, client
);
1206 const expected
= [22, 4, 6]; // auth selection, len user, len target
1207 for (let i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
1209 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
1213 describe('TightVNC Authentication (type 16) Handler', function () {
1214 beforeEach(function () {
1215 client
._rfbInitState
= 'Security';
1216 client
._rfbVersion
= 3.8;
1217 sendSecurity(16, client
);
1218 client
._sock
._websocket
._getSentData(); // skip the security reply
1221 function sendNumStrPairs(pairs
, client
) {
1223 push32(data
, pairs
.length
);
1225 for (let i
= 0; i
< pairs
.length
; i
++) {
1226 push32(data
, pairs
[i
][0]);
1227 for (let j
= 0; j
< 4; j
++) {
1228 data
.push(pairs
[i
][1].charCodeAt(j
));
1230 for (let j
= 0; j
< 8; j
++) {
1231 data
.push(pairs
[i
][2].charCodeAt(j
));
1235 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1238 it('should skip tunnel negotiation if no tunnels are requested', function () {
1239 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1240 expect(client
._rfbTightVNC
).to
.be
.true;
1243 it('should fail if no supported tunnels are listed', function () {
1244 sinon
.spy(client
, "_fail");
1245 sendNumStrPairs([[123, 'OTHR', 'SOMETHNG']], client
);
1246 expect(client
._fail
).to
.have
.been
.calledOnce
;
1249 it('should choose the notunnel tunnel type', function () {
1250 sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
1251 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1254 it('should choose the notunnel tunnel type for Siemens devices', function () {
1255 sendNumStrPairs([[1, 'SICR', 'SCHANNEL'], [2, 'SICR', 'SCHANLPW']], client
);
1256 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
1259 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
1260 sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client
);
1261 client
._sock
._websocket
._getSentData(); // skip the tunnel choice here
1262 sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client
);
1263 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1264 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1267 /*it('should attempt to use VNC auth over no auth when possible', function () {
1268 client._rfbTightVNC = true;
1269 client._negotiateStdVNCAuth = sinon.spy();
1270 sendNumStrPairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
1271 expect(client._sock).to.have.sent([0, 0, 0, 1]);
1272 expect(client._negotiateStdVNCAuth).to.have.been.calledOnce;
1273 expect(client._rfbAuthScheme).to.equal(2);
1274 });*/ // while this would make sense, the original code doesn't actually do this
1276 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
1277 client
._rfbTightVNC
= true;
1278 sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client
);
1279 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
1280 expect(client
._rfbInitState
).to
.equal('SecurityResult');
1283 it('should accept VNC authentication and transition to that', function () {
1284 client
._rfbTightVNC
= true;
1285 client
._negotiateStdVNCAuth
= sinon
.spy();
1286 sendNumStrPairs([[2, 'STDV', 'VNCAUTH__']], client
);
1287 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1288 expect(client
._negotiateStdVNCAuth
).to
.have
.been
.calledOnce
;
1289 expect(client
._rfbAuthScheme
).to
.equal(2);
1292 it('should fail if there are no supported auth types', function () {
1293 sinon
.spy(client
, "_fail");
1294 client
._rfbTightVNC
= true;
1295 sendNumStrPairs([[23, 'stdv', 'badval__']], client
);
1296 expect(client
._fail
).to
.have
.been
.calledOnce
;
1300 describe('VeNCrypt Authentication (type 19) Handler', function () {
1301 beforeEach(function () {
1302 client
._rfbInitState
= 'Security';
1303 client
._rfbVersion
= 3.8;
1304 sendSecurity(19, client
);
1305 expect(client
._sock
).to
.have
.sent(new Uint8Array([19]));
1308 it('should fail with non-0.2 versions', function () {
1309 sinon
.spy(client
, "_fail");
1310 client
._sock
._websocket
._receiveData(new Uint8Array([0, 1]));
1311 expect(client
._fail
).to
.have
.been
.calledOnce
;
1314 it('should fail if the Plain authentication is not present', function () {
1316 client
._sock
._websocket
._receiveData(new Uint8Array([0, 2]));
1317 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 2]));
1319 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1320 // Subtype list, only list subtype 1.
1321 sinon
.spy(client
, "_fail");
1322 client
._sock
._websocket
._receiveData(new Uint8Array([1, 0, 0, 0, 1]));
1323 expect(client
._fail
).to
.have
.been
.calledOnce
;
1326 it('should support Plain authentication', function () {
1327 client
._rfbCredentials
= { username
: 'username', password
: 'password' };
1329 client
._sock
._websocket
._receiveData(new Uint8Array([0, 2]));
1330 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 2]));
1332 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1334 client
._sock
._websocket
._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
1336 const expectedResponse
= [];
1337 push32(expectedResponse
, 256); // Chosen subtype.
1338 push32(expectedResponse
, client
._rfbCredentials
.username
.length
);
1339 push32(expectedResponse
, client
._rfbCredentials
.password
.length
);
1340 pushString(expectedResponse
, client
._rfbCredentials
.username
);
1341 pushString(expectedResponse
, client
._rfbCredentials
.password
);
1342 expect(client
._sock
).to
.have
.sent(new Uint8Array(expectedResponse
));
1344 client
._initMsg
= sinon
.spy();
1345 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1346 expect(client
._initMsg
).to
.have
.been
.called
;
1349 it('should support Plain authentication with an empty password', function () {
1350 client
._rfbCredentials
= { username
: 'username', password
: '' };
1352 client
._sock
._websocket
._receiveData(new Uint8Array([0, 2]));
1353 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 2]));
1355 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1357 client
._sock
._websocket
._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
1359 const expectedResponse
= [];
1360 push32(expectedResponse
, 256); // Chosen subtype.
1361 push32(expectedResponse
, client
._rfbCredentials
.username
.length
);
1362 push32(expectedResponse
, client
._rfbCredentials
.password
.length
);
1363 pushString(expectedResponse
, client
._rfbCredentials
.username
);
1364 pushString(expectedResponse
, client
._rfbCredentials
.password
);
1365 expect(client
._sock
).to
.have
.sent(new Uint8Array(expectedResponse
));
1367 client
._initMsg
= sinon
.spy();
1368 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1369 expect(client
._initMsg
).to
.have
.been
.called
;
1372 it('should support Plain authentication with a very long username and password', function () {
1373 client
._rfbCredentials
= { username
: 'a'.repeat(300), password
: 'a'.repeat(300) };
1375 client
._sock
._websocket
._receiveData(new Uint8Array([0, 2]));
1376 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 2]));
1378 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1380 client
._sock
._websocket
._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
1382 const expectedResponse
= [];
1383 push32(expectedResponse
, 256); // Chosen subtype.
1384 push32(expectedResponse
, client
._rfbCredentials
.username
.length
);
1385 push32(expectedResponse
, client
._rfbCredentials
.password
.length
);
1386 pushString(expectedResponse
, client
._rfbCredentials
.username
);
1387 pushString(expectedResponse
, client
._rfbCredentials
.password
);
1388 expect(client
._sock
).to
.have
.sent(new Uint8Array(expectedResponse
));
1390 client
._initMsg
= sinon
.spy();
1391 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1392 expect(client
._initMsg
).to
.have
.been
.called
;
1397 describe('SecurityResult', function () {
1398 beforeEach(function () {
1399 client
._rfbInitState
= 'SecurityResult';
1402 it('should fall through to ServerInitialisation on a response code of 0', function () {
1403 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1404 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1407 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1408 client
._rfbVersion
= 3.8;
1409 sinon
.spy(client
, '_fail');
1410 const failureData
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1411 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1412 expect(client
._fail
).to
.have
.been
.calledWith(
1413 'Security negotiation failed on security result (reason: whoops)');
1416 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1417 sinon
.spy(client
, '_fail');
1418 client
._rfbVersion
= 3.7;
1419 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 1]));
1420 expect(client
._fail
).to
.have
.been
.calledWith(
1421 'Security handshake failed');
1424 it('should result in securityfailure event when receiving a non zero status', function () {
1425 const spy
= sinon
.spy();
1426 client
.addEventListener("securityfailure", spy
);
1427 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 2]));
1428 expect(spy
).to
.have
.been
.calledOnce
;
1429 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1432 it('should include reason when provided in securityfailure event', function () {
1433 client
._rfbVersion
= 3.8;
1434 const spy
= sinon
.spy();
1435 client
.addEventListener("securityfailure", spy
);
1436 const failureData
= [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
1437 32, 102, 97, 105, 108, 117, 114, 101];
1438 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1439 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1440 expect(spy
.args
[0][0].detail
.reason
).to
.equal('such failure');
1443 it('should not include reason when length is zero in securityfailure event', function () {
1444 client
._rfbVersion
= 3.9;
1445 const spy
= sinon
.spy();
1446 client
.addEventListener("securityfailure", spy
);
1447 const failureData
= [0, 0, 0, 1, 0, 0, 0, 0];
1448 client
._sock
._websocket
._receiveData(new Uint8Array(failureData
));
1449 expect(spy
.args
[0][0].detail
.status
).to
.equal(1);
1450 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1453 it('should not include reason in securityfailure event for version < 3.8', function () {
1454 client
._rfbVersion
= 3.6;
1455 const spy
= sinon
.spy();
1456 client
.addEventListener("securityfailure", spy
);
1457 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 2]));
1458 expect(spy
.args
[0][0].detail
.status
).to
.equal(2);
1459 expect('reason' in spy
.args
[0][0].detail
).to
.be
.false;
1463 describe('ClientInitialisation', function () {
1464 it('should transition to the ServerInitialisation state', function () {
1465 const client
= makeRFB();
1466 client
._rfbConnectionState
= 'connecting';
1467 client
._rfbInitState
= 'SecurityResult';
1468 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1469 expect(client
._rfbInitState
).to
.equal('ServerInitialisation');
1472 it('should send 1 if we are in shared mode', function () {
1473 const client
= makeRFB('wss://host:8675', { shared
: true });
1474 client
._rfbConnectionState
= 'connecting';
1475 client
._rfbInitState
= 'SecurityResult';
1476 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1477 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1480 it('should send 0 if we are not in shared mode', function () {
1481 const client
= makeRFB('wss://host:8675', { shared
: false });
1482 client
._rfbConnectionState
= 'connecting';
1483 client
._rfbInitState
= 'SecurityResult';
1484 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 0]));
1485 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1489 describe('ServerInitialisation', function () {
1490 beforeEach(function () {
1491 client
._rfbInitState
= 'ServerInitialisation';
1494 function sendServerInit(opts
, client
) {
1495 const fullOpts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, bigEndian
: 0,
1496 trueColor
: 1, redMax
: 255, greenMax
: 255, blueMax
: 255,
1497 redShift
: 16, greenShift
: 8, blueShift
: 0, name
: 'a name' };
1498 for (let opt
in opts
) {
1499 fullOpts
[opt
] = opts
[opt
];
1503 push16(data
, fullOpts
.width
);
1504 push16(data
, fullOpts
.height
);
1506 data
.push(fullOpts
.bpp
);
1507 data
.push(fullOpts
.depth
);
1508 data
.push(fullOpts
.bigEndian
);
1509 data
.push(fullOpts
.trueColor
);
1511 push16(data
, fullOpts
.redMax
);
1512 push16(data
, fullOpts
.greenMax
);
1513 push16(data
, fullOpts
.blueMax
);
1514 push8(data
, fullOpts
.redShift
);
1515 push8(data
, fullOpts
.greenShift
);
1516 push8(data
, fullOpts
.blueShift
);
1523 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1525 const nameData
= [];
1527 pushString(nameData
, fullOpts
.name
);
1528 push32(nameLen
, nameData
.length
);
1530 client
._sock
._websocket
._receiveData(new Uint8Array(nameLen
));
1531 client
._sock
._websocket
._receiveData(new Uint8Array(nameData
));
1534 it('should set the framebuffer width and height', function () {
1535 sendServerInit({ width
: 32, height
: 84 }, client
);
1536 expect(client
._fbWidth
).to
.equal(32);
1537 expect(client
._fbHeight
).to
.equal(84);
1540 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1542 it('should set the framebuffer name and call the callback', function () {
1543 const spy
= sinon
.spy();
1544 client
.addEventListener("desktopname", spy
);
1545 sendServerInit({ name
: 'som€ nam€' }, client
);
1547 expect(client
._fbName
).to
.equal('som€ nam€');
1548 expect(spy
).to
.have
.been
.calledOnce
;
1549 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
1552 it('should handle the extended init message of the tight encoding', function () {
1553 // NB(sross): we don't actually do anything with it, so just test that we can
1554 // read it w/o throwing an error
1555 client
._rfbTightVNC
= true;
1556 sendServerInit({}, client
);
1558 const tightData
= [];
1559 push16(tightData
, 1);
1560 push16(tightData
, 2);
1561 push16(tightData
, 3);
1562 push16(tightData
, 0);
1563 for (let i
= 0; i
< 16 + 32 + 48; i
++) {
1566 client
._sock
._websocket
._receiveData(new Uint8Array(tightData
));
1568 expect(client
._rfbConnectionState
).to
.equal('connected');
1571 it('should resize the display', function () {
1572 sinon
.spy(client
._display
, 'resize');
1573 sendServerInit({ width
: 27, height
: 32 }, client
);
1575 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1576 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1579 it('should grab the keyboard', function () {
1580 sinon
.spy(client
._keyboard
, 'grab');
1581 sendServerInit({}, client
);
1582 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1585 describe('Initial Update Request', function () {
1586 beforeEach(function () {
1587 sinon
.spy(RFB
.messages
, "pixelFormat");
1588 sinon
.spy(RFB
.messages
, "clientEncodings");
1589 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1592 afterEach(function () {
1593 RFB
.messages
.pixelFormat
.restore();
1594 RFB
.messages
.clientEncodings
.restore();
1595 RFB
.messages
.fbUpdateRequest
.restore();
1598 // TODO(directxman12): test the various options in this configuration matrix
1599 it('should reply with the pixel format, client encodings, and initial update request', function () {
1600 sendServerInit({ width
: 27, height
: 32 }, client
);
1602 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1603 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1604 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1605 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1606 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1607 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1608 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1609 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1612 it('should reply with restricted settings for Intel AMT servers', function () {
1613 sendServerInit({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1615 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1616 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1617 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1618 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1619 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1620 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1621 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1622 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1623 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1627 it('should send the "connect" event', function () {
1628 let spy
= sinon
.spy();
1629 client
.addEventListener('connect', spy
);
1630 sendServerInit({}, client
);
1631 expect(spy
).to
.have
.been
.calledOnce
;
1636 describe('Protocol Message Processing After Completing Initialization', function () {
1639 beforeEach(function () {
1641 client
._fbName
= 'some device';
1642 client
._fbWidth
= 640;
1643 client
._fbHeight
= 20;
1646 describe('Framebuffer Update Handling', function () {
1647 function sendFbuMsg(rectInfo
, rectData
, client
, rectCnt
) {
1650 if (!rectCnt
|| rectCnt
> -1) {
1652 data
.push(0); // msg type
1653 data
.push(0); // padding
1654 push16(data
, rectCnt
|| rectData
.length
);
1657 for (let i
= 0; i
< rectData
.length
; i
++) {
1659 push16(data
, rectInfo
[i
].x
);
1660 push16(data
, rectInfo
[i
].y
);
1661 push16(data
, rectInfo
[i
].width
);
1662 push16(data
, rectInfo
[i
].height
);
1663 push32(data
, rectInfo
[i
].encoding
);
1665 data
= data
.concat(rectData
[i
]);
1668 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
1671 it('should send an update request if there is sufficient data', function () {
1672 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1673 RFB
.messages
.fbUpdateRequest(expectedMsg
, true, 0, 0, 640, 20);
1675 client
._framebufferUpdate
= () => true;
1676 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1678 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
1681 it('should not send an update request if we need more data', function () {
1682 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1683 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1686 it('should resume receiving an update if we previously did not have enough data', function () {
1687 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
1688 RFB
.messages
.fbUpdateRequest(expectedMsg
, true, 0, 0, 640, 20);
1690 // just enough to set FBU.rects
1691 client
._sock
._websocket
._receiveData(new Uint8Array([0, 0, 0, 3]));
1692 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1694 client
._framebufferUpdate = function () { this._sock
.rQskipBytes(1); return true; }; // we magically have enough data
1695 // 247 should *not* be used as the message type here
1696 client
._sock
._websocket
._receiveData(new Uint8Array([247]));
1697 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
1700 it('should not send a request in continuous updates mode', function () {
1701 client
._enabledContinuousUpdates
= true;
1702 client
._framebufferUpdate
= () => true;
1703 client
._sock
._websocket
._receiveData(new Uint8Array([0]));
1705 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
1708 it('should fail on an unsupported encoding', function () {
1709 sinon
.spy(client
, "_fail");
1710 const rectInfo
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1711 sendFbuMsg([rectInfo
], [[]], client
);
1712 expect(client
._fail
).to
.have
.been
.calledOnce
;
1715 describe('Message Encoding Handlers', function () {
1716 beforeEach(function () {
1717 // a really small frame
1718 client
._fbWidth
= 4;
1719 client
._fbHeight
= 4;
1720 client
._fbDepth
= 24;
1721 client
._display
.resize(4, 4);
1724 it('should handle the DesktopSize pseduo-encoding', function () {
1725 sinon
.spy(client
._display
, 'resize');
1726 sendFbuMsg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1728 expect(client
._fbWidth
).to
.equal(20);
1729 expect(client
._fbHeight
).to
.equal(50);
1731 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1732 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1735 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1736 beforeEach(function () {
1737 // a really small frame
1738 client
._fbWidth
= 4;
1739 client
._fbHeight
= 4;
1740 client
._display
.resize(4, 4);
1741 sinon
.spy(client
._display
, 'resize');
1744 function makeScreenData(nrOfScreens
) {
1746 push8(data
, nrOfScreens
); // number-of-screens
1747 push8(data
, 0); // padding
1748 push16(data
, 0); // padding
1749 for (let i
=0; i
<nrOfScreens
; i
+= 1) {
1750 push32(data
, 0); // id
1751 push16(data
, 0); // x-position
1752 push16(data
, 0); // y-position
1753 push16(data
, 20); // width
1754 push16(data
, 50); // height
1755 push32(data
, 0); // flags
1760 it('should handle a resize requested by this client', function () {
1761 const reasonForChange
= 1; // requested by this client
1762 const statusCode
= 0; // No error
1764 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1765 width
: 20, height
: 50, encoding
: -308 }],
1766 makeScreenData(1), client
);
1768 expect(client
._fbWidth
).to
.equal(20);
1769 expect(client
._fbHeight
).to
.equal(50);
1771 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1772 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1775 it('should handle a resize requested by another client', function () {
1776 const reasonForChange
= 2; // requested by another client
1777 const statusCode
= 0; // No error
1779 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1780 width
: 20, height
: 50, encoding
: -308 }],
1781 makeScreenData(1), client
);
1783 expect(client
._fbWidth
).to
.equal(20);
1784 expect(client
._fbHeight
).to
.equal(50);
1786 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1787 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1790 it('should be able to recieve requests which contain data for multiple screens', function () {
1791 const reasonForChange
= 2; // requested by another client
1792 const statusCode
= 0; // No error
1794 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1795 width
: 60, height
: 50, encoding
: -308 }],
1796 makeScreenData(3), client
);
1798 expect(client
._fbWidth
).to
.equal(60);
1799 expect(client
._fbHeight
).to
.equal(50);
1801 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1802 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1805 it('should not handle a failed request', function () {
1806 const reasonForChange
= 1; // requested by this client
1807 const statusCode
= 1; // Resize is administratively prohibited
1809 sendFbuMsg([{ x
: reasonForChange
, y
: statusCode
,
1810 width
: 20, height
: 50, encoding
: -308 }],
1811 makeScreenData(1), client
);
1813 expect(client
._fbWidth
).to
.equal(4);
1814 expect(client
._fbHeight
).to
.equal(4);
1816 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1820 describe('the Cursor pseudo-encoding handler', function () {
1821 beforeEach(function () {
1822 sinon
.spy(client
._cursor
, 'change');
1825 it('should handle a standard cursor', function () {
1826 const info
= { x
: 5, y
: 7,
1827 width
: 4, height
: 4,
1832 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1833 push32(rect
, 0x11223300);
1835 push32(rect
, 0xa0a0a0a0);
1837 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
1838 push32(expected
, 0x332211ff);
1839 push32(expected
, 0x33221100);
1841 expected
= new Uint8Array(expected
);
1843 sendFbuMsg([info
], [rect
], client
);
1845 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1846 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1849 it('should handle an empty cursor', function () {
1850 const info
= { x
: 0, y
: 0,
1851 width
: 0, height
: 0,
1855 sendFbuMsg([info
], [rect
], client
);
1857 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1858 expect(client
._cursor
.change
).to
.have
.been
.calledWith(new Uint8Array
, 0, 0, 0, 0);
1861 it('should handle a transparent cursor', function () {
1862 const info
= { x
: 5, y
: 7,
1863 width
: 4, height
: 4,
1868 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1869 push32(rect
, 0x11223300);
1871 push32(rect
, 0x00000000);
1873 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1874 push32(expected
, 0x33221100);
1876 expected
= new Uint8Array(expected
);
1878 sendFbuMsg([info
], [rect
], client
);
1880 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1881 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1884 describe('dot for empty cursor', function () {
1885 beforeEach(function () {
1886 client
.showDotCursor
= true;
1887 // Was called when we enabled dot cursor
1888 client
._cursor
.change
.resetHistory();
1891 it('should show a standard cursor', function () {
1892 const info
= { x
: 5, y
: 7,
1893 width
: 4, height
: 4,
1898 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1899 push32(rect
, 0x11223300);
1901 push32(rect
, 0xa0a0a0a0);
1903 for (let i
= 0;i
< info
.width
*info
.height
/2;i
++) {
1904 push32(expected
, 0x332211ff);
1905 push32(expected
, 0x33221100);
1907 expected
= new Uint8Array(expected
);
1909 sendFbuMsg([info
], [rect
], client
);
1911 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1912 expect(client
._cursor
.change
).to
.have
.been
.calledWith(expected
, 5, 7, 4, 4);
1915 it('should handle an empty cursor', function () {
1916 const info
= { x
: 0, y
: 0,
1917 width
: 0, height
: 0,
1920 const dot
= RFB
.cursors
.dot
;
1922 sendFbuMsg([info
], [rect
], client
);
1924 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1925 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
1932 it('should handle a transparent cursor', function () {
1933 const info
= { x
: 5, y
: 7,
1934 width
: 4, height
: 4,
1937 const dot
= RFB
.cursors
.dot
;
1939 for (let i
= 0;i
< info
.width
*info
.height
;i
++) {
1940 push32(rect
, 0x11223300);
1942 push32(rect
, 0x00000000);
1944 sendFbuMsg([info
], [rect
], client
);
1946 expect(client
._cursor
.change
).to
.have
.been
.calledOnce
;
1947 expect(client
._cursor
.change
).to
.have
.been
.calledWith(dot
.rgbaPixels
,
1956 describe('the VMware Cursor pseudo-encoding handler', function () {
1957 beforeEach(function () {
1958 sinon
.spy(client
._cursor
, 'change');
1960 afterEach(function () {
1961 client
._cursor
.change
.resetHistory();
1964 it('should handle the VMware cursor pseudo-encoding', function () {
1965 let data
= [0x00, 0x00, 0xff, 0,
1966 0x00, 0xff, 0x00, 0,
1967 0x00, 0xff, 0x00, 0,
1968 0x00, 0x00, 0xff, 0];
1974 for (let i
= 0; i
< data
.length
; i
++) {
1975 push8(rect
, data
[i
]);
1978 for (let i
= 0; i
< data
.length
; i
++) {
1979 push8(rect
, data
[i
]);
1982 sendFbuMsg([{ x
: 0, y
: 0, width
: 2, height
: 2,
1983 encoding
: 0x574d5664}],
1985 expect(client
._FBU
.rects
).to
.equal(0);
1988 it('should handle insufficient cursor pixel data', function () {
1990 // Specified 14x23 pixels for the cursor,
1991 // but only send 2x2 pixels worth of data
1994 let data
= [0x00, 0x00, 0xff, 0,
1995 0x00, 0xff, 0x00, 0];
2002 for (let i
= 0; i
< data
.length
; i
++) {
2003 push8(rect
, data
[i
]);
2006 for (let i
= 0; i
< data
.length
; i
++) {
2007 push8(rect
, data
[i
]);
2010 sendFbuMsg([{ x
: 0, y
: 0, width
: w
, height
: h
,
2011 encoding
: 0x574d5664}],
2014 // expect one FBU to remain unhandled
2015 expect(client
._FBU
.rects
).to
.equal(1);
2018 it('should update the cursor when type is classic', function () {
2020 [0xff, 0xff, 0xff, 0xff, //Transparent
2021 0xff, 0xff, 0xff, 0xff, //Transparent
2022 0x00, 0x00, 0x00, 0x00, //Opaque
2023 0xff, 0xff, 0xff, 0xff]; //Inverted
2026 [0x00, 0x00, 0x00, 0x00, //Transparent
2027 0x00, 0x00, 0x00, 0x00, //Transparent
2028 0x11, 0x22, 0x33, 0x44, //Opaque
2029 0xff, 0xff, 0xff, 0x44]; //Inverted
2032 push8(rect
, 0); //cursor_type
2033 push8(rect
, 0); //padding
2040 for (let i
= 0; i
< andMask
.length
; i
++) {
2041 push8(rect
, andMask
[i
]);
2044 for (let i
= 0; i
< xorMask
.length
; i
++) {
2045 push8(rect
, xorMask
[i
]);
2048 let expectedRgba
= [0x00, 0x00, 0x00, 0x00,
2049 0x00, 0x00, 0x00, 0x00,
2050 0x33, 0x22, 0x11, 0xff,
2051 0x00, 0x00, 0x00, 0xff];
2053 sendFbuMsg([{ x
: hotx
, y
: hoty
,
2054 width
: w
, height
: h
,
2055 encoding
: 0x574d5664}],
2058 expect(client
._cursor
.change
)
2059 .to
.have
.been
.calledOnce
;
2060 expect(client
._cursor
.change
)
2061 .to
.have
.been
.calledWith(expectedRgba
,
2066 it('should update the cursor when type is alpha', function () {
2067 let data
= [0xee, 0x55, 0xff, 0x00, // rgba
2068 0x00, 0xff, 0x00, 0xff,
2069 0x00, 0xff, 0x00, 0x22,
2070 0x00, 0xff, 0x00, 0x22,
2071 0x00, 0xff, 0x00, 0x22,
2072 0x00, 0x00, 0xff, 0xee];
2074 push8(rect
, 1); //cursor_type
2075 push8(rect
, 0); //padding
2081 for (let i
= 0; i
< data
.length
; i
++) {
2082 push8(rect
, data
[i
]);
2085 let expectedRgba
= [0xee, 0x55, 0xff, 0x00,
2086 0x00, 0xff, 0x00, 0xff,
2087 0x00, 0xff, 0x00, 0x22,
2088 0x00, 0xff, 0x00, 0x22,
2089 0x00, 0xff, 0x00, 0x22,
2090 0x00, 0x00, 0xff, 0xee];
2092 sendFbuMsg([{ x
: hotx
, y
: hoty
,
2093 width
: w
, height
: h
,
2094 encoding
: 0x574d5664}],
2097 expect(client
._cursor
.change
)
2098 .to
.have
.been
.calledOnce
;
2099 expect(client
._cursor
.change
)
2100 .to
.have
.been
.calledWith(expectedRgba
,
2105 it('should not update cursor when incorrect cursor type given', function () {
2107 push8(rect
, 3); // invalid cursor type
2108 push8(rect
, 0); // padding
2110 client
._cursor
.change
.resetHistory();
2111 sendFbuMsg([{ x
: 0, y
: 0, width
: 2, height
: 2,
2112 encoding
: 0x574d5664}],
2115 expect(client
._cursor
.change
)
2116 .to
.not
.have
.been
.called
;
2120 it('should handle the last_rect pseudo-encoding', function () {
2121 sendFbuMsg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
2122 expect(client
._FBU
.rects
).to
.equal(0);
2125 it('should handle the DesktopName pseudo-encoding', function () {
2128 pushString(data
, "som€ nam€");
2130 const spy
= sinon
.spy();
2131 client
.addEventListener("desktopname", spy
);
2133 sendFbuMsg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -307 }], [data
], client
);
2135 expect(client
._fbName
).to
.equal('som€ nam€');
2136 expect(spy
).to
.have
.been
.calledOnce
;
2137 expect(spy
.args
[0][0].detail
.name
).to
.equal('som€ nam€');
2142 describe('XVP Message Handling', function () {
2143 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
2144 const spy
= sinon
.spy();
2145 client
.addEventListener("capabilities", spy
);
2146 client
._sock
._websocket
._receiveData(new Uint8Array([250, 0, 10, 1]));
2147 expect(client
._rfbXvpVer
).to
.equal(10);
2148 expect(spy
).to
.have
.been
.calledOnce
;
2149 expect(spy
.args
[0][0].detail
.capabilities
.power
).to
.be
.true;
2150 expect(client
.capabilities
.power
).to
.be
.true;
2153 it('should fail on unknown XVP message types', function () {
2154 sinon
.spy(client
, "_fail");
2155 client
._sock
._websocket
._receiveData(new Uint8Array([250, 0, 10, 237]));
2156 expect(client
._fail
).to
.have
.been
.calledOnce
;
2160 describe('Normal Clipboard Handling Receive', function () {
2161 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
2162 const expectedStr
= 'cheese!';
2163 const data
= [3, 0, 0, 0];
2164 push32(data
, expectedStr
.length
);
2165 for (let i
= 0; i
< expectedStr
.length
; i
++) { data
.push(expectedStr
.charCodeAt(i
)); }
2166 const spy
= sinon
.spy();
2167 client
.addEventListener("clipboard", spy
);
2169 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2170 expect(spy
).to
.have
.been
.calledOnce
;
2171 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedStr
);
2175 describe('Extended clipboard Handling', function () {
2177 describe('Extended clipboard initialization', function () {
2178 beforeEach(function () {
2179 sinon
.spy(RFB
.messages
, 'extendedClipboardCaps');
2182 afterEach(function () {
2183 RFB
.messages
.extendedClipboardCaps
.restore();
2186 it('should update capabilities when receiving a Caps message', function () {
2187 let data
= [3, 0, 0, 0];
2188 const flags
= [0x1F, 0x00, 0x00, 0x03];
2189 let fileSizes
= [0x00, 0x00, 0x00, 0x1E,
2190 0x00, 0x00, 0x00, 0x3C];
2192 push32(data
, toUnsigned32bit(-12));
2193 data
= data
.concat(flags
);
2194 data
= data
.concat(fileSizes
);
2195 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2197 // Check that we give an response caps when we receive one
2198 expect(RFB
.messages
.extendedClipboardCaps
).to
.have
.been
.calledOnce
;
2200 // FIXME: Can we avoid checking internal variables?
2201 expect(client
._clipboardServerCapabilitiesFormats
[0]).to
.not
.equal(true);
2202 expect(client
._clipboardServerCapabilitiesFormats
[1]).to
.equal(true);
2203 expect(client
._clipboardServerCapabilitiesFormats
[2]).to
.equal(true);
2204 expect(client
._clipboardServerCapabilitiesActions
[(1 << 24)]).to
.equal(true);
2210 describe('Extended Clipboard Handling Receive', function () {
2212 beforeEach(function () {
2213 // Send our capabilities
2214 let data
= [3, 0, 0, 0];
2215 const flags
= [0x1F, 0x00, 0x00, 0x01];
2216 let fileSizes
= [0x00, 0x00, 0x00, 0x1E];
2218 push32(data
, toUnsigned32bit(-8));
2219 data
= data
.concat(flags
);
2220 data
= data
.concat(fileSizes
);
2221 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2224 describe('Handle Provide', function () {
2225 it('should update clipboard with correct Unicode data from a Provide message', function () {
2226 let expectedData
= "Aå漢å—!";
2227 let data
= [3, 0, 0, 0];
2228 const flags
= [0x10, 0x00, 0x00, 0x01];
2230 /* The size 10 (utf8 encoded string size) and the
2231 string "Aå漢å—!" utf8 encoded and deflated. */
2232 let deflatedData
= [120, 94, 99, 96, 96, 224, 114, 60,
2233 188, 244, 217, 158, 69, 79, 215,
2234 78, 87, 4, 0, 35, 207, 6, 66];
2236 // How much data we are sending.
2237 push32(data
, toUnsigned32bit(-(4 + deflatedData
.length
)));
2239 data
= data
.concat(flags
);
2240 data
= data
.concat(deflatedData
);
2242 const spy
= sinon
.spy();
2243 client
.addEventListener("clipboard", spy
);
2245 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2246 expect(spy
).to
.have
.been
.calledOnce
;
2247 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2248 client
.removeEventListener("clipboard", spy
);
2251 it('should update clipboard with correct escape characters from a Provide message ', function () {
2252 let expectedData
= "Oh\nmy!";
2253 let data
= [3, 0, 0, 0];
2254 const flags
= [0x10, 0x00, 0x00, 0x01];
2256 let text
= encodeUTF8("Oh\r\nmy!\0");
2258 let deflatedText
= deflateWithSize(text
);
2260 // How much data we are sending.
2261 push32(data
, toUnsigned32bit(-(4 + deflatedText
.length
)));
2263 data
= data
.concat(flags
);
2265 let sendData
= new Uint8Array(data
.length
+ deflatedText
.length
);
2267 sendData
.set(deflatedText
, data
.length
);
2269 const spy
= sinon
.spy();
2270 client
.addEventListener("clipboard", spy
);
2272 client
._sock
._websocket
._receiveData(sendData
);
2273 expect(spy
).to
.have
.been
.calledOnce
;
2274 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2275 client
.removeEventListener("clipboard", spy
);
2278 it('should be able to handle large Provide messages', function () {
2279 let expectedData
= "hello".repeat(100000);
2280 let data
= [3, 0, 0, 0];
2281 const flags
= [0x10, 0x00, 0x00, 0x01];
2283 let text
= encodeUTF8(expectedData
+ "\0");
2285 let deflatedText
= deflateWithSize(text
);
2287 // How much data we are sending.
2288 push32(data
, toUnsigned32bit(-(4 + deflatedText
.length
)));
2290 data
= data
.concat(flags
);
2292 let sendData
= new Uint8Array(data
.length
+ deflatedText
.length
);
2294 sendData
.set(deflatedText
, data
.length
);
2296 const spy
= sinon
.spy();
2297 client
.addEventListener("clipboard", spy
);
2299 client
._sock
._websocket
._receiveData(sendData
);
2300 expect(spy
).to
.have
.been
.calledOnce
;
2301 expect(spy
.args
[0][0].detail
.text
).to
.equal(expectedData
);
2302 client
.removeEventListener("clipboard", spy
);
2307 describe('Handle Notify', function () {
2308 beforeEach(function () {
2309 sinon
.spy(RFB
.messages
, 'extendedClipboardRequest');
2312 afterEach(function () {
2313 RFB
.messages
.extendedClipboardRequest
.restore();
2316 it('should make a request with supported formats when receiving a notify message', function () {
2317 let data
= [3, 0, 0, 0];
2318 const flags
= [0x08, 0x00, 0x00, 0x07];
2319 push32(data
, toUnsigned32bit(-4));
2320 data
= data
.concat(flags
);
2321 let expectedData
= [0x01];
2323 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2325 expect(RFB
.messages
.extendedClipboardRequest
).to
.have
.been
.calledOnce
;
2326 expect(RFB
.messages
.extendedClipboardRequest
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2330 describe('Handle Peek', function () {
2331 beforeEach(function () {
2332 sinon
.spy(RFB
.messages
, 'extendedClipboardNotify');
2335 afterEach(function () {
2336 RFB
.messages
.extendedClipboardNotify
.restore();
2339 it('should send an empty Notify when receiving a Peek and no excisting clipboard data', function () {
2340 let data
= [3, 0, 0, 0];
2341 const flags
= [0x04, 0x00, 0x00, 0x00];
2342 push32(data
, toUnsigned32bit(-4));
2343 data
= data
.concat(flags
);
2344 let expectedData
= [];
2346 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2348 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
2349 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2352 it('should send a Notify message with supported formats when receiving a Peek', function () {
2353 let data
= [3, 0, 0, 0];
2354 const flags
= [0x04, 0x00, 0x00, 0x00];
2355 push32(data
, toUnsigned32bit(-4));
2356 data
= data
.concat(flags
);
2357 let expectedData
= [0x01];
2359 // Needed to have clipboard data to read.
2360 // This will trigger a call to Notify, reset history
2361 client
.clipboardPasteFrom("HejHej");
2362 RFB
.messages
.extendedClipboardNotify
.resetHistory();
2364 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2366 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledOnce
;
2367 expect(RFB
.messages
.extendedClipboardNotify
).to
.have
.been
.calledWith(client
._sock
, expectedData
);
2371 describe('Handle Request', function () {
2372 beforeEach(function () {
2373 sinon
.spy(RFB
.messages
, 'extendedClipboardProvide');
2376 afterEach(function () {
2377 RFB
.messages
.extendedClipboardProvide
.restore();
2380 it('should send a Provide message with supported formats when receiving a Request', function () {
2381 let data
= [3, 0, 0, 0];
2382 const flags
= [0x02, 0x00, 0x00, 0x01];
2383 push32(data
, toUnsigned32bit(-4));
2384 data
= data
.concat(flags
);
2385 let expectedData
= [0x01];
2387 client
.clipboardPasteFrom("HejHej");
2388 expect(RFB
.messages
.extendedClipboardProvide
).to
.not
.have
.been
.called
;
2390 client
._sock
._websocket
._receiveData(new Uint8Array(data
));
2392 expect(RFB
.messages
.extendedClipboardProvide
).to
.have
.been
.calledOnce
;
2393 expect(RFB
.messages
.extendedClipboardProvide
).to
.have
.been
.calledWith(client
._sock
, expectedData
, ["HejHej"]);
2400 it('should fire the bell callback on Bell', function () {
2401 const spy
= sinon
.spy();
2402 client
.addEventListener("bell", spy
);
2403 client
._sock
._websocket
._receiveData(new Uint8Array([2]));
2404 expect(spy
).to
.have
.been
.calledOnce
;
2407 it('should respond correctly to ServerFence', function () {
2408 const expectedMsg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2409 const incomingMsg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush
: () => {}};
2411 const payload
= "foo\x00ab9";
2413 // ClientFence and ServerFence are identical in structure
2414 RFB
.messages
.clientFence(expectedMsg
, (1<<0) | (1<<1), payload
);
2415 RFB
.messages
.clientFence(incomingMsg
, 0xffffffff, payload
);
2417 client
._sock
._websocket
._receiveData(incomingMsg
._sQ
);
2419 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2421 expectedMsg
._sQlen
= 0;
2422 incomingMsg
._sQlen
= 0;
2424 RFB
.messages
.clientFence(expectedMsg
, (1<<0), payload
);
2425 RFB
.messages
.clientFence(incomingMsg
, (1<<0) | (1<<31), payload
);
2427 client
._sock
._websocket
._receiveData(incomingMsg
._sQ
);
2429 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2432 it('should enable continuous updates on first EndOfContinousUpdates', function () {
2433 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2435 RFB
.messages
.enableContinuousUpdates(expectedMsg
, true, 0, 0, 640, 20);
2437 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2439 client
._sock
._websocket
._receiveData(new Uint8Array([150]));
2441 expect(client
._enabledContinuousUpdates
).to
.be
.true;
2442 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2445 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
2446 client
._enabledContinuousUpdates
= true;
2447 client
._supportsContinuousUpdates
= true;
2449 client
._sock
._websocket
._receiveData(new Uint8Array([150]));
2451 expect(client
._enabledContinuousUpdates
).to
.be
.false;
2454 it('should update continuous updates on resize', function () {
2455 const expectedMsg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush
: () => {}};
2456 RFB
.messages
.enableContinuousUpdates(expectedMsg
, true, 0, 0, 90, 700);
2458 client
._resize(450, 160);
2460 expect(client
._sock
._websocket
._getSentData()).to
.have
.length(0);
2462 client
._enabledContinuousUpdates
= true;
2464 client
._resize(90, 700);
2466 expect(client
._sock
).to
.have
.sent(expectedMsg
._sQ
);
2469 it('should fail on an unknown message type', function () {
2470 sinon
.spy(client
, "_fail");
2471 client
._sock
._websocket
._receiveData(new Uint8Array([87]));
2472 expect(client
._fail
).to
.have
.been
.calledOnce
;
2476 describe('Asynchronous Events', function () {
2482 beforeEach(function () {
2484 client
._display
.resize(100, 100);
2486 // We need to disable this as focusing the canvas will
2487 // cause the browser to scoll to it, messing up our
2488 // client coordinate calculations
2489 client
.focusOnClick
= false;
2491 pointerEvent
= sinon
.spy(RFB
.messages
, 'pointerEvent');
2492 keyEvent
= sinon
.spy(RFB
.messages
, 'keyEvent');
2493 qemuKeyEvent
= sinon
.spy(RFB
.messages
, 'QEMUExtendedKeyEvent');
2496 afterEach(function () {
2497 pointerEvent
.restore();
2499 qemuKeyEvent
.restore();
2502 function elementToClient(x
, y
) {
2503 let res
= { x
: 0, y
: 0 };
2505 let bounds
= client
._canvas
.getBoundingClientRect();
2508 * If the canvas is on a fractional position we will calculate
2509 * a fractional mouse position. But that gets truncated when we
2510 * send the event, AND the same thing happens in RFB when it
2511 * generates the PointerEvent message. To compensate for that
2512 * fact we round the value upwards here.
2514 res
.x
= Math
.ceil(bounds
.left
+ x
);
2515 res
.y
= Math
.ceil(bounds
.top
+ y
);
2520 describe('Mouse Events', function () {
2521 function sendMouseMoveEvent(x
, y
) {
2522 let pos
= elementToClient(x
, y
);
2525 ev
= new MouseEvent('mousemove',
2526 { 'screenX': pos
.x
+ window
.screenX
,
2527 'screenY': pos
.y
+ window
.screenY
,
2529 'clientY': pos
.y
});
2530 client
._canvas
.dispatchEvent(ev
);
2533 function sendMouseButtonEvent(x
, y
, down
, button
) {
2534 let pos
= elementToClient(x
, y
);
2537 ev
= new MouseEvent(down
? 'mousedown' : 'mouseup',
2538 { 'screenX': pos
.x
+ window
.screenX
,
2539 'screenY': pos
.y
+ window
.screenY
,
2543 'buttons': 1 << button
});
2544 client
._canvas
.dispatchEvent(ev
);
2547 it('should not send button messages in view-only mode', function () {
2548 client
._viewOnly
= true;
2549 sendMouseButtonEvent(10, 10, true, 0);
2551 expect(pointerEvent
).to
.not
.have
.been
.called
;
2554 it('should not send movement messages in view-only mode', function () {
2555 client
._viewOnly
= true;
2556 sendMouseMoveEvent(10, 10);
2558 expect(pointerEvent
).to
.not
.have
.been
.called
;
2561 it('should handle left mouse button', function () {
2562 sendMouseButtonEvent(10, 10, true, 0);
2564 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2566 pointerEvent
.resetHistory();
2568 sendMouseButtonEvent(10, 10, false, 0);
2570 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2574 it('should handle middle mouse button', function () {
2575 sendMouseButtonEvent(10, 10, true, 1);
2577 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2579 pointerEvent
.resetHistory();
2581 sendMouseButtonEvent(10, 10, false, 1);
2583 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2587 it('should handle right mouse button', function () {
2588 sendMouseButtonEvent(10, 10, true, 2);
2590 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2592 pointerEvent
.resetHistory();
2594 sendMouseButtonEvent(10, 10, false, 2);
2596 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2600 it('should handle multiple mouse buttons', function () {
2601 sendMouseButtonEvent(10, 10, true, 0);
2602 sendMouseButtonEvent(10, 10, true, 2);
2604 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2605 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2607 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2610 pointerEvent
.resetHistory();
2612 sendMouseButtonEvent(10, 10, false, 0);
2613 sendMouseButtonEvent(10, 10, false, 2);
2615 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2616 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2618 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2622 it('should handle mouse movement', function () {
2623 sendMouseMoveEvent(50, 70);
2624 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2628 it('should handle click and drag', function () {
2629 sendMouseButtonEvent(10, 10, true, 0);
2630 sendMouseMoveEvent(50, 70);
2632 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2633 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2635 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2638 pointerEvent
.resetHistory();
2640 sendMouseButtonEvent(50, 70, false, 0);
2642 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2646 describe('Event Aggregation', function () {
2647 it('should send a single pointer event on mouse movement', function () {
2648 sendMouseMoveEvent(50, 70);
2650 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2654 it('should delay one move if two events are too close', function () {
2655 sendMouseMoveEvent(18, 30);
2656 sendMouseMoveEvent(20, 50);
2658 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2660 pointerEvent
.resetHistory();
2664 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2668 it('should only send first and last move of many close events', function () {
2669 sendMouseMoveEvent(18, 30);
2670 sendMouseMoveEvent(20, 50);
2671 sendMouseMoveEvent(21, 55);
2673 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2675 pointerEvent
.resetHistory();
2679 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2683 // We selected the 17ms since that is ~60 FPS
2684 it('should send move events every 17 ms', function () {
2685 sendMouseMoveEvent(1, 10); // instant send
2688 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2690 pointerEvent
.resetHistory();
2692 sendMouseMoveEvent(2, 20); // delayed
2693 clock
.tick(10); // timeout send
2695 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2697 pointerEvent
.resetHistory();
2699 sendMouseMoveEvent(3, 30); // delayed
2701 sendMouseMoveEvent(4, 40); // delayed
2702 clock
.tick(10); // timeout send
2704 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2706 pointerEvent
.resetHistory();
2708 sendMouseMoveEvent(5, 50); // delayed
2710 expect(pointerEvent
).to
.not
.have
.been
.called
;
2713 it('should send waiting move events before a button press', function () {
2714 sendMouseMoveEvent(13, 9);
2716 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2718 pointerEvent
.resetHistory();
2720 sendMouseMoveEvent(20, 70);
2722 expect(pointerEvent
).to
.not
.have
.been
.called
;
2724 sendMouseButtonEvent(20, 70, true, 0);
2726 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2727 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2729 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2733 it('should send move events with enough time apart normally', function () {
2734 sendMouseMoveEvent(58, 60);
2736 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2738 pointerEvent
.resetHistory();
2742 sendMouseMoveEvent(25, 60);
2744 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2746 pointerEvent
.resetHistory();
2749 it('should not send waiting move events if disconnected', function () {
2750 sendMouseMoveEvent(88, 99);
2752 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
2754 pointerEvent
.resetHistory();
2756 sendMouseMoveEvent(66, 77);
2757 client
.disconnect();
2760 expect(pointerEvent
).to
.not
.have
.been
.called
;
2764 it
.skip('should block click events', function () {
2768 it
.skip('should block contextmenu events', function () {
2773 describe('Wheel Events', function () {
2774 function sendWheelEvent(x
, y
, dx
, dy
, mode
=0) {
2775 let pos
= elementToClient(x
, y
);
2778 ev
= new WheelEvent('wheel',
2779 { 'screenX': pos
.x
+ window
.screenX
,
2780 'screenY': pos
.y
+ window
.screenY
,
2785 'deltaMode': mode
});
2786 client
._canvas
.dispatchEvent(ev
);
2789 it('should handle wheel up event', function () {
2790 sendWheelEvent(10, 10, 0, -50);
2792 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2793 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2795 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2799 it('should handle wheel down event', function () {
2800 sendWheelEvent(10, 10, 0, 50);
2802 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2803 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2805 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2809 it('should handle wheel left event', function () {
2810 sendWheelEvent(10, 10, -50, 0);
2812 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2813 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2815 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2819 it('should handle wheel right event', function () {
2820 sendWheelEvent(10, 10, 50, 0);
2822 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2823 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2825 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2829 it('should ignore wheel when in view only', function () {
2830 client
._viewOnly
= true;
2832 sendWheelEvent(10, 10, 50, 0);
2834 expect(pointerEvent
).to
.not
.have
.been
.called
;
2837 it('should accumulate wheel events if small enough', function () {
2838 sendWheelEvent(10, 10, 0, 20);
2839 sendWheelEvent(10, 10, 0, 20);
2841 expect(pointerEvent
).to
.not
.have
.been
.called
;
2843 sendWheelEvent(10, 10, 0, 20);
2845 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2846 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2848 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2852 it('should not accumulate large wheel events', function () {
2853 sendWheelEvent(10, 10, 0, 400);
2855 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2856 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2858 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2862 it('should handle line based wheel event', function () {
2863 sendWheelEvent(10, 10, 0, 3, 1);
2865 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2866 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2868 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2872 it('should handle page based wheel event', function () {
2873 sendWheelEvent(10, 10, 0, 3, 2);
2875 expect(pointerEvent
).to
.have
.been
.calledTwice
;
2876 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2878 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2883 describe('Keyboard Events', function () {
2884 it('should send a key message on a key press', function () {
2885 client
._handleKeyEvent(0x41, 'KeyA', true);
2886 const keyMsg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush
: () => {}};
2887 RFB
.messages
.keyEvent(keyMsg
, 0x41, 1);
2888 expect(client
._sock
).to
.have
.sent(keyMsg
._sQ
);
2891 it('should not send messages in view-only mode', function () {
2892 client
._viewOnly
= true;
2893 sinon
.spy(client
._sock
, 'flush');
2894 client
._handleKeyEvent('a', 'KeyA', true);
2895 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2899 describe('Gesture event handlers', function () {
2900 function gestureStart(gestureType
, x
, y
,
2901 magnitudeX
= 0, magnitudeY
= 0) {
2902 let pos
= elementToClient(x
, y
);
2903 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2905 detail
.magnitudeX
= magnitudeX
;
2906 detail
.magnitudeY
= magnitudeY
;
2908 let ev
= new CustomEvent('gesturestart', { detail
: detail
});
2909 client
._canvas
.dispatchEvent(ev
);
2912 function gestureMove(gestureType
, x
, y
,
2913 magnitudeX
= 0, magnitudeY
= 0) {
2914 let pos
= elementToClient(x
, y
);
2915 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2917 detail
.magnitudeX
= magnitudeX
;
2918 detail
.magnitudeY
= magnitudeY
;
2920 let ev
= new CustomEvent('gesturemove', { detail
: detail
});
2921 client
._canvas
.dispatchEvent(ev
);
2924 function gestureEnd(gestureType
, x
, y
) {
2925 let pos
= elementToClient(x
, y
);
2926 let detail
= {type
: gestureType
, clientX
: pos
.x
, clientY
: pos
.y
};
2927 let ev
= new CustomEvent('gestureend', { detail
: detail
});
2928 client
._canvas
.dispatchEvent(ev
);
2931 describe('Gesture onetap', function () {
2932 it('should handle onetap events', function () {
2935 gestureStart('onetap', 20, 40);
2936 gestureEnd('onetap', 20, 40);
2938 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2939 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2941 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2943 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2947 it('should keep same position for multiple onetap events', function () {
2950 gestureStart('onetap', 20, 40);
2951 gestureEnd('onetap', 20, 40);
2953 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2954 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2956 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2958 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2961 pointerEvent
.resetHistory();
2963 gestureStart('onetap', 20, 50);
2964 gestureEnd('onetap', 20, 50);
2966 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2967 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2969 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2971 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2974 pointerEvent
.resetHistory();
2976 gestureStart('onetap', 30, 50);
2977 gestureEnd('onetap', 30, 50);
2979 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2980 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2982 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2984 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
2988 it('should not keep same position for onetap events when too far apart', function () {
2991 gestureStart('onetap', 20, 40);
2992 gestureEnd('onetap', 20, 40);
2994 expect(pointerEvent
).to
.have
.been
.calledThrice
;
2995 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
2997 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
2999 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3002 pointerEvent
.resetHistory();
3004 gestureStart('onetap', 80, 95);
3005 gestureEnd('onetap', 80, 95);
3007 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3008 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3010 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3012 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3016 it('should not keep same position for onetap events when enough time inbetween', function () {
3019 gestureStart('onetap', 10, 20);
3020 gestureEnd('onetap', 10, 20);
3022 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3023 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3025 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3027 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3030 pointerEvent
.resetHistory();
3031 this.clock
.tick(1500);
3033 gestureStart('onetap', 15, 20);
3034 gestureEnd('onetap', 15, 20);
3036 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3037 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3039 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3041 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3044 pointerEvent
.resetHistory();
3048 describe('Gesture twotap', function () {
3049 it('should handle gesture twotap events', function () {
3052 gestureStart("twotap", 20, 40);
3054 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3055 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3057 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3059 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3063 it('should keep same position for multiple twotap events', function () {
3066 for (let offset
= 0;offset
< 30;offset
+= 10) {
3067 pointerEvent
.resetHistory();
3069 gestureStart('twotap', 20, 40 + offset
);
3070 gestureEnd('twotap', 20, 40 + offset
);
3072 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3073 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3075 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3077 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3083 describe('Gesture threetap', function () {
3084 it('should handle gesture start for threetap events', function () {
3087 gestureStart("threetap", 20, 40);
3089 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3090 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3092 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3094 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3098 it('should keep same position for multiple threetap events', function () {
3101 for (let offset
= 0;offset
< 30;offset
+= 10) {
3102 pointerEvent
.resetHistory();
3104 gestureStart('threetap', 20, 40 + offset
);
3105 gestureEnd('threetap', 20, 40 + offset
);
3107 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3108 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3110 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3112 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3118 describe('Gesture drag', function () {
3119 it('should handle gesture drag events', function () {
3122 gestureStart('drag', 20, 40);
3124 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3125 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3127 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3130 pointerEvent
.resetHistory();
3132 gestureMove('drag', 30, 50);
3135 expect(pointerEvent
).to
.have
.been
.calledOnce
;
3136 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3139 pointerEvent
.resetHistory();
3141 gestureEnd('drag', 30, 50);
3143 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3144 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3146 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3151 describe('Gesture long press', function () {
3152 it('should handle long press events', function () {
3155 gestureStart('longpress', 20, 40);
3157 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3158 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3160 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3162 pointerEvent
.resetHistory();
3164 gestureMove('longpress', 40, 60);
3167 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3170 pointerEvent
.resetHistory();
3172 gestureEnd('longpress', 40, 60);
3174 expect(pointerEvent
).to
.have
.been
.calledTwice
;
3175 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3177 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3182 describe('Gesture twodrag', function () {
3183 it('should handle gesture twodrag up events', function () {
3184 let bmask
= 0x10; // Button mask for scroll down
3186 gestureStart('twodrag', 20, 40, 0, 0);
3188 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3191 pointerEvent
.resetHistory();
3193 gestureMove('twodrag', 20, 40, 0, -60);
3195 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3196 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3198 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3200 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3204 it('should handle gesture twodrag down events', function () {
3205 let bmask
= 0x8; // Button mask for scroll up
3207 gestureStart('twodrag', 20, 40, 0, 0);
3209 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3212 pointerEvent
.resetHistory();
3214 gestureMove('twodrag', 20, 40, 0, 60);
3216 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3217 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3219 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3221 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3225 it('should handle gesture twodrag right events', function () {
3226 let bmask
= 0x20; // Button mask for scroll right
3228 gestureStart('twodrag', 20, 40, 0, 0);
3230 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3233 pointerEvent
.resetHistory();
3235 gestureMove('twodrag', 20, 40, 60, 0);
3237 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3238 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3240 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3242 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3246 it('should handle gesture twodrag left events', function () {
3247 let bmask
= 0x40; // Button mask for scroll left
3249 gestureStart('twodrag', 20, 40, 0, 0);
3251 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3254 pointerEvent
.resetHistory();
3256 gestureMove('twodrag', 20, 40, -60, 0);
3258 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3259 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3261 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3263 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3267 it('should handle gesture twodrag diag events', function () {
3268 let scrlUp
= 0x8; // Button mask for scroll up
3269 let scrlRight
= 0x20; // Button mask for scroll right
3271 gestureStart('twodrag', 20, 40, 0, 0);
3273 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3276 pointerEvent
.resetHistory();
3278 gestureMove('twodrag', 20, 40, 60, 60);
3280 expect(pointerEvent
).to
.have
.been
.callCount(5);
3281 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3283 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3285 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3287 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3289 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3293 it('should handle multiple small gesture twodrag events', function () {
3294 let bmask
= 0x8; // Button mask for scroll up
3296 gestureStart('twodrag', 20, 40, 0, 0);
3298 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3301 pointerEvent
.resetHistory();
3303 gestureMove('twodrag', 20, 40, 0, 10);
3306 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3309 pointerEvent
.resetHistory();
3311 gestureMove('twodrag', 20, 40, 0, 20);
3314 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3317 pointerEvent
.resetHistory();
3319 gestureMove('twodrag', 20, 40, 0, 60);
3321 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3322 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3324 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3326 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3330 it('should handle large gesture twodrag events', function () {
3331 let bmask
= 0x8; // Button mask for scroll up
3333 gestureStart('twodrag', 30, 50, 0, 0);
3335 expect(pointerEvent
).
3336 to
.have
.been
.calledOnceWith(client
._sock
, 30, 50, 0x0);
3338 pointerEvent
.resetHistory();
3340 gestureMove('twodrag', 30, 50, 0, 200);
3342 expect(pointerEvent
).to
.have
.callCount(7);
3343 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3345 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3347 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3349 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3351 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3353 expect(pointerEvent
.getCall(5)).to
.have
.been
.calledWith(client
._sock
,
3355 expect(pointerEvent
.getCall(6)).to
.have
.been
.calledWith(client
._sock
,
3360 describe('Gesture pinch', function () {
3361 it('should handle gesture pinch in events', function () {
3362 let keysym
= KeyTable
.XK_Control_L
;
3363 let bmask
= 0x10; // Button mask for scroll down
3365 gestureStart('pinch', 20, 40, 90, 90);
3367 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3369 expect(keyEvent
).to
.not
.have
.been
.called
;
3371 pointerEvent
.resetHistory();
3373 gestureMove('pinch', 20, 40, 30, 30);
3375 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3376 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3378 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3380 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3383 expect(keyEvent
).to
.have
.been
.calledTwice
;
3384 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3386 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3389 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3390 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3392 pointerEvent
.resetHistory();
3393 keyEvent
.resetHistory();
3395 gestureEnd('pinch', 20, 40);
3397 expect(pointerEvent
).to
.not
.have
.been
.called
;
3398 expect(keyEvent
).to
.not
.have
.been
.called
;
3401 it('should handle gesture pinch out events', function () {
3402 let keysym
= KeyTable
.XK_Control_L
;
3403 let bmask
= 0x8; // Button mask for scroll up
3405 gestureStart('pinch', 10, 20, 10, 20);
3407 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3409 expect(keyEvent
).to
.not
.have
.been
.called
;
3411 pointerEvent
.resetHistory();
3413 gestureMove('pinch', 10, 20, 70, 80);
3415 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3416 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3418 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3420 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3423 expect(keyEvent
).to
.have
.been
.calledTwice
;
3424 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3426 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3429 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3430 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3432 pointerEvent
.resetHistory();
3433 keyEvent
.resetHistory();
3435 gestureEnd('pinch', 10, 20);
3437 expect(pointerEvent
).to
.not
.have
.been
.called
;
3438 expect(keyEvent
).to
.not
.have
.been
.called
;
3441 it('should handle large gesture pinch', function () {
3442 let keysym
= KeyTable
.XK_Control_L
;
3443 let bmask
= 0x10; // Button mask for scroll down
3445 gestureStart('pinch', 20, 40, 150, 150);
3447 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3449 expect(keyEvent
).to
.not
.have
.been
.called
;
3451 pointerEvent
.resetHistory();
3453 gestureMove('pinch', 20, 40, 30, 30);
3455 expect(pointerEvent
).to
.have
.been
.callCount(5);
3456 expect(pointerEvent
.getCall(0)).to
.have
.been
.calledWith(client
._sock
,
3458 expect(pointerEvent
.getCall(1)).to
.have
.been
.calledWith(client
._sock
,
3460 expect(pointerEvent
.getCall(2)).to
.have
.been
.calledWith(client
._sock
,
3462 expect(pointerEvent
.getCall(3)).to
.have
.been
.calledWith(client
._sock
,
3464 expect(pointerEvent
.getCall(4)).to
.have
.been
.calledWith(client
._sock
,
3467 expect(keyEvent
).to
.have
.been
.calledTwice
;
3468 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3470 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3473 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3474 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3476 pointerEvent
.resetHistory();
3477 keyEvent
.resetHistory();
3479 gestureEnd('pinch', 20, 40);
3481 expect(pointerEvent
).to
.not
.have
.been
.called
;
3482 expect(keyEvent
).to
.not
.have
.been
.called
;
3485 it('should handle multiple small gesture pinch out events', function () {
3486 let keysym
= KeyTable
.XK_Control_L
;
3487 let bmask
= 0x8; // Button mask for scroll down
3489 gestureStart('pinch', 20, 40, 0, 10);
3491 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3493 expect(keyEvent
).to
.not
.have
.been
.called
;
3495 pointerEvent
.resetHistory();
3497 gestureMove('pinch', 20, 40, 0, 30);
3500 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3503 pointerEvent
.resetHistory();
3505 gestureMove('pinch', 20, 40, 0, 60);
3508 expect(pointerEvent
).to
.have
.been
.calledWith(client
._sock
,
3511 pointerEvent
.resetHistory();
3512 keyEvent
.resetHistory();
3514 gestureMove('pinch', 20, 40, 0, 90);
3516 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3517 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3519 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3521 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3524 expect(keyEvent
).to
.have
.been
.calledTwice
;
3525 expect(keyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3527 expect(keyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3530 expect(keyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3531 expect(keyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3533 pointerEvent
.resetHistory();
3534 keyEvent
.resetHistory();
3536 gestureEnd('pinch', 20, 40);
3538 expect(keyEvent
).to
.not
.have
.been
.called
;
3541 it('should send correct key control code', function () {
3542 let keysym
= KeyTable
.XK_Control_L
;
3544 let bmask
= 0x10; // Button mask for scroll down
3546 client
._qemuExtKeyEventSupported
= true;
3548 gestureStart('pinch', 20, 40, 90, 90);
3550 expect(pointerEvent
).to
.have
.been
.calledOnceWith(client
._sock
,
3552 expect(qemuKeyEvent
).to
.not
.have
.been
.called
;
3554 pointerEvent
.resetHistory();
3556 gestureMove('pinch', 20, 40, 30, 30);
3558 expect(pointerEvent
).to
.have
.been
.calledThrice
;
3559 expect(pointerEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3561 expect(pointerEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3563 expect(pointerEvent
.thirdCall
).to
.have
.been
.calledWith(client
._sock
,
3566 expect(qemuKeyEvent
).to
.have
.been
.calledTwice
;
3567 expect(qemuKeyEvent
.firstCall
).to
.have
.been
.calledWith(client
._sock
,
3571 expect(qemuKeyEvent
.secondCall
).to
.have
.been
.calledWith(client
._sock
,
3576 expect(qemuKeyEvent
.firstCall
).to
.have
.been
.calledBefore(pointerEvent
.secondCall
);
3577 expect(qemuKeyEvent
.lastCall
).to
.have
.been
.calledAfter(pointerEvent
.lastCall
);
3579 pointerEvent
.resetHistory();
3580 qemuKeyEvent
.resetHistory();
3582 gestureEnd('pinch', 20, 40);
3584 expect(pointerEvent
).to
.not
.have
.been
.called
;
3585 expect(qemuKeyEvent
).to
.not
.have
.been
.called
;
3590 describe('WebSocket Events', function () {
3592 it('should do nothing if we receive an empty message and have nothing in the queue', function () {
3593 client
._normalMsg
= sinon
.spy();
3594 client
._sock
._websocket
._receiveData(new Uint8Array([]));
3595 expect(client
._normalMsg
).to
.not
.have
.been
.called
;
3598 it('should handle a message in the connected state as a normal message', function () {
3599 client
._normalMsg
= sinon
.spy();
3600 client
._sock
._websocket
._receiveData(new Uint8Array([1, 2, 3]));
3601 expect(client
._normalMsg
).to
.have
.been
.called
;
3604 it('should handle a message in any non-disconnected/failed state like an init message', function () {
3605 client
._rfbConnectionState
= 'connecting';
3606 client
._rfbInitState
= 'ProtocolVersion';
3607 client
._initMsg
= sinon
.spy();
3608 client
._sock
._websocket
._receiveData(new Uint8Array([1, 2, 3]));
3609 expect(client
._initMsg
).to
.have
.been
.called
;
3612 it('should process all normal messages directly', function () {
3613 const spy
= sinon
.spy();
3614 client
.addEventListener("bell", spy
);
3615 client
._sock
._websocket
._receiveData(new Uint8Array([0x02, 0x02]));
3616 expect(spy
).to
.have
.been
.calledTwice
;
3620 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
3621 client
= new RFB(document
.createElement('div'), 'wss://host:8675');
3623 client
._sock
._websocket
._open();
3624 expect(client
._rfbInitState
).to
.equal('ProtocolVersion');
3627 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
3628 sinon
.spy(client
, "_fail");
3629 client
._rfbConnectionState
= 'connected';
3630 client
._sock
._websocket
._open();
3631 expect(client
._fail
).to
.have
.been
.calledOnce
;
3635 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
3636 const real
= client
._sock
._websocket
.close
;
3637 client
._sock
._websocket
.close
= () => {};
3638 client
.disconnect();
3639 expect(client
._rfbConnectionState
).to
.equal('disconnecting');
3640 client
._sock
._websocket
.close
= real
;
3641 client
._sock
._websocket
.close();
3642 expect(client
._rfbConnectionState
).to
.equal('disconnected');
3645 it('should fail if we get a close event while connecting', function () {
3646 sinon
.spy(client
, "_fail");
3647 client
._rfbConnectionState
= 'connecting';
3648 client
._sock
._websocket
.close();
3649 expect(client
._fail
).to
.have
.been
.calledOnce
;
3652 it('should unregister close event handler', function () {
3653 sinon
.spy(client
._sock
, 'off');
3654 client
.disconnect();
3655 client
._sock
._websocket
.close();
3656 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
3659 // error events do nothing
3663 describe('Quality level setting', function () {
3664 const defaultQuality
= 6;
3668 beforeEach(function () {
3670 sinon
.spy(RFB
.messages
, "clientEncodings");
3673 afterEach(function () {
3674 RFB
.messages
.clientEncodings
.restore();
3677 it(`should equal ${defaultQuality} by default`, function () {
3678 expect(client
._qualityLevel
).to
.equal(defaultQuality
);
3681 it('should ignore non-integers when set', function () {
3682 client
.qualityLevel
= '1';
3683 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3685 RFB
.messages
.clientEncodings
.resetHistory();
3687 client
.qualityLevel
= 1.5;
3688 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3690 RFB
.messages
.clientEncodings
.resetHistory();
3692 client
.qualityLevel
= null;
3693 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3695 RFB
.messages
.clientEncodings
.resetHistory();
3697 client
.qualityLevel
= undefined;
3698 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3700 RFB
.messages
.clientEncodings
.resetHistory();
3702 client
.qualityLevel
= {};
3703 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3706 it('should ignore integers out of range [0, 9]', function () {
3707 client
.qualityLevel
= -1;
3708 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3710 RFB
.messages
.clientEncodings
.resetHistory();
3712 client
.qualityLevel
= 10;
3713 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3716 it('should send clientEncodings with new quality value', function () {
3720 client
.qualityLevel
= newQuality
;
3721 expect(client
.qualityLevel
).to
.equal(newQuality
);
3722 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3723 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3726 it('should not send clientEncodings if quality is the same', function () {
3730 client
.qualityLevel
= newQuality
;
3731 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3732 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3734 RFB
.messages
.clientEncodings
.resetHistory();
3736 client
.qualityLevel
= newQuality
;
3737 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3740 it('should not send clientEncodings if not in connected state', function () {
3743 client
._rfbConnectionState
= '';
3745 client
.qualityLevel
= newQuality
;
3746 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3748 RFB
.messages
.clientEncodings
.resetHistory();
3750 client
._rfbConnectionState
= 'connnecting';
3752 client
.qualityLevel
= newQuality
;
3753 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3755 RFB
.messages
.clientEncodings
.resetHistory();
3757 client
._rfbConnectionState
= 'connected';
3759 client
.qualityLevel
= newQuality
;
3760 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3761 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingQualityLevel0
+ newQuality
);
3765 describe('Compression level setting', function () {
3766 const defaultCompression
= 2;
3770 beforeEach(function () {
3772 sinon
.spy(RFB
.messages
, "clientEncodings");
3775 afterEach(function () {
3776 RFB
.messages
.clientEncodings
.restore();
3779 it(`should equal ${defaultCompression} by default`, function () {
3780 expect(client
._compressionLevel
).to
.equal(defaultCompression
);
3783 it('should ignore non-integers when set', function () {
3784 client
.compressionLevel
= '1';
3785 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3787 RFB
.messages
.clientEncodings
.resetHistory();
3789 client
.compressionLevel
= 1.5;
3790 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3792 RFB
.messages
.clientEncodings
.resetHistory();
3794 client
.compressionLevel
= null;
3795 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3797 RFB
.messages
.clientEncodings
.resetHistory();
3799 client
.compressionLevel
= undefined;
3800 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3802 RFB
.messages
.clientEncodings
.resetHistory();
3804 client
.compressionLevel
= {};
3805 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3808 it('should ignore integers out of range [0, 9]', function () {
3809 client
.compressionLevel
= -1;
3810 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3812 RFB
.messages
.clientEncodings
.resetHistory();
3814 client
.compressionLevel
= 10;
3815 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3818 it('should send clientEncodings with new compression value', function () {
3822 client
.compressionLevel
= newCompression
;
3823 expect(client
.compressionLevel
).to
.equal(newCompression
);
3824 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3825 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3828 it('should not send clientEncodings if compression is the same', function () {
3832 client
.compressionLevel
= newCompression
;
3833 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3834 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3836 RFB
.messages
.clientEncodings
.resetHistory();
3838 client
.compressionLevel
= newCompression
;
3839 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3842 it('should not send clientEncodings if not in connected state', function () {
3845 client
._rfbConnectionState
= '';
3847 client
.compressionLevel
= newCompression
;
3848 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3850 RFB
.messages
.clientEncodings
.resetHistory();
3852 client
._rfbConnectionState
= 'connnecting';
3854 client
.compressionLevel
= newCompression
;
3855 expect(RFB
.messages
.clientEncodings
).to
.not
.have
.been
.called
;
3857 RFB
.messages
.clientEncodings
.resetHistory();
3859 client
._rfbConnectionState
= 'connected';
3861 client
.compressionLevel
= newCompression
;
3862 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
3863 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.pseudoEncodingCompressLevel0
+ newCompression
);
3868 describe('RFB messages', function () {
3871 before(function () {
3872 FakeWebSocket
.replace();
3873 sock
= new Websock();
3878 FakeWebSocket
.restore();
3881 describe('Extended Clipboard Handling Send', function () {
3882 beforeEach(function () {
3883 sinon
.spy(RFB
.messages
, 'clientCutText');
3886 afterEach(function () {
3887 RFB
.messages
.clientCutText
.restore();
3890 it('should call clientCutText with correct Caps data', function () {
3895 let expectedData
= new Uint8Array([0x1F, 0x00, 0x00, 0x05,
3896 0x00, 0x00, 0x00, 0x02,
3897 0x00, 0x00, 0x10, 0x19]);
3906 RFB
.messages
.extendedClipboardCaps(sock
, actions
, formats
);
3907 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3908 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3911 it('should call clientCutText with correct Request data', function () {
3912 let formats
= new Uint8Array([0x01]);
3913 let expectedData
= new Uint8Array([0x02, 0x00, 0x00, 0x01]);
3915 RFB
.messages
.extendedClipboardRequest(sock
, formats
);
3916 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3917 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3920 it('should call clientCutText with correct Notify data', function () {
3921 let formats
= new Uint8Array([0x01]);
3922 let expectedData
= new Uint8Array([0x08, 0x00, 0x00, 0x01]);
3924 RFB
.messages
.extendedClipboardNotify(sock
, formats
);
3925 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3926 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
);
3929 it('should call clientCutText with correct Provide data', function () {
3930 let testText
= "Test string";
3931 let expectedText
= encodeUTF8(testText
+ "\0");
3933 let deflatedData
= deflateWithSize(expectedText
);
3935 // Build Expected with flags and deflated data
3936 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3937 expectedData
[0] = 0x10; // The client capabilities
3938 expectedData
[1] = 0x00; // Reserved flags
3939 expectedData
[2] = 0x00; // Reserved flags
3940 expectedData
[3] = 0x01; // The formats client supports
3941 expectedData
.set(deflatedData
, 4);
3943 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3944 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3945 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3949 describe('End of line characters', function () {
3950 it('Carriage return', function () {
3952 let testText
= "Hello\rworld\r\r!";
3953 let expectedText
= encodeUTF8("Hello\r\nworld\r\n\r\n!\0");
3955 let deflatedData
= deflateWithSize(expectedText
);
3957 // Build Expected with flags and deflated data
3958 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3959 expectedData
[0] = 0x10; // The client capabilities
3960 expectedData
[1] = 0x00; // Reserved flags
3961 expectedData
[2] = 0x00; // Reserved flags
3962 expectedData
[3] = 0x01; // The formats client supports
3963 expectedData
.set(deflatedData
, 4);
3965 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3966 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3967 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3970 it('Carriage return Line feed', function () {
3972 let testText
= "Hello\r\n\r\nworld\r\n!";
3973 let expectedText
= encodeUTF8(testText
+ "\0");
3975 let deflatedData
= deflateWithSize(expectedText
);
3977 // Build Expected with flags and deflated data
3978 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3979 expectedData
[0] = 0x10; // The client capabilities
3980 expectedData
[1] = 0x00; // Reserved flags
3981 expectedData
[2] = 0x00; // Reserved flags
3982 expectedData
[3] = 0x01; // The formats client supports
3983 expectedData
.set(deflatedData
, 4);
3985 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
3986 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
3987 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
3990 it('Line feed', function () {
3991 let testText
= "Hello\n\n\nworld\n!";
3992 let expectedText
= encodeUTF8("Hello\r\n\r\n\r\nworld\r\n!\0");
3994 let deflatedData
= deflateWithSize(expectedText
);
3996 // Build Expected with flags and deflated data
3997 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
3998 expectedData
[0] = 0x10; // The client capabilities
3999 expectedData
[1] = 0x00; // Reserved flags
4000 expectedData
[2] = 0x00; // Reserved flags
4001 expectedData
[3] = 0x01; // The formats client supports
4002 expectedData
.set(deflatedData
, 4);
4004 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
4005 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
4006 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);
4009 it('Carriage return and Line feed mixed', function () {
4010 let testText
= "\rHello\r\n\rworld\n\n!";
4011 let expectedText
= encodeUTF8("\r\nHello\r\n\r\nworld\r\n\r\n!\0");
4013 let deflatedData
= deflateWithSize(expectedText
);
4015 // Build Expected with flags and deflated data
4016 let expectedData
= new Uint8Array(4 + deflatedData
.length
);
4017 expectedData
[0] = 0x10; // The client capabilities
4018 expectedData
[1] = 0x00; // Reserved flags
4019 expectedData
[2] = 0x00; // Reserved flags
4020 expectedData
[3] = 0x01; // The formats client supports
4021 expectedData
.set(deflatedData
, 4);
4023 RFB
.messages
.extendedClipboardProvide(sock
, [0x01], [testText
]);
4024 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledOnce
;
4025 expect(RFB
.messages
.clientCutText
).to
.have
.been
.calledWith(sock
, expectedData
, true);