]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.rfb.js
Add transpilation for IE11 and skip linux tests
[mirror_novnc.git] / tests / test.rfb.js
CommitLineData
b1dee947
SR
1var expect = chai.expect;
2
dfae3209
SR
3import RFB from '../core/rfb.js';
4import Websock from '../core/websock.js';
69411b9e 5import { encodings } from '../core/encodings.js';
dfae3209
SR
6
7import FakeWebSocket from './fake.websocket.js';
0aaf59c2 8import sinon from '../vendor/sinon.js';
dfae3209 9
9b84f516
PO
10/* UIEvent constructor polyfill for IE */
11(function () {
12 if (typeof window.UIEvent === "function") return;
13
14 function UIEvent ( event, params ) {
15 params = params || { bubbles: false, cancelable: false, view: window, detail: undefined };
16 var evt = document.createEvent( 'UIEvent' );
17 evt.initUIEvent( event, params.bubbles, params.cancelable, params.view, params.detail );
18 return evt;
19 }
20
21 UIEvent.prototype = window.UIEvent.prototype;
22
23 window.UIEvent = UIEvent;
24})();
25
3949a095
SR
26var push8 = function (arr, num) {
27 "use strict";
28 arr.push(num & 0xFF);
29};
30
31var push16 = function (arr, num) {
32 "use strict";
33 arr.push((num >> 8) & 0xFF,
34 num & 0xFF);
35};
36
37var push32 = function (arr, num) {
38 "use strict";
39 arr.push((num >> 24) & 0xFF,
40 (num >> 16) & 0xFF,
41 (num >> 8) & 0xFF,
42 num & 0xFF);
43};
44
b1dee947 45describe('Remote Frame Buffer Protocol Client', function() {
2f4516f2 46 var clock;
9b84f516 47 var raf;
2f4516f2 48
b1dee947
SR
49 before(FakeWebSocket.replace);
50 after(FakeWebSocket.restore);
51
38781d93 52 before(function () {
2f4516f2 53 this.clock = clock = sinon.useFakeTimers();
9b84f516
PO
54 // sinon doesn't support this yet
55 raf = window.requestAnimationFrame;
56 window.requestAnimationFrame = setTimeout;
38781d93
SR
57 // Use a single set of buffers instead of reallocating to
58 // speed up tests
59 var sock = new Websock();
9ff86fb7 60 var _sQ = new Uint8Array(sock._sQbufferSize);
38781d93
SR
61 var rQ = new Uint8Array(sock._rQbufferSize);
62
63 Websock.prototype._old_allocate_buffers = Websock.prototype._allocate_buffers;
64 Websock.prototype._allocate_buffers = function () {
9ff86fb7 65 this._sQ = _sQ;
38781d93
SR
66 this._rQ = rQ;
67 };
68
69 });
70
71 after(function () {
72 Websock.prototype._allocate_buffers = Websock.prototype._old_allocate_buffers;
73 this.clock.restore();
9b84f516 74 window.requestAnimationFrame = raf;
38781d93
SR
75 });
76
9b84f516 77 var container;
bb25d3d6
PO
78 var rfbs;
79
80 beforeEach(function () {
9b84f516
PO
81 // Create a container element for all RFB objects to attach to
82 container = document.createElement('div');
83 container.style.width = "100%";
84 container.style.height = "100%";
85 document.body.appendChild(container);
86
87 // And track all created RFB objects
bb25d3d6
PO
88 rfbs = [];
89 });
90 afterEach(function () {
91 // Make sure every created RFB object is properly cleaned up
92 // or they might affect subsequent tests
93 rfbs.forEach(function (rfb) {
94 rfb.disconnect();
95 expect(rfb._disconnect).to.have.been.called;
96 });
97 rfbs = [];
9b84f516
PO
98
99 document.body.removeChild(container);
100 container = null;
bb25d3d6
PO
101 });
102
2f4516f2
PO
103 function make_rfb (url, options) {
104 url = url || 'wss://host:8675';
9b84f516 105 var rfb = new RFB(container, url, options);
2f4516f2
PO
106 clock.tick();
107 rfb._sock._websocket._open();
108 rfb._rfb_connection_state = 'connected';
bb25d3d6
PO
109 sinon.spy(rfb, "_disconnect");
110 rfbs.push(rfb);
2f4516f2
PO
111 return rfb;
112 }
b1dee947 113
2f4516f2
PO
114 describe('Connecting/Disconnecting', function () {
115 describe('#RFB', function () {
c2a4d3ef 116 it('should set the current state to "connecting"', function () {
9b84f516 117 var client = new RFB(document.createElement('div'), 'wss://host:8675');
ee5cae9f 118 client._rfb_connection_state = '';
2f4516f2 119 this.clock.tick();
ee5cae9f 120 expect(client._rfb_connection_state).to.equal('connecting');
b1dee947
SR
121 });
122
2f4516f2 123 it('should actually connect to the websocket', function () {
9b84f516 124 var client = new RFB(document.createElement('div'), 'ws://HOST:8675/PATH');
2f4516f2
PO
125 sinon.spy(client._sock, 'open');
126 this.clock.tick();
127 expect(client._sock.open).to.have.been.calledOnce;
128 expect(client._sock.open).to.have.been.calledWith('ws://HOST:8675/PATH');
b1dee947 129 });
b1dee947
SR
130 });
131
132 describe('#disconnect', function () {
2f4516f2
PO
133 var client;
134 beforeEach(function () {
135 client = make_rfb();
136 });
b1dee947 137
ee5cae9f
SM
138 it('should go to state "disconnecting" before "disconnected"', function () {
139 sinon.spy(client, '_updateConnectionState');
b1dee947 140 client.disconnect();
ee5cae9f
SM
141 expect(client._updateConnectionState).to.have.been.calledTwice;
142 expect(client._updateConnectionState.getCall(0).args[0])
143 .to.equal('disconnecting');
144 expect(client._updateConnectionState.getCall(1).args[0])
145 .to.equal('disconnected');
146 expect(client._rfb_connection_state).to.equal('disconnected');
b1dee947 147 });
155d78b3
JS
148
149 it('should unregister error event handler', function () {
150 sinon.spy(client._sock, 'off');
151 client.disconnect();
152 expect(client._sock.off).to.have.been.calledWith('error');
153 });
154
155 it('should unregister message event handler', function () {
156 sinon.spy(client._sock, 'off');
157 client.disconnect();
158 expect(client._sock.off).to.have.been.calledWith('message');
159 });
160
161 it('should unregister open event handler', function () {
162 sinon.spy(client._sock, 'off');
163 client.disconnect();
164 expect(client._sock.off).to.have.been.calledWith('open');
165 });
b1dee947
SR
166 });
167
430f00d6 168 describe('#sendCredentials', function () {
2f4516f2
PO
169 var client;
170 beforeEach(function () {
171 client = make_rfb();
172 client._rfb_connection_state = 'connecting';
173 });
174
430f00d6
PO
175 it('should set the rfb credentials properly"', function () {
176 client.sendCredentials({ password: 'pass' });
177 expect(client._rfb_credentials).to.deep.equal({ password: 'pass' });
b1dee947
SR
178 });
179
180 it('should call init_msg "soon"', function () {
181 client._init_msg = sinon.spy();
430f00d6 182 client.sendCredentials({ password: 'pass' });
b1dee947
SR
183 this.clock.tick(5);
184 expect(client._init_msg).to.have.been.calledOnce;
185 });
186 });
057b8fec 187 });
b1dee947 188
057b8fec
PO
189 describe('Public API Basic Behavior', function () {
190 var client;
191 beforeEach(function () {
192 client = make_rfb();
057b8fec 193 });
b1dee947 194
057b8fec 195 describe('#sendCtrlAlDel', function () {
b1dee947 196 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
89d2837f 197 var expected = {_sQ: new Uint8Array(48), _sQlen: 0, flush: function () {}};
9ff86fb7
SR
198 RFB.messages.keyEvent(expected, 0xFFE3, 1);
199 RFB.messages.keyEvent(expected, 0xFFE9, 1);
200 RFB.messages.keyEvent(expected, 0xFFFF, 1);
201 RFB.messages.keyEvent(expected, 0xFFFF, 0);
202 RFB.messages.keyEvent(expected, 0xFFE9, 0);
203 RFB.messages.keyEvent(expected, 0xFFE3, 0);
b1dee947
SR
204
205 client.sendCtrlAltDel();
9ff86fb7 206 expect(client._sock).to.have.sent(expected._sQ);
b1dee947
SR
207 });
208
209 it('should not send the keys if we are not in a normal state', function () {
057b8fec 210 sinon.spy(client._sock, 'flush');
bb25d3d6 211 client._rfb_connection_state = "connecting";
b1dee947 212 client.sendCtrlAltDel();
9ff86fb7 213 expect(client._sock.flush).to.not.have.been.called;
b1dee947
SR
214 });
215
216 it('should not send the keys if we are set as view_only', function () {
057b8fec 217 sinon.spy(client._sock, 'flush');
747b4623 218 client._viewOnly = true;
b1dee947 219 client.sendCtrlAltDel();
9ff86fb7 220 expect(client._sock.flush).to.not.have.been.called;
b1dee947
SR
221 });
222 });
223
224 describe('#sendKey', function () {
b1dee947 225 it('should send a single key with the given code and state (down = true)', function () {
89d2837f 226 var expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: function () {}};
9ff86fb7 227 RFB.messages.keyEvent(expected, 123, 1);
94f5cf05 228 client.sendKey(123, 'Key123', true);
9ff86fb7 229 expect(client._sock).to.have.sent(expected._sQ);
b1dee947
SR
230 });
231
232 it('should send both a down and up event if the state is not specified', function () {
89d2837f 233 var expected = {_sQ: new Uint8Array(16), _sQlen: 0, flush: function () {}};
9ff86fb7
SR
234 RFB.messages.keyEvent(expected, 123, 1);
235 RFB.messages.keyEvent(expected, 123, 0);
94f5cf05 236 client.sendKey(123, 'Key123');
9ff86fb7 237 expect(client._sock).to.have.sent(expected._sQ);
b1dee947
SR
238 });
239
240 it('should not send the key if we are not in a normal state', function () {
057b8fec 241 sinon.spy(client._sock, 'flush');
bb25d3d6 242 client._rfb_connection_state = "connecting";
94f5cf05 243 client.sendKey(123, 'Key123');
9ff86fb7 244 expect(client._sock.flush).to.not.have.been.called;
b1dee947
SR
245 });
246
247 it('should not send the key if we are set as view_only', function () {
057b8fec 248 sinon.spy(client._sock, 'flush');
747b4623 249 client._viewOnly = true;
94f5cf05 250 client.sendKey(123, 'Key123');
9ff86fb7 251 expect(client._sock.flush).to.not.have.been.called;
b1dee947 252 });
94f5cf05
PO
253
254 it('should send QEMU extended events if supported', function () {
255 client._qemuExtKeyEventSupported = true;
256 var expected = {_sQ: new Uint8Array(12), _sQlen: 0, flush: function () {}};
257 RFB.messages.QEMUExtendedKeyEvent(expected, 0x20, true, 0x0039);
258 client.sendKey(0x20, 'Space', true);
259 expect(client._sock).to.have.sent(expected._sQ);
260 });
be70fe0a
PO
261
262 it('should not send QEMU extended events if unknown key code', function () {
263 client._qemuExtKeyEventSupported = true;
264 var expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: function () {}};
265 RFB.messages.keyEvent(expected, 123, 1);
266 client.sendKey(123, 'FooBar', true);
267 expect(client._sock).to.have.sent(expected._sQ);
268 });
b1dee947
SR
269 });
270
9b84f516
PO
271 describe('#focus', function () {
272 it('should move focus to canvas object', function () {
273 client._canvas.focus = sinon.spy();
274 client.focus();
275 expect(client._canvas.focus).to.have.been.called.once;
276 });
277 });
278
279 describe('#blur', function () {
280 it('should remove focus from canvas object', function () {
281 client._canvas.blur = sinon.spy();
282 client.blur();
283 expect(client._canvas.blur).to.have.been.called.once;
284 });
285 });
286
b1dee947 287 describe('#clipboardPasteFrom', function () {
b1dee947 288 it('should send the given text in a paste event', function () {
2bb8b28d
SM
289 var expected = {_sQ: new Uint8Array(11), _sQlen: 0,
290 _sQbufferSize: 11, flush: function () {}};
9ff86fb7 291 RFB.messages.clientCutText(expected, 'abc');
b1dee947 292 client.clipboardPasteFrom('abc');
9ff86fb7 293 expect(client._sock).to.have.sent(expected._sQ);
b1dee947
SR
294 });
295
2bb8b28d
SM
296 it('should flush multiple times for large clipboards', function () {
297 sinon.spy(client._sock, 'flush');
298 let long_text = "";
299 for (let i = 0; i < client._sock._sQbufferSize + 100; i++) {
300 long_text += 'a';
301 }
302 client.clipboardPasteFrom(long_text);
303 expect(client._sock.flush).to.have.been.calledTwice;
304 });
305
b1dee947 306 it('should not send the text if we are not in a normal state', function () {
057b8fec 307 sinon.spy(client._sock, 'flush');
bb25d3d6 308 client._rfb_connection_state = "connecting";
b1dee947 309 client.clipboardPasteFrom('abc');
9ff86fb7 310 expect(client._sock.flush).to.not.have.been.called;
b1dee947
SR
311 });
312 });
313
314 describe("XVP operations", function () {
315 beforeEach(function () {
b1dee947
SR
316 client._rfb_xvp_ver = 1;
317 });
318
cd523e8f
PO
319 it('should send the shutdown signal on #machineShutdown', function () {
320 client.machineShutdown();
9ff86fb7 321 expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
b1dee947
SR
322 });
323
cd523e8f
PO
324 it('should send the reboot signal on #machineReboot', function () {
325 client.machineReboot();
9ff86fb7 326 expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
b1dee947
SR
327 });
328
cd523e8f
PO
329 it('should send the reset signal on #machineReset', function () {
330 client.machineReset();
9ff86fb7 331 expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
b1dee947
SR
332 });
333
b1dee947 334 it('should not send XVP operations with higher versions than we support', function () {
057b8fec 335 sinon.spy(client._sock, 'flush');
cd523e8f 336 client._xvpOp(2, 7);
9ff86fb7 337 expect(client._sock.flush).to.not.have.been.called;
b1dee947
SR
338 });
339 });
340 });
341
9b84f516
PO
342 describe('Clipping', function () {
343 var client;
344 beforeEach(function () {
345 client = make_rfb();
346 container.style.width = '70px';
347 container.style.height = '80px';
348 client.clipViewport = true;
349 });
350
351 it('should update display clip state when changing the property', function () {
352 var spy = sinon.spy(client._display, "clipViewport", ["set"]);
353
354 client.clipViewport = false;
355 expect(spy.set).to.have.been.calledOnce;
356 expect(spy.set).to.have.been.calledWith(false);
357 spy.set.reset();
358
359 client.clipViewport = true;
360 expect(spy.set).to.have.been.calledOnce;
361 expect(spy.set).to.have.been.calledWith(true);
362 });
363
364 it('should update the viewport when the container size changes', function () {
365 sinon.spy(client._display, "viewportChangeSize");
366
367 container.style.width = '40px';
368 container.style.height = '50px';
369 var event = new UIEvent('resize');
370 window.dispatchEvent(event);
371 clock.tick();
372
373 expect(client._display.viewportChangeSize).to.have.been.calledOnce;
374 expect(client._display.viewportChangeSize).to.have.been.calledWith(40, 50);
375 });
376
377 it('should update the viewport when the remote session resizes', function () {
378 // Simple ExtendedDesktopSize FBU message
379 var incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
380 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
381 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
383 0x00, 0x00, 0x00, 0x00 ];
384
385 sinon.spy(client._display, "viewportChangeSize");
386
387 client._sock._websocket._receive_data(new Uint8Array(incoming));
388
389 // FIXME: Display implicitly calls viewportChangeSize() when
390 // resizing the framebuffer, hence calledTwice.
391 expect(client._display.viewportChangeSize).to.have.been.calledTwice;
392 expect(client._display.viewportChangeSize).to.have.been.calledWith(70, 80);
393 });
394
395 it('should not update the viewport if not clipping', function () {
396 client.clipViewport = false;
397 sinon.spy(client._display, "viewportChangeSize");
398
399 container.style.width = '40px';
400 container.style.height = '50px';
401 var event = new UIEvent('resize');
402 window.dispatchEvent(event);
403 clock.tick();
404
405 expect(client._display.viewportChangeSize).to.not.have.been.called;
406 });
407
408 it('should not update the viewport if scaling', function () {
409 client.scaleViewport = true;
410 sinon.spy(client._display, "viewportChangeSize");
411
412 container.style.width = '40px';
413 container.style.height = '50px';
414 var event = new UIEvent('resize');
415 window.dispatchEvent(event);
416 clock.tick();
417
418 expect(client._display.viewportChangeSize).to.not.have.been.called;
419 });
420
421 describe('Dragging', function () {
422 beforeEach(function () {
423 client.dragViewport = true;
424 sinon.spy(RFB.messages, "pointerEvent");
425 });
426
427 afterEach(function () {
428 RFB.messages.pointerEvent.restore();
429 });
430
431 it('should not send button messages when initiating viewport dragging', function () {
432 client._handleMouseButton(13, 9, 0x001);
433 expect(RFB.messages.pointerEvent).to.not.have.been.called;
434 });
435
436 it('should send button messages when release without movement', function () {
437 // Just up and down
438 client._handleMouseButton(13, 9, 0x001);
439 client._handleMouseButton(13, 9, 0x000);
440 expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
441
442 RFB.messages.pointerEvent.reset();
443
444 // Small movement
445 client._handleMouseButton(13, 9, 0x001);
446 client._handleMouseMove(15, 14);
447 client._handleMouseButton(15, 14, 0x000);
448 expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
449 });
450
451 it('should send button message directly when drag is disabled', function () {
452 client.dragViewport = false;
453 client._handleMouseButton(13, 9, 0x001);
454 expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
455 });
456
457 it('should be initiate viewport dragging on sufficient movement', function () {
458 sinon.spy(client._display, "viewportChangePos");
459
460 // Too small movement
461
462 client._handleMouseButton(13, 9, 0x001);
463 client._handleMouseMove(18, 9);
464
465 expect(RFB.messages.pointerEvent).to.not.have.been.called;
466 expect(client._display.viewportChangePos).to.not.have.been.called;
467
468 // Sufficient movement
469
470 client._handleMouseMove(43, 9);
471
472 expect(RFB.messages.pointerEvent).to.not.have.been.called;
473 expect(client._display.viewportChangePos).to.have.been.calledOnce;
474 expect(client._display.viewportChangePos).to.have.been.calledWith(-30, 0);
475
476 client._display.viewportChangePos.reset();
477
478 // Now a small movement should move right away
479
480 client._handleMouseMove(43, 14);
481
482 expect(RFB.messages.pointerEvent).to.not.have.been.called;
483 expect(client._display.viewportChangePos).to.have.been.calledOnce;
484 expect(client._display.viewportChangePos).to.have.been.calledWith(0, -5);
485 });
486
487 it('should not send button messages when dragging ends', function () {
488 // First the movement
489
490 client._handleMouseButton(13, 9, 0x001);
491 client._handleMouseMove(43, 9);
492 client._handleMouseButton(43, 9, 0x000);
493
494 expect(RFB.messages.pointerEvent).to.not.have.been.called;
495 });
496
497 it('should terminate viewport dragging on a button up event', function () {
498 // First the dragging movement
499
500 client._handleMouseButton(13, 9, 0x001);
501 client._handleMouseMove(43, 9);
502 client._handleMouseButton(43, 9, 0x000);
503
504 // Another movement now should not move the viewport
505
506 sinon.spy(client._display, "viewportChangePos");
507
508 client._handleMouseMove(43, 59);
509
510 expect(client._display.viewportChangePos).to.not.have.been.called;
511 });
512 });
513 });
514
515 describe('Scaling', function () {
516 var client;
517 beforeEach(function () {
518 client = make_rfb();
519 container.style.width = '70px';
520 container.style.height = '80px';
521 client.scaleViewport = true;
522 });
523
524 it('should update display scale factor when changing the property', function () {
525 var spy = sinon.spy(client._display, "scale", ["set"]);
526 sinon.spy(client._display, "autoscale");
527
528 client.scaleViewport = false;
529 expect(spy.set).to.have.been.calledOnce;
530 expect(spy.set).to.have.been.calledWith(1.0);
531 expect(client._display.autoscale).to.not.have.been.called;
532
533 client.scaleViewport = true;
534 expect(client._display.autoscale).to.have.been.calledOnce;
535 expect(client._display.autoscale).to.have.been.calledWith(70, 80);
536 });
537
538 it('should update the clipping setting when changing the property', function () {
539 client.clipViewport = true;
540
541 var spy = sinon.spy(client._display, "clipViewport", ["set"]);
542
543 client.scaleViewport = false;
544 expect(spy.set).to.have.been.calledOnce;
545 expect(spy.set).to.have.been.calledWith(true);
546
547 spy.set.reset();
548
549 client.scaleViewport = true;
550 expect(spy.set).to.have.been.calledOnce;
551 expect(spy.set).to.have.been.calledWith(false);
552 });
553
554 it('should update the scaling when the container size changes', function () {
555 sinon.spy(client._display, "autoscale");
556
557 container.style.width = '40px';
558 container.style.height = '50px';
559 var event = new UIEvent('resize');
560 window.dispatchEvent(event);
561 clock.tick();
562
563 expect(client._display.autoscale).to.have.been.calledOnce;
564 expect(client._display.autoscale).to.have.been.calledWith(40, 50);
565 });
566
567 it('should update the scaling when the remote session resizes', function () {
568 // Simple ExtendedDesktopSize FBU message
569 var incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
570 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
571 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
573 0x00, 0x00, 0x00, 0x00 ];
574
575 sinon.spy(client._display, "autoscale");
576
577 client._sock._websocket._receive_data(new Uint8Array(incoming));
578
579 expect(client._display.autoscale).to.have.been.calledOnce;
580 expect(client._display.autoscale).to.have.been.calledWith(70, 80);
581 });
582
583 it('should not update the display scale factor if not scaling', function () {
584 client.scaleViewport = false;
585
586 sinon.spy(client._display, "autoscale");
587
588 container.style.width = '40px';
589 container.style.height = '50px';
590 var event = new UIEvent('resize');
591 window.dispatchEvent(event);
592 clock.tick();
593
594 expect(client._display.autoscale).to.not.have.been.called;
595 });
596 });
597
598 describe('Remote resize', function () {
599 var client;
600 beforeEach(function () {
601 client = make_rfb();
602 client._supportsSetDesktopSize = true;
603 client.resizeSession = true;
604 container.style.width = '70px';
605 container.style.height = '80px';
606 sinon.spy(RFB.messages, "setDesktopSize");
607 });
608
609 afterEach(function () {
610 RFB.messages.setDesktopSize.restore();
611 });
612
613 it('should only request a resize when turned on', function () {
614 client.resizeSession = false;
615 expect(RFB.messages.setDesktopSize).to.not.have.been.called;
616 client.resizeSession = true;
617 expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
618 });
619
620 it('should request a resize when initially connecting', function () {
621 // Simple ExtendedDesktopSize FBU message
622 var incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
623 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
624 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
625 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
626 0x00, 0x00, 0x00, 0x00 ];
627
628 // First message should trigger a resize
629
630 client._supportsSetDesktopSize = false;
631
632 client._sock._websocket._receive_data(new Uint8Array(incoming));
633
634 expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
635 expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 70, 80, 0, 0);
636
637 RFB.messages.setDesktopSize.reset();
638
639 // Second message should not trigger a resize
640
641 client._sock._websocket._receive_data(new Uint8Array(incoming));
642
643 expect(RFB.messages.setDesktopSize).to.not.have.been.called;
644 });
645
646 it('should request a resize when the container resizes', function () {
647 container.style.width = '40px';
648 container.style.height = '50px';
649 var event = new UIEvent('resize');
650 window.dispatchEvent(event);
651 clock.tick(1000);
652
653 expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
654 expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 40, 50, 0, 0);
655 });
656
657 it('should not resize until the container size is stable', function () {
658 container.style.width = '20px';
659 container.style.height = '30px';
8727f598
JD
660 var event1 = new UIEvent('resize');
661 window.dispatchEvent(event1);
9b84f516
PO
662 clock.tick(400);
663
664 expect(RFB.messages.setDesktopSize).to.not.have.been.called;
665
666 container.style.width = '40px';
667 container.style.height = '50px';
8727f598
JD
668 var event2 = new UIEvent('resize');
669 window.dispatchEvent(event2);
9b84f516
PO
670 clock.tick(400);
671
672 expect(RFB.messages.setDesktopSize).to.not.have.been.called;
673
674 clock.tick(200);
675
676 expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
677 expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 40, 50, 0, 0);
678 });
679
680 it('should not resize when resize is disabled', function () {
681 client._resizeSession = false;
682
683 container.style.width = '40px';
684 container.style.height = '50px';
685 var event = new UIEvent('resize');
686 window.dispatchEvent(event);
687 clock.tick(1000);
688
689 expect(RFB.messages.setDesktopSize).to.not.have.been.called;
690 });
691
692 it('should not resize when resize is not supported', function () {
693 client._supportsSetDesktopSize = false;
694
695 container.style.width = '40px';
696 container.style.height = '50px';
697 var event = new UIEvent('resize');
698 window.dispatchEvent(event);
699 clock.tick(1000);
700
701 expect(RFB.messages.setDesktopSize).to.not.have.been.called;
702 });
703
704 it('should not resize when in view only mode', function () {
705 client._viewOnly = true;
706
707 container.style.width = '40px';
708 container.style.height = '50px';
709 var event = new UIEvent('resize');
710 window.dispatchEvent(event);
711 clock.tick(1000);
712
713 expect(RFB.messages.setDesktopSize).to.not.have.been.called;
714 });
715
716 it('should not try to override a server resize', function () {
717 // Simple ExtendedDesktopSize FBU message
718 var incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
719 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
720 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
721 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
722 0x00, 0x00, 0x00, 0x00 ];
723
724 client._sock._websocket._receive_data(new Uint8Array(incoming));
725
726 expect(RFB.messages.setDesktopSize).to.not.have.been.called;
727 });
728 });
729
b1dee947 730 describe('Misc Internals', function () {
c00ee156 731 describe('#_updateConnectionState', function () {
b1dee947
SR
732 var client;
733 beforeEach(function () {
b1dee947
SR
734 client = make_rfb();
735 });
736
c2a4d3ef 737 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
b1dee947
SR
738 var spy = sinon.spy();
739 client._disconnTimer = setTimeout(spy, 50);
2f4516f2
PO
740 client._rfb_connection_state = 'connecting';
741 client._updateConnectionState('connected');
b1dee947
SR
742 this.clock.tick(51);
743 expect(spy).to.not.have.been.called;
744 expect(client._disconnTimer).to.be.null;
745 });
3bb12056 746
3bb12056 747 it('should set the rfb_connection_state', function () {
bb25d3d6
PO
748 client._rfb_connection_state = 'connecting';
749 client._updateConnectionState('connected');
750 expect(client._rfb_connection_state).to.equal('connected');
3bb12056
SM
751 });
752
753 it('should not change the state when we are disconnected', function () {
bb25d3d6
PO
754 client.disconnect();
755 expect(client._rfb_connection_state).to.equal('disconnected');
b2e961d4
SM
756 client._updateConnectionState('connecting');
757 expect(client._rfb_connection_state).to.not.equal('connecting');
3bb12056
SM
758 });
759
760 it('should ignore state changes to the same state', function () {
ee5cae9f 761 var connectSpy = sinon.spy();
ee5cae9f 762 client.addEventListener("connect", connectSpy);
ee5cae9f 763
bb25d3d6 764 expect(client._rfb_connection_state).to.equal('connected');
ee5cae9f
SM
765 client._updateConnectionState('connected');
766 expect(connectSpy).to.not.have.been.called;
767
bb25d3d6
PO
768 client.disconnect();
769
770 var disconnectSpy = sinon.spy();
771 client.addEventListener("disconnect", disconnectSpy);
772
773 expect(client._rfb_connection_state).to.equal('disconnected');
ee5cae9f
SM
774 client._updateConnectionState('disconnected');
775 expect(disconnectSpy).to.not.have.been.called;
b2e961d4
SM
776 });
777
778 it('should ignore illegal state changes', function () {
e89eef94 779 var spy = sinon.spy();
ee5cae9f 780 client.addEventListener("disconnect", spy);
b2e961d4
SM
781 client._updateConnectionState('disconnected');
782 expect(client._rfb_connection_state).to.not.equal('disconnected');
3bb12056
SM
783 expect(spy).to.not.have.been.called;
784 });
785 });
786
787 describe('#_fail', function () {
788 var client;
789 beforeEach(function () {
3bb12056 790 client = make_rfb();
3bb12056
SM
791 });
792
3bb12056
SM
793 it('should close the WebSocket connection', function () {
794 sinon.spy(client._sock, 'close');
795 client._fail();
796 expect(client._sock.close).to.have.been.calledOnce;
797 });
798
799 it('should transition to disconnected', function () {
800 sinon.spy(client, '_updateConnectionState');
801 client._fail();
802 this.clock.tick(2000);
803 expect(client._updateConnectionState).to.have.been.called;
804 expect(client._rfb_connection_state).to.equal('disconnected');
805 });
806
d472f3f1
SM
807 it('should set clean_disconnect variable', function () {
808 client._rfb_clean_disconnect = true;
b2e961d4 809 client._rfb_connection_state = 'connected';
d472f3f1
SM
810 client._fail();
811 expect(client._rfb_clean_disconnect).to.be.false;
67cd2072
SM
812 });
813
d472f3f1 814 it('should result in disconnect event with clean set to false', function () {
b2e961d4 815 client._rfb_connection_state = 'connected';
e89eef94
PO
816 var spy = sinon.spy();
817 client.addEventListener("disconnect", spy);
d472f3f1 818 client._fail();
3bb12056
SM
819 this.clock.tick(2000);
820 expect(spy).to.have.been.calledOnce;
d472f3f1 821 expect(spy.args[0][0].detail.clean).to.be.false;
3bb12056
SM
822 });
823
b1dee947
SR
824 });
825 });
826
c00ee156 827 describe('Connection States', function () {
ee5cae9f
SM
828 describe('connecting', function () {
829 it('should open the websocket connection', function () {
9b84f516 830 var client = new RFB(document.createElement('div'),
ee5cae9f
SM
831 'ws://HOST:8675/PATH');
832 sinon.spy(client._sock, 'open');
833 this.clock.tick();
834 expect(client._sock.open).to.have.been.calledOnce;
835 });
836 });
837
838 describe('connected', function () {
839 var client;
840 beforeEach(function () {
841 client = make_rfb();
842 });
843
844 it('should result in a connect event if state becomes connected', function () {
845 var spy = sinon.spy();
846 client.addEventListener("connect", spy);
847 client._rfb_connection_state = 'connecting';
848 client._updateConnectionState('connected');
849 expect(spy).to.have.been.calledOnce;
850 });
851
852 it('should not result in a connect event if the state is not "connected"', function () {
853 var spy = sinon.spy();
854 client.addEventListener("connect", spy);
855 client._sock._websocket.open = function () {}; // explicitly don't call onopen
856 client._updateConnectionState('connecting');
857 expect(spy).to.not.have.been.called;
858 });
859 });
860
c2a4d3ef 861 describe('disconnecting', function () {
b1dee947
SR
862 var client;
863 beforeEach(function () {
b1dee947 864 client = make_rfb();
b1dee947
SR
865 });
866
3bb12056
SM
867 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
868 sinon.spy(client, '_updateConnectionState');
b1dee947 869 client._sock._websocket.close = function () {}; // explicitly don't call onclose
c2a4d3ef 870 client._updateConnectionState('disconnecting');
68e09edc 871 this.clock.tick(3 * 1000);
3bb12056
SM
872 expect(client._updateConnectionState).to.have.been.calledTwice;
873 expect(client._rfb_disconnect_reason).to.not.equal("");
874 expect(client._rfb_connection_state).to.equal("disconnected");
b1dee947
SR
875 });
876
877 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
c2a4d3ef 878 client._updateConnectionState('disconnecting');
68e09edc 879 this.clock.tick(3 * 1000 / 2);
b1dee947 880 client._sock._websocket.close();
68e09edc 881 this.clock.tick(3 * 1000 / 2 + 1);
c00ee156 882 expect(client._rfb_connection_state).to.equal('disconnected');
b1dee947
SR
883 });
884
885 it('should close the WebSocket connection', function () {
886 sinon.spy(client._sock, 'close');
c2a4d3ef 887 client._updateConnectionState('disconnecting');
3bb12056 888 expect(client._sock.close).to.have.been.calledOnce;
b1dee947 889 });
bb25d3d6
PO
890
891 it('should not result in a disconnect event', function () {
892 var spy = sinon.spy();
893 client.addEventListener("disconnect", spy);
894 client._sock._websocket.close = function () {}; // explicitly don't call onclose
895 client._updateConnectionState('disconnecting');
896 expect(spy).to.not.have.been.called;
897 });
b1dee947
SR
898 });
899
3bb12056 900 describe('disconnected', function () {
b1dee947 901 var client;
bb25d3d6 902 beforeEach(function () {
9b84f516 903 client = new RFB(document.createElement('div'), 'ws://HOST:8675/PATH');
bb25d3d6 904 });
b1dee947 905
d472f3f1 906 it('should result in a disconnect event if state becomes "disconnected"', function () {
e89eef94
PO
907 var spy = sinon.spy();
908 client.addEventListener("disconnect", spy);
3bb12056 909 client._rfb_connection_state = 'disconnecting';
3bb12056 910 client._updateConnectionState('disconnected');
3bb12056 911 expect(spy).to.have.been.calledOnce;
d472f3f1 912 expect(spy.args[0][0].detail.clean).to.be.true;
b1dee947
SR
913 });
914
d472f3f1 915 it('should result in a disconnect event without msg when no reason given', function () {
e89eef94
PO
916 var spy = sinon.spy();
917 client.addEventListener("disconnect", spy);
3bb12056
SM
918 client._rfb_connection_state = 'disconnecting';
919 client._rfb_disconnect_reason = "";
920 client._updateConnectionState('disconnected');
3bb12056
SM
921 expect(spy).to.have.been.calledOnce;
922 expect(spy.args[0].length).to.equal(1);
b1dee947 923 });
b1dee947 924 });
b1dee947
SR
925 });
926
927 describe('Protocol Initialization States', function () {
057b8fec
PO
928 var client;
929 beforeEach(function () {
930 client = make_rfb();
2f4516f2 931 client._rfb_connection_state = 'connecting';
057b8fec 932 });
b1dee947 933
057b8fec 934 describe('ProtocolVersion', function () {
b1dee947
SR
935 function send_ver (ver, client) {
936 var arr = new Uint8Array(12);
937 for (var i = 0; i < ver.length; i++) {
938 arr[i+4] = ver.charCodeAt(i);
939 }
940 arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' ';
941 arr[11] = '\n';
942 client._sock._websocket._receive_data(arr);
943 }
944
945 describe('version parsing', function () {
b1dee947
SR
946 it('should interpret version 003.003 as version 3.3', function () {
947 send_ver('003.003', client);
948 expect(client._rfb_version).to.equal(3.3);
949 });
950
951 it('should interpret version 003.006 as version 3.3', function () {
952 send_ver('003.006', client);
953 expect(client._rfb_version).to.equal(3.3);
954 });
955
956 it('should interpret version 003.889 as version 3.3', function () {
957 send_ver('003.889', client);
958 expect(client._rfb_version).to.equal(3.3);
959 });
960
961 it('should interpret version 003.007 as version 3.7', function () {
962 send_ver('003.007', client);
963 expect(client._rfb_version).to.equal(3.7);
964 });
965
966 it('should interpret version 003.008 as version 3.8', function () {
967 send_ver('003.008', client);
968 expect(client._rfb_version).to.equal(3.8);
969 });
970
971 it('should interpret version 004.000 as version 3.8', function () {
972 send_ver('004.000', client);
973 expect(client._rfb_version).to.equal(3.8);
974 });
975
976 it('should interpret version 004.001 as version 3.8', function () {
977 send_ver('004.001', client);
978 expect(client._rfb_version).to.equal(3.8);
979 });
980
49aa5b81
LOH
981 it('should interpret version 005.000 as version 3.8', function () {
982 send_ver('005.000', client);
983 expect(client._rfb_version).to.equal(3.8);
984 });
985
b1dee947 986 it('should fail on an invalid version', function () {
3bb12056 987 sinon.spy(client, "_fail");
b1dee947 988 send_ver('002.000', client);
3bb12056 989 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
990 });
991 });
992
b1dee947
SR
993 it('should send back the interpreted version', function () {
994 send_ver('004.000', client);
995
996 var expected_str = 'RFB 003.008\n';
997 var expected = [];
998 for (var i = 0; i < expected_str.length; i++) {
999 expected[i] = expected_str.charCodeAt(i);
1000 }
1001
9ff86fb7 1002 expect(client._sock).to.have.sent(new Uint8Array(expected));
b1dee947
SR
1003 });
1004
1005 it('should transition to the Security state on successful negotiation', function () {
1006 send_ver('003.008', client);
c00ee156 1007 expect(client._rfb_init_state).to.equal('Security');
b1dee947 1008 });
3d7bb020
PO
1009
1010 describe('Repeater', function () {
057b8fec 1011 beforeEach(function () {
2f4516f2
PO
1012 client = make_rfb('wss://host:8675', { repeaterID: "12345" });
1013 client._rfb_connection_state = 'connecting';
057b8fec
PO
1014 });
1015
1016 it('should interpret version 000.000 as a repeater', function () {
3d7bb020
PO
1017 send_ver('000.000', client);
1018 expect(client._rfb_version).to.equal(0);
1019
1020 var sent_data = client._sock._websocket._get_sent_data();
1021 expect(new Uint8Array(sent_data.buffer, 0, 9)).to.array.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
1022 expect(sent_data).to.have.length(250);
1023 });
1024
1025 it('should handle two step repeater negotiation', function () {
3d7bb020
PO
1026 send_ver('000.000', client);
1027 send_ver('003.008', client);
1028 expect(client._rfb_version).to.equal(3.8);
1029 });
1030 });
b1dee947
SR
1031 });
1032
1033 describe('Security', function () {
b1dee947 1034 beforeEach(function () {
c00ee156 1035 client._rfb_init_state = 'Security';
b1dee947
SR
1036 });
1037
1038 it('should simply receive the auth scheme when for versions < 3.7', function () {
1039 client._rfb_version = 3.6;
1040 var auth_scheme_raw = [1, 2, 3, 4];
1041 var auth_scheme = (auth_scheme_raw[0] << 24) + (auth_scheme_raw[1] << 16) +
1042 (auth_scheme_raw[2] << 8) + auth_scheme_raw[3];
1043 client._sock._websocket._receive_data(auth_scheme_raw);
1044 expect(client._rfb_auth_scheme).to.equal(auth_scheme);
1045 });
1046
0ee5ca6e
PO
1047 it('should prefer no authentication is possible', function () {
1048 client._rfb_version = 3.7;
1049 var auth_schemes = [2, 1, 3];
1050 client._sock._websocket._receive_data(auth_schemes);
1051 expect(client._rfb_auth_scheme).to.equal(1);
1052 expect(client._sock).to.have.sent(new Uint8Array([1, 1]));
1053 });
1054
b1dee947
SR
1055 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
1056 client._rfb_version = 3.7;
0ee5ca6e 1057 var auth_schemes = [2, 22, 16];
b1dee947 1058 client._sock._websocket._receive_data(auth_schemes);
0ee5ca6e
PO
1059 expect(client._rfb_auth_scheme).to.equal(22);
1060 expect(client._sock).to.have.sent(new Uint8Array([22]));
b1dee947
SR
1061 });
1062
1063 it('should fail if there are no supported schemes for versions >= 3.7', function () {
3bb12056 1064 sinon.spy(client, "_fail");
b1dee947
SR
1065 client._rfb_version = 3.7;
1066 var auth_schemes = [1, 32];
1067 client._sock._websocket._receive_data(auth_schemes);
3bb12056 1068 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
1069 });
1070
1071 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
1072 client._rfb_version = 3.7;
1073 var failure_data = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1074 sinon.spy(client, '_fail');
1075 client._sock._websocket._receive_data(failure_data);
1076
159c50c0 1077 expect(client._fail).to.have.been.calledOnce;
67cd2072 1078 expect(client._fail).to.have.been.calledWith(
d472f3f1 1079 'Security negotiation failed on no security types (reason: whoops)');
b1dee947
SR
1080 });
1081
1082 it('should transition to the Authentication state and continue on successful negotiation', function () {
1083 client._rfb_version = 3.7;
1084 var auth_schemes = [1, 1];
1085 client._negotiate_authentication = sinon.spy();
1086 client._sock._websocket._receive_data(auth_schemes);
c00ee156 1087 expect(client._rfb_init_state).to.equal('Authentication');
b1dee947
SR
1088 expect(client._negotiate_authentication).to.have.been.calledOnce;
1089 });
1090 });
1091
1092 describe('Authentication', function () {
b1dee947 1093 beforeEach(function () {
c00ee156 1094 client._rfb_init_state = 'Security';
b1dee947
SR
1095 });
1096
1097 function send_security(type, cl) {
1098 cl._sock._websocket._receive_data(new Uint8Array([1, type]));
1099 }
1100
1101 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
1102 client._rfb_version = 3.6;
1103 var err_msg = "Whoopsies";
1104 var data = [0, 0, 0, 0];
1105 var err_len = err_msg.length;
3949a095 1106 push32(data, err_len);
b1dee947
SR
1107 for (var i = 0; i < err_len; i++) {
1108 data.push(err_msg.charCodeAt(i));
1109 }
1110
1111 sinon.spy(client, '_fail');
1112 client._sock._websocket._receive_data(new Uint8Array(data));
67cd2072 1113 expect(client._fail).to.have.been.calledWith(
d472f3f1 1114 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
b1dee947
SR
1115 });
1116
1117 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
1118 client._rfb_version = 3.8;
1119 send_security(1, client);
c00ee156 1120 expect(client._rfb_init_state).to.equal('SecurityResult');
b1dee947
SR
1121 });
1122
c00ee156 1123 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
b1dee947 1124 client._rfb_version = 3.7;
b1dee947 1125 send_security(1, client);
c00ee156 1126 expect(client._rfb_init_state).to.equal('ServerInitialisation');
b1dee947
SR
1127 });
1128
1129 it('should fail on an unknown auth scheme', function () {
3bb12056 1130 sinon.spy(client, "_fail");
b1dee947
SR
1131 client._rfb_version = 3.8;
1132 send_security(57, client);
3bb12056 1133 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
1134 });
1135
1136 describe('VNC Authentication (type 2) Handler', function () {
b1dee947 1137 beforeEach(function () {
c00ee156 1138 client._rfb_init_state = 'Security';
b1dee947
SR
1139 client._rfb_version = 3.8;
1140 });
1141
e89eef94
PO
1142 it('should fire the credentialsrequired event if missing a password', function () {
1143 var spy = sinon.spy();
1144 client.addEventListener("credentialsrequired", spy);
b1dee947 1145 send_security(2, client);
7d714b15 1146
aa5b3a35
SM
1147 var challenge = [];
1148 for (var i = 0; i < 16; i++) { challenge[i] = i; }
1149 client._sock._websocket._receive_data(new Uint8Array(challenge));
1150
430f00d6 1151 expect(client._rfb_credentials).to.be.empty;
7d714b15 1152 expect(spy).to.have.been.calledOnce;
e89eef94 1153 expect(spy.args[0][0].detail.types).to.have.members(["password"]);
b1dee947
SR
1154 });
1155
1156 it('should encrypt the password with DES and then send it back', function () {
430f00d6 1157 client._rfb_credentials = { password: 'passwd' };
b1dee947
SR
1158 send_security(2, client);
1159 client._sock._websocket._get_sent_data(); // skip the choice of auth reply
1160
1161 var challenge = [];
1162 for (var i = 0; i < 16; i++) { challenge[i] = i; }
1163 client._sock._websocket._receive_data(new Uint8Array(challenge));
1164
1165 var des_pass = RFB.genDES('passwd', challenge);
9ff86fb7 1166 expect(client._sock).to.have.sent(new Uint8Array(des_pass));
b1dee947
SR
1167 });
1168
1169 it('should transition to SecurityResult immediately after sending the password', function () {
430f00d6 1170 client._rfb_credentials = { password: 'passwd' };
b1dee947
SR
1171 send_security(2, client);
1172
1173 var challenge = [];
1174 for (var i = 0; i < 16; i++) { challenge[i] = i; }
1175 client._sock._websocket._receive_data(new Uint8Array(challenge));
1176
c00ee156 1177 expect(client._rfb_init_state).to.equal('SecurityResult');
b1dee947
SR
1178 });
1179 });
1180
1181 describe('XVP Authentication (type 22) Handler', function () {
b1dee947 1182 beforeEach(function () {
c00ee156 1183 client._rfb_init_state = 'Security';
b1dee947
SR
1184 client._rfb_version = 3.8;
1185 });
1186
1187 it('should fall through to standard VNC authentication upon completion', function () {
430f00d6
PO
1188 client._rfb_credentials = { username: 'user',
1189 target: 'target',
1190 password: 'password' };
b1dee947
SR
1191 client._negotiate_std_vnc_auth = sinon.spy();
1192 send_security(22, client);
1193 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
1194 });
1195
e89eef94
PO
1196 it('should fire the credentialsrequired event if all credentials are missing', function() {
1197 var spy = sinon.spy();
1198 client.addEventListener("credentialsrequired", spy);
430f00d6 1199 client._rfb_credentials = {};
b1dee947 1200 send_security(22, client);
7d714b15 1201
430f00d6 1202 expect(client._rfb_credentials).to.be.empty;
7d714b15 1203 expect(spy).to.have.been.calledOnce;
e89eef94 1204 expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]);
b1dee947
SR
1205 });
1206
e89eef94
PO
1207 it('should fire the credentialsrequired event if some credentials are missing', function() {
1208 var spy = sinon.spy();
1209 client.addEventListener("credentialsrequired", spy);
430f00d6
PO
1210 client._rfb_credentials = { username: 'user',
1211 target: 'target' };
b1dee947 1212 send_security(22, client);
7d714b15 1213
7d714b15 1214 expect(spy).to.have.been.calledOnce;
e89eef94 1215 expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]);
b1dee947
SR
1216 });
1217
430f00d6
PO
1218 it('should send user and target separately', function () {
1219 client._rfb_credentials = { username: 'user',
1220 target: 'target',
1221 password: 'password' };
b1dee947
SR
1222 client._negotiate_std_vnc_auth = sinon.spy();
1223
1224 send_security(22, client);
1225
b1dee947
SR
1226 var expected = [22, 4, 6]; // auth selection, len user, len target
1227 for (var i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); }
1228
9ff86fb7 1229 expect(client._sock).to.have.sent(new Uint8Array(expected));
b1dee947
SR
1230 });
1231 });
1232
1233 describe('TightVNC Authentication (type 16) Handler', function () {
b1dee947 1234 beforeEach(function () {
c00ee156 1235 client._rfb_init_state = 'Security';
b1dee947
SR
1236 client._rfb_version = 3.8;
1237 send_security(16, client);
1238 client._sock._websocket._get_sent_data(); // skip the security reply
1239 });
1240
1241 function send_num_str_pairs(pairs, client) {
1242 var pairs_len = pairs.length;
1243 var data = [];
3949a095 1244 push32(data, pairs_len);
b1dee947
SR
1245
1246 for (var i = 0; i < pairs_len; i++) {
3949a095 1247 push32(data, pairs[i][0]);
b1dee947
SR
1248 var j;
1249 for (j = 0; j < 4; j++) {
1250 data.push(pairs[i][1].charCodeAt(j));
1251 }
1252 for (j = 0; j < 8; j++) {
1253 data.push(pairs[i][2].charCodeAt(j));
1254 }
1255 }
1256
1257 client._sock._websocket._receive_data(new Uint8Array(data));
1258 }
1259
1260 it('should skip tunnel negotiation if no tunnels are requested', function () {
1261 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
1262 expect(client._rfb_tightvnc).to.be.true;
1263 });
1264
1265 it('should fail if no supported tunnels are listed', function () {
3bb12056 1266 sinon.spy(client, "_fail");
b1dee947 1267 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client);
3bb12056 1268 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
1269 });
1270
1271 it('should choose the notunnel tunnel type', function () {
1272 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client);
9ff86fb7 1273 expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0]));
b1dee947
SR
1274 });
1275
1276 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
1277 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client);
1278 client._sock._websocket._get_sent_data(); // skip the tunnel choice here
1279 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
9ff86fb7 1280 expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
c00ee156 1281 expect(client._rfb_init_state).to.equal('SecurityResult');
b1dee947
SR
1282 });
1283
1284 /*it('should attempt to use VNC auth over no auth when possible', function () {
1285 client._rfb_tightvnc = true;
1286 client._negotiate_std_vnc_auth = sinon.spy();
1287 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
1288 expect(client._sock).to.have.sent([0, 0, 0, 1]);
1289 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
1290 expect(client._rfb_auth_scheme).to.equal(2);
1291 });*/ // while this would make sense, the original code doesn't actually do this
1292
1293 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
1294 client._rfb_tightvnc = true;
1295 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
9ff86fb7 1296 expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
c00ee156 1297 expect(client._rfb_init_state).to.equal('SecurityResult');
b1dee947
SR
1298 });
1299
1300 it('should accept VNC authentication and transition to that', function () {
1301 client._rfb_tightvnc = true;
1302 client._negotiate_std_vnc_auth = sinon.spy();
1303 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client);
9ff86fb7 1304 expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 2]));
b1dee947
SR
1305 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
1306 expect(client._rfb_auth_scheme).to.equal(2);
1307 });
1308
1309 it('should fail if there are no supported auth types', function () {
3bb12056 1310 sinon.spy(client, "_fail");
b1dee947
SR
1311 client._rfb_tightvnc = true;
1312 send_num_str_pairs([[23, 'stdv', 'badval__']], client);
3bb12056 1313 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
1314 });
1315 });
1316 });
1317
1318 describe('SecurityResult', function () {
b1dee947 1319 beforeEach(function () {
c00ee156 1320 client._rfb_init_state = 'SecurityResult';
b1dee947
SR
1321 });
1322
c00ee156 1323 it('should fall through to ServerInitialisation on a response code of 0', function () {
b1dee947 1324 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
c00ee156 1325 expect(client._rfb_init_state).to.equal('ServerInitialisation');
b1dee947
SR
1326 });
1327
1328 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1329 client._rfb_version = 3.8;
1330 sinon.spy(client, '_fail');
1331 var failure_data = [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1332 client._sock._websocket._receive_data(new Uint8Array(failure_data));
67cd2072 1333 expect(client._fail).to.have.been.calledWith(
d472f3f1 1334 'Security negotiation failed on security result (reason: whoops)');
b1dee947
SR
1335 });
1336
1337 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
3bb12056 1338 sinon.spy(client, '_fail');
b1dee947
SR
1339 client._rfb_version = 3.7;
1340 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 1]));
d472f3f1
SM
1341 expect(client._fail).to.have.been.calledWith(
1342 'Security handshake failed');
1343 });
1344
1345 it('should result in securityfailure event when receiving a non zero status', function () {
1346 var spy = sinon.spy();
1347 client.addEventListener("securityfailure", spy);
1348 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 2]));
1349 expect(spy).to.have.been.calledOnce;
1350 expect(spy.args[0][0].detail.status).to.equal(2);
1351 });
1352
1353 it('should include reason when provided in securityfailure event', function () {
1354 client._rfb_version = 3.8;
1355 var spy = sinon.spy();
1356 client.addEventListener("securityfailure", spy);
1357 var failure_data = [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
1358 32, 102, 97, 105, 108, 117, 114, 101];
1359 client._sock._websocket._receive_data(new Uint8Array(failure_data));
1360 expect(spy.args[0][0].detail.status).to.equal(1);
1361 expect(spy.args[0][0].detail.reason).to.equal('such failure');
1362 });
1363
1364 it('should not include reason when length is zero in securityfailure event', function () {
1365 client._rfb_version = 3.9;
1366 var spy = sinon.spy();
1367 client.addEventListener("securityfailure", spy);
1368 var failure_data = [0, 0, 0, 1, 0, 0, 0, 0];
1369 client._sock._websocket._receive_data(new Uint8Array(failure_data));
1370 expect(spy.args[0][0].detail.status).to.equal(1);
1371 expect('reason' in spy.args[0][0].detail).to.be.false;
1372 });
1373
1374 it('should not include reason in securityfailure event for version < 3.8', function () {
1375 client._rfb_version = 3.6;
1376 var spy = sinon.spy();
1377 client.addEventListener("securityfailure", spy);
1378 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 2]));
1379 expect(spy.args[0][0].detail.status).to.equal(2);
1380 expect('reason' in spy.args[0][0].detail).to.be.false;
b1dee947
SR
1381 });
1382 });
1383
1384 describe('ClientInitialisation', function () {
b1dee947 1385 it('should transition to the ServerInitialisation state', function () {
2f4516f2
PO
1386 var client = make_rfb();
1387 client._rfb_connection_state = 'connecting';
3d7bb020 1388 client._rfb_init_state = 'SecurityResult';
b1dee947 1389 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
c00ee156 1390 expect(client._rfb_init_state).to.equal('ServerInitialisation');
b1dee947
SR
1391 });
1392
1393 it('should send 1 if we are in shared mode', function () {
2f4516f2
PO
1394 var client = make_rfb('wss://host:8675', { shared: true });
1395 client._rfb_connection_state = 'connecting';
3d7bb020 1396 client._rfb_init_state = 'SecurityResult';
b1dee947 1397 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
9ff86fb7 1398 expect(client._sock).to.have.sent(new Uint8Array([1]));
b1dee947
SR
1399 });
1400
1401 it('should send 0 if we are not in shared mode', function () {
2f4516f2
PO
1402 var client = make_rfb('wss://host:8675', { shared: false });
1403 client._rfb_connection_state = 'connecting';
3d7bb020 1404 client._rfb_init_state = 'SecurityResult';
b1dee947 1405 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
9ff86fb7 1406 expect(client._sock).to.have.sent(new Uint8Array([0]));
b1dee947
SR
1407 });
1408 });
1409
1410 describe('ServerInitialisation', function () {
b1dee947 1411 beforeEach(function () {
c00ee156 1412 client._rfb_init_state = 'ServerInitialisation';
b1dee947
SR
1413 });
1414
1415 function send_server_init(opts, client) {
1416 var full_opts = { width: 10, height: 12, bpp: 24, depth: 24, big_endian: 0,
1417 true_color: 1, red_max: 255, green_max: 255, blue_max: 255,
1418 red_shift: 16, green_shift: 8, blue_shift: 0, name: 'a name' };
1419 for (var opt in opts) {
1420 full_opts[opt] = opts[opt];
1421 }
1422 var data = [];
1423
3949a095
SR
1424 push16(data, full_opts.width);
1425 push16(data, full_opts.height);
b1dee947
SR
1426
1427 data.push(full_opts.bpp);
1428 data.push(full_opts.depth);
1429 data.push(full_opts.big_endian);
1430 data.push(full_opts.true_color);
1431
3949a095
SR
1432 push16(data, full_opts.red_max);
1433 push16(data, full_opts.green_max);
1434 push16(data, full_opts.blue_max);
1435 push8(data, full_opts.red_shift);
1436 push8(data, full_opts.green_shift);
1437 push8(data, full_opts.blue_shift);
b1dee947
SR
1438
1439 // padding
3949a095
SR
1440 push8(data, 0);
1441 push8(data, 0);
1442 push8(data, 0);
b1dee947
SR
1443
1444 client._sock._websocket._receive_data(new Uint8Array(data));
1445
1446 var name_data = [];
3949a095 1447 push32(name_data, full_opts.name.length);
b1dee947
SR
1448 for (var i = 0; i < full_opts.name.length; i++) {
1449 name_data.push(full_opts.name.charCodeAt(i));
1450 }
1451 client._sock._websocket._receive_data(new Uint8Array(name_data));
1452 }
1453
1454 it('should set the framebuffer width and height', function () {
1455 send_server_init({ width: 32, height: 84 }, client);
1456 expect(client._fb_width).to.equal(32);
1457 expect(client._fb_height).to.equal(84);
1458 });
1459
1460 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1461
1462 it('should set the framebuffer name and call the callback', function () {
e89eef94
PO
1463 var spy = sinon.spy();
1464 client.addEventListener("desktopname", spy);
b1dee947
SR
1465 send_server_init({ name: 'some name' }, client);
1466
b1dee947
SR
1467 expect(client._fb_name).to.equal('some name');
1468 expect(spy).to.have.been.calledOnce;
e89eef94 1469 expect(spy.args[0][0].detail.name).to.equal('some name');
b1dee947
SR
1470 });
1471
1472 it('should handle the extended init message of the tight encoding', function () {
1473 // NB(sross): we don't actually do anything with it, so just test that we can
1474 // read it w/o throwing an error
1475 client._rfb_tightvnc = true;
1476 send_server_init({}, client);
1477
1478 var tight_data = [];
3949a095
SR
1479 push16(tight_data, 1);
1480 push16(tight_data, 2);
1481 push16(tight_data, 3);
1482 push16(tight_data, 0);
b1dee947
SR
1483 for (var i = 0; i < 16 + 32 + 48; i++) {
1484 tight_data.push(i);
1485 }
1486 client._sock._websocket._receive_data(tight_data);
1487
c2a4d3ef 1488 expect(client._rfb_connection_state).to.equal('connected');
b1dee947
SR
1489 });
1490
9b84f516 1491 it('should resize the display', function () {
b1dee947
SR
1492 sinon.spy(client._display, 'resize');
1493 send_server_init({ width: 27, height: 32 }, client);
1494
b1dee947
SR
1495 expect(client._display.resize).to.have.been.calledOnce;
1496 expect(client._display.resize).to.have.been.calledWith(27, 32);
b1dee947
SR
1497 });
1498
1499 it('should grab the mouse and keyboard', function () {
1500 sinon.spy(client._keyboard, 'grab');
1501 sinon.spy(client._mouse, 'grab');
1502 send_server_init({}, client);
1503 expect(client._keyboard.grab).to.have.been.calledOnce;
1504 expect(client._mouse.grab).to.have.been.calledOnce;
1505 });
1506
69411b9e
PO
1507 describe('Initial Update Request', function () {
1508 beforeEach(function () {
1509 sinon.spy(RFB.messages, "pixelFormat");
1510 sinon.spy(RFB.messages, "clientEncodings");
1511 sinon.spy(RFB.messages, "fbUpdateRequest");
1512 });
49a81837 1513
69411b9e
PO
1514 afterEach(function () {
1515 RFB.messages.pixelFormat.restore();
1516 RFB.messages.clientEncodings.restore();
1517 RFB.messages.fbUpdateRequest.restore();
1518 });
b1dee947 1519
69411b9e
PO
1520 // TODO(directxman12): test the various options in this configuration matrix
1521 it('should reply with the pixel format, client encodings, and initial update request', function () {
1522 send_server_init({ width: 27, height: 32 }, client);
1523
1524 expect(RFB.messages.pixelFormat).to.have.been.calledOnce;
1525 expect(RFB.messages.pixelFormat).to.have.been.calledWith(client._sock, 24, true);
1526 expect(RFB.messages.pixelFormat).to.have.been.calledBefore(RFB.messages.clientEncodings);
1527 expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
1528 expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.encodingTight);
1529 expect(RFB.messages.clientEncodings).to.have.been.calledBefore(RFB.messages.fbUpdateRequest);
1530 expect(RFB.messages.fbUpdateRequest).to.have.been.calledOnce;
1531 expect(RFB.messages.fbUpdateRequest).to.have.been.calledWith(client._sock, false, 0, 0, 27, 32);
1532 });
1533
1534 it('should reply with restricted settings for Intel AMT servers', function () {
1535 send_server_init({ width: 27, height: 32, name: "Intel(r) AMT KVM"}, client);
1536
1537 expect(RFB.messages.pixelFormat).to.have.been.calledOnce;
1538 expect(RFB.messages.pixelFormat).to.have.been.calledWith(client._sock, 8, true);
1539 expect(RFB.messages.pixelFormat).to.have.been.calledBefore(RFB.messages.clientEncodings);
1540 expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
1541 expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.not.include(encodings.encodingTight);
1542 expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.not.include(encodings.encodingHextile);
1543 expect(RFB.messages.clientEncodings).to.have.been.calledBefore(RFB.messages.fbUpdateRequest);
1544 expect(RFB.messages.fbUpdateRequest).to.have.been.calledOnce;
1545 expect(RFB.messages.fbUpdateRequest).to.have.been.calledWith(client._sock, false, 0, 0, 27, 32);
1546 });
b1dee947
SR
1547 });
1548
c2a4d3ef 1549 it('should transition to the "connected" state', function () {
b1dee947 1550 send_server_init({}, client);
c2a4d3ef 1551 expect(client._rfb_connection_state).to.equal('connected');
b1dee947
SR
1552 });
1553 });
1554 });
1555
1556 describe('Protocol Message Processing After Completing Initialization', function () {
1557 var client;
1558
1559 beforeEach(function () {
1560 client = make_rfb();
b1dee947
SR
1561 client._fb_name = 'some device';
1562 client._fb_width = 640;
1563 client._fb_height = 20;
1564 });
1565
1566 describe('Framebuffer Update Handling', function () {
b1dee947
SR
1567 var target_data_arr = [
1568 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1569 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1570 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1571 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1572 ];
1573 var target_data;
1574
1575 var target_data_check_arr = [
1576 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1577 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1578 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1579 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1580 ];
1581 var target_data_check;
1582
1583 before(function () {
1584 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1585 target_data = new Uint8Array(target_data_arr);
1586 target_data_check = new Uint8Array(target_data_check_arr);
1587 });
1588
1589 function send_fbu_msg (rect_info, rect_data, client, rect_cnt) {
1590 var data = [];
1591
1592 if (!rect_cnt || rect_cnt > -1) {
1593 // header
1594 data.push(0); // msg type
1595 data.push(0); // padding
3949a095 1596 push16(data, rect_cnt || rect_data.length);
b1dee947
SR
1597 }
1598
1599 for (var i = 0; i < rect_data.length; i++) {
1600 if (rect_info[i]) {
3949a095
SR
1601 push16(data, rect_info[i].x);
1602 push16(data, rect_info[i].y);
1603 push16(data, rect_info[i].width);
1604 push16(data, rect_info[i].height);
1605 push32(data, rect_info[i].encoding);
b1dee947
SR
1606 }
1607 data = data.concat(rect_data[i]);
1608 }
1609
1610 client._sock._websocket._receive_data(new Uint8Array(data));
1611 }
1612
1613 it('should send an update request if there is sufficient data', function () {
89d2837f 1614 var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
2ba767a7 1615 RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20);
b1dee947
SR
1616
1617 client._framebufferUpdate = function () { return true; };
1618 client._sock._websocket._receive_data(new Uint8Array([0]));
1619
9ff86fb7 1620 expect(client._sock).to.have.sent(expected_msg._sQ);
b1dee947
SR
1621 });
1622
1623 it('should not send an update request if we need more data', function () {
1624 client._sock._websocket._receive_data(new Uint8Array([0]));
1625 expect(client._sock._websocket._get_sent_data()).to.have.length(0);
1626 });
1627
1628 it('should resume receiving an update if we previously did not have enough data', function () {
89d2837f 1629 var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
2ba767a7 1630 RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20);
b1dee947
SR
1631
1632 // just enough to set FBU.rects
1633 client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 3]));
1634 expect(client._sock._websocket._get_sent_data()).to.have.length(0);
1635
9535539b 1636 client._framebufferUpdate = function () { this._sock.rQskip8(); return true; }; // we magically have enough data
b1dee947
SR
1637 // 247 should *not* be used as the message type here
1638 client._sock._websocket._receive_data(new Uint8Array([247]));
9ff86fb7 1639 expect(client._sock).to.have.sent(expected_msg._sQ);
b1dee947
SR
1640 });
1641
2ba767a7 1642 it('should not send a request in continuous updates mode', function () {
76a86ff5 1643 client._enabledContinuousUpdates = true;
1644 client._framebufferUpdate = function () { return true; };
76a86ff5 1645 client._sock._websocket._receive_data(new Uint8Array([0]));
1646
1647 expect(client._sock._websocket._get_sent_data()).to.have.length(0);
1648 });
1649
b1dee947 1650 it('should fail on an unsupported encoding', function () {
3bb12056 1651 sinon.spy(client, "_fail");
b1dee947
SR
1652 var rect_info = { x: 8, y: 11, width: 27, height: 32, encoding: 234 };
1653 send_fbu_msg([rect_info], [[]], client);
3bb12056 1654 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
1655 });
1656
1657 it('should be able to pause and resume receiving rects if not enought data', function () {
1658 // seed some initial data to copy
1659 client._fb_width = 4;
1660 client._fb_height = 4;
1661 client._display.resize(4, 4);
02329ab1 1662 client._display.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr.slice(0, 32)), 0);
b1dee947
SR
1663
1664 var info = [{ x: 0, y: 2, width: 2, height: 2, encoding: 0x01},
1665 { x: 2, y: 2, width: 2, height: 2, encoding: 0x01}];
02329ab1 1666 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
b1dee947
SR
1667 var rects = [[0, 2, 0, 0], [0, 0, 0, 0]];
1668 send_fbu_msg([info[0]], [rects[0]], client, 2);
1669 send_fbu_msg([info[1]], [rects[1]], client, -1);
1670 expect(client._display).to.have.displayed(target_data_check);
1671 });
1672
1673 describe('Message Encoding Handlers', function () {
b1dee947 1674 beforeEach(function () {
b1dee947
SR
1675 // a really small frame
1676 client._fb_width = 4;
1677 client._fb_height = 4;
69411b9e 1678 client._fb_depth = 24;
02329ab1 1679 client._display.resize(4, 4);
b1dee947
SR
1680 });
1681
1682 it('should handle the RAW encoding', function () {
1683 var info = [{ x: 0, y: 0, width: 2, height: 2, encoding: 0x00 },
1684 { x: 2, y: 0, width: 2, height: 2, encoding: 0x00 },
1685 { x: 0, y: 2, width: 4, height: 1, encoding: 0x00 },
1686 { x: 0, y: 3, width: 4, height: 1, encoding: 0x00 }];
1687 // data is in bgrx
1688 var rects = [
1689 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1690 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1691 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1692 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1693 send_fbu_msg(info, rects, client);
1694 expect(client._display).to.have.displayed(target_data);
1695 });
1696
69411b9e
PO
1697 it('should handle the RAW encoding in low colour mode', function () {
1698 var info = [{ x: 0, y: 0, width: 2, height: 2, encoding: 0x00 },
1699 { x: 2, y: 0, width: 2, height: 2, encoding: 0x00 },
1700 { x: 0, y: 2, width: 4, height: 1, encoding: 0x00 },
1701 { x: 0, y: 3, width: 4, height: 1, encoding: 0x00 }];
1702 var rects = [
1703 [0x03, 0x03, 0x03, 0x03],
1704 [0x0c, 0x0c, 0x0c, 0x0c],
1705 [0x0c, 0x0c, 0x03, 0x03],
1706 [0x0c, 0x0c, 0x03, 0x03]];
1707 client._fb_depth = 8;
1708 send_fbu_msg(info, rects, client);
1709 expect(client._display).to.have.displayed(target_data_check);
1710 });
1711
b1dee947
SR
1712 it('should handle the COPYRECT encoding', function () {
1713 // seed some initial data to copy
02329ab1 1714 client._display.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr.slice(0, 32)), 0);
b1dee947
SR
1715
1716 var info = [{ x: 0, y: 2, width: 2, height: 2, encoding: 0x01},
1717 { x: 2, y: 2, width: 2, height: 2, encoding: 0x01}];
1718 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1719 var rects = [[0, 2, 0, 0], [0, 0, 0, 0]];
1720 send_fbu_msg(info, rects, client);
1721 expect(client._display).to.have.displayed(target_data_check);
1722 });
1723
1724 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1725 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1726
1727 it('should handle the RRE encoding', function () {
1728 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x02 }];
1729 var rect = [];
3949a095
SR
1730 push32(rect, 2); // 2 subrects
1731 push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
b1dee947
SR
1732 rect.push(0xff); // becomes ff0000ff --> #0000FF color
1733 rect.push(0x00);
1734 rect.push(0x00);
1735 rect.push(0xff);
3949a095
SR
1736 push16(rect, 0); // x: 0
1737 push16(rect, 0); // y: 0
1738 push16(rect, 2); // width: 2
1739 push16(rect, 2); // height: 2
b1dee947
SR
1740 rect.push(0xff); // becomes ff0000ff --> #0000FF color
1741 rect.push(0x00);
1742 rect.push(0x00);
1743 rect.push(0xff);
3949a095
SR
1744 push16(rect, 2); // x: 2
1745 push16(rect, 2); // y: 2
1746 push16(rect, 2); // width: 2
1747 push16(rect, 2); // height: 2
b1dee947
SR
1748
1749 send_fbu_msg(info, [rect], client);
1750 expect(client._display).to.have.displayed(target_data_check);
1751 });
1752
1753 describe('the HEXTILE encoding handler', function () {
b1dee947
SR
1754 it('should handle a tile with fg, bg specified, normal subrects', function () {
1755 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1756 var rect = [];
1757 rect.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
3949a095 1758 push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
b1dee947
SR
1759 rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
1760 rect.push(0x00);
1761 rect.push(0x00);
1762 rect.push(0xff);
1763 rect.push(2); // 2 subrects
1764 rect.push(0); // x: 0, y: 0
1765 rect.push(1 | (1 << 4)); // width: 2, height: 2
1766 rect.push(2 | (2 << 4)); // x: 2, y: 2
1767 rect.push(1 | (1 << 4)); // width: 2, height: 2
1768 send_fbu_msg(info, [rect], client);
1769 expect(client._display).to.have.displayed(target_data_check);
1770 });
1771
1772 it('should handle a raw tile', function () {
1773 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1774 var rect = [];
1775 rect.push(0x01); // raw
1776 for (var i = 0; i < target_data.length; i += 4) {
1777 rect.push(target_data[i + 2]);
1778 rect.push(target_data[i + 1]);
1779 rect.push(target_data[i]);
1780 rect.push(target_data[i + 3]);
1781 }
1782 send_fbu_msg(info, [rect], client);
1783 expect(client._display).to.have.displayed(target_data);
1784 });
1785
1786 it('should handle a tile with only bg specified (solid bg)', function () {
1787 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1788 var rect = [];
1789 rect.push(0x02);
3949a095 1790 push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
b1dee947
SR
1791 send_fbu_msg(info, [rect], client);
1792
1793 var expected = [];
3949a095 1794 for (var i = 0; i < 16; i++) { push32(expected, 0xff00ff); }
b1dee947
SR
1795 expect(client._display).to.have.displayed(new Uint8Array(expected));
1796 });
1797
40ac6f0a
RK
1798 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1799 // set the width so we can have two tiles
1800 client._fb_width = 8;
02329ab1 1801 client._display.resize(8, 4);
40ac6f0a 1802
4865278d 1803 var info = [{ x: 0, y: 0, width: 32, height: 4, encoding: 0x05 }];
40ac6f0a
RK
1804
1805 var rect = [];
1806
1807 // send a bg frame
1808 rect.push(0x02);
3949a095 1809 push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
40ac6f0a
RK
1810
1811 // send an empty frame
1812 rect.push(0x00);
1813
1814 send_fbu_msg(info, [rect], client);
1815
1816 var expected = [];
1817 var i;
3949a095
SR
1818 for (i = 0; i < 16; i++) { push32(expected, 0xff00ff); } // rect 1: solid
1819 for (i = 0; i < 16; i++) { push32(expected, 0xff00ff); } // rect 2: same bkground color
40ac6f0a
RK
1820 expect(client._display).to.have.displayed(new Uint8Array(expected));
1821 });
1822
b1dee947
SR
1823 it('should handle a tile with bg and coloured subrects', function () {
1824 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1825 var rect = [];
1826 rect.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
3949a095 1827 push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
b1dee947
SR
1828 rect.push(2); // 2 subrects
1829 rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
1830 rect.push(0x00);
1831 rect.push(0x00);
1832 rect.push(0xff);
1833 rect.push(0); // x: 0, y: 0
1834 rect.push(1 | (1 << 4)); // width: 2, height: 2
1835 rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
1836 rect.push(0x00);
1837 rect.push(0x00);
1838 rect.push(0xff);
1839 rect.push(2 | (2 << 4)); // x: 2, y: 2
1840 rect.push(1 | (1 << 4)); // width: 2, height: 2
1841 send_fbu_msg(info, [rect], client);
1842 expect(client._display).to.have.displayed(target_data_check);
1843 });
1844
1845 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1846 client._fb_width = 4;
1847 client._fb_height = 17;
1848 client._display.resize(4, 17);
1849
1850 var info = [{ x: 0, y: 0, width: 4, height: 17, encoding: 0x05}];
1851 var rect = [];
1852 rect.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
3949a095 1853 push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
b1dee947
SR
1854 rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
1855 rect.push(0x00);
1856 rect.push(0x00);
1857 rect.push(0xff);
1858 rect.push(8); // 8 subrects
1859 var i;
1860 for (i = 0; i < 4; i++) {
1861 rect.push((0 << 4) | (i * 4)); // x: 0, y: i*4
1862 rect.push(1 | (1 << 4)); // width: 2, height: 2
1863 rect.push((2 << 4) | (i * 4 + 2)); // x: 2, y: i * 4 + 2
1864 rect.push(1 | (1 << 4)); // width: 2, height: 2
1865 }
1866 rect.push(0x08); // anysubrects
1867 rect.push(1); // 1 subrect
1868 rect.push(0); // x: 0, y: 0
1869 rect.push(1 | (1 << 4)); // width: 2, height: 2
1870 send_fbu_msg(info, [rect], client);
1871
1872 var expected = [];
1873 for (i = 0; i < 4; i++) { expected = expected.concat(target_data_check_arr); }
1874 expected = expected.concat(target_data_check_arr.slice(0, 16));
1875 expect(client._display).to.have.displayed(new Uint8Array(expected));
1876 });
1877
1878 it('should fail on an invalid subencoding', function () {
3bb12056 1879 sinon.spy(client,"_fail");
b1dee947
SR
1880 var info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
1881 var rects = [[45]]; // an invalid subencoding
1882 send_fbu_msg(info, rects, client);
3bb12056 1883 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
1884 });
1885 });
1886
1887 it.skip('should handle the TIGHT encoding', function () {
1888 // TODO(directxman12): test this
1889 });
1890
1891 it.skip('should handle the TIGHT_PNG encoding', function () {
1892 // TODO(directxman12): test this
1893 });
1894
1895 it('should handle the DesktopSize pseduo-encoding', function () {
b1dee947
SR
1896 sinon.spy(client._display, 'resize');
1897 send_fbu_msg([{ x: 0, y: 0, width: 20, height: 50, encoding: -223 }], [[]], client);
1898
b1dee947
SR
1899 expect(client._fb_width).to.equal(20);
1900 expect(client._fb_height).to.equal(50);
1901
1902 expect(client._display.resize).to.have.been.calledOnce;
1903 expect(client._display.resize).to.have.been.calledWith(20, 50);
1904 });
1905
4dec490a 1906 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
4dec490a 1907 beforeEach(function () {
4dec490a 1908 // a really small frame
1909 client._fb_width = 4;
1910 client._fb_height = 4;
02329ab1 1911 client._display.resize(4, 4);
4dec490a 1912 sinon.spy(client._display, 'resize');
4dec490a 1913 });
1914
1915 function make_screen_data (nr_of_screens) {
1916 var data = [];
3949a095
SR
1917 push8(data, nr_of_screens); // number-of-screens
1918 push8(data, 0); // padding
1919 push16(data, 0); // padding
4dec490a 1920 for (var i=0; i<nr_of_screens; i += 1) {
3949a095
SR
1921 push32(data, 0); // id
1922 push16(data, 0); // x-position
1923 push16(data, 0); // y-position
1924 push16(data, 20); // width
1925 push16(data, 50); // height
1926 push32(data, 0); // flags
4dec490a 1927 }
1928 return data;
1929 }
1930
1931 it('should handle a resize requested by this client', function () {
1932 var reason_for_change = 1; // requested by this client
1933 var status_code = 0; // No error
1934
1935 send_fbu_msg([{ x: reason_for_change, y: status_code,
1936 width: 20, height: 50, encoding: -308 }],
1937 make_screen_data(1), client);
1938
4dec490a 1939 expect(client._fb_width).to.equal(20);
1940 expect(client._fb_height).to.equal(50);
1941
1942 expect(client._display.resize).to.have.been.calledOnce;
1943 expect(client._display.resize).to.have.been.calledWith(20, 50);
4dec490a 1944 });
1945
1946 it('should handle a resize requested by another client', function () {
1947 var reason_for_change = 2; // requested by another client
1948 var status_code = 0; // No error
1949
1950 send_fbu_msg([{ x: reason_for_change, y: status_code,
1951 width: 20, height: 50, encoding: -308 }],
1952 make_screen_data(1), client);
1953
4dec490a 1954 expect(client._fb_width).to.equal(20);
1955 expect(client._fb_height).to.equal(50);
1956
1957 expect(client._display.resize).to.have.been.calledOnce;
1958 expect(client._display.resize).to.have.been.calledWith(20, 50);
4dec490a 1959 });
1960
1961 it('should be able to recieve requests which contain data for multiple screens', function () {
1962 var reason_for_change = 2; // requested by another client
1963 var status_code = 0; // No error
1964
1965 send_fbu_msg([{ x: reason_for_change, y: status_code,
1966 width: 60, height: 50, encoding: -308 }],
1967 make_screen_data(3), client);
1968
4dec490a 1969 expect(client._fb_width).to.equal(60);
1970 expect(client._fb_height).to.equal(50);
1971
1972 expect(client._display.resize).to.have.been.calledOnce;
1973 expect(client._display.resize).to.have.been.calledWith(60, 50);
4dec490a 1974 });
1975
1976 it('should not handle a failed request', function () {
798340b9 1977 var reason_for_change = 1; // requested by this client
4dec490a 1978 var status_code = 1; // Resize is administratively prohibited
1979
1980 send_fbu_msg([{ x: reason_for_change, y: status_code,
1981 width: 20, height: 50, encoding: -308 }],
1982 make_screen_data(1), client);
1983
1984 expect(client._fb_width).to.equal(4);
1985 expect(client._fb_height).to.equal(4);
1986
1987 expect(client._display.resize).to.not.have.been.called;
4dec490a 1988 });
1989 });
1990
b1dee947
SR
1991 it.skip('should handle the Cursor pseudo-encoding', function () {
1992 // TODO(directxman12): test
1993 });
1994
1995 it('should handle the last_rect pseudo-encoding', function () {
b1dee947
SR
1996 send_fbu_msg([{ x: 0, y: 0, width: 0, height: 0, encoding: -224}], [[]], client, 100);
1997 expect(client._FBU.rects).to.equal(0);
b1dee947
SR
1998 });
1999 });
2000 });
2001
b1dee947 2002 describe('XVP Message Handling', function () {
b1dee947 2003 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
e89eef94
PO
2004 var spy = sinon.spy();
2005 client.addEventListener("capabilities", spy);
b1dee947
SR
2006 client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 1]));
2007 expect(client._rfb_xvp_ver).to.equal(10);
e89eef94
PO
2008 expect(spy).to.have.been.calledOnce;
2009 expect(spy.args[0][0].detail.capabilities.power).to.be.true;
747b4623 2010 expect(client.capabilities.power).to.be.true;
b1dee947
SR
2011 });
2012
2013 it('should fail on unknown XVP message types', function () {
3bb12056 2014 sinon.spy(client, "_fail");
b1dee947 2015 client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 237]));
3bb12056 2016 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
2017 });
2018 });
2019
2020 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
2021 var expected_str = 'cheese!';
2022 var data = [3, 0, 0, 0];
3949a095 2023 push32(data, expected_str.length);
b1dee947 2024 for (var i = 0; i < expected_str.length; i++) { data.push(expected_str.charCodeAt(i)); }
e89eef94
PO
2025 var spy = sinon.spy();
2026 client.addEventListener("clipboard", spy);
b1dee947
SR
2027
2028 client._sock._websocket._receive_data(new Uint8Array(data));
b1dee947 2029 expect(spy).to.have.been.calledOnce;
e89eef94 2030 expect(spy.args[0][0].detail.text).to.equal(expected_str);
b1dee947
SR
2031 });
2032
2033 it('should fire the bell callback on Bell', function () {
e89eef94
PO
2034 var spy = sinon.spy();
2035 client.addEventListener("bell", spy);
b1dee947 2036 client._sock._websocket._receive_data(new Uint8Array([2]));
e89eef94 2037 expect(spy).to.have.been.calledOnce;
b1dee947
SR
2038 });
2039
3df13262 2040 it('should respond correctly to ServerFence', function () {
2041 var expected_msg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: function() {}};
2042 var incoming_msg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: function() {}};
2043
2044 var payload = "foo\x00ab9";
2045
2046 // ClientFence and ServerFence are identical in structure
2047 RFB.messages.clientFence(expected_msg, (1<<0) | (1<<1), payload);
2048 RFB.messages.clientFence(incoming_msg, 0xffffffff, payload);
2049
2050 client._sock._websocket._receive_data(incoming_msg._sQ);
2051
2052 expect(client._sock).to.have.sent(expected_msg._sQ);
2053
2054 expected_msg._sQlen = 0;
2055 incoming_msg._sQlen = 0;
2056
2057 RFB.messages.clientFence(expected_msg, (1<<0), payload);
2058 RFB.messages.clientFence(incoming_msg, (1<<0) | (1<<31), payload);
2059
2060 client._sock._websocket._receive_data(incoming_msg._sQ);
2061
2062 expect(client._sock).to.have.sent(expected_msg._sQ);
2063 });
2064
76a86ff5 2065 it('should enable continuous updates on first EndOfContinousUpdates', function () {
2066 var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
2067
2068 RFB.messages.enableContinuousUpdates(expected_msg, true, 0, 0, 640, 20);
2069
2070 expect(client._enabledContinuousUpdates).to.be.false;
2071
2072 client._sock._websocket._receive_data(new Uint8Array([150]));
2073
2074 expect(client._enabledContinuousUpdates).to.be.true;
2075 expect(client._sock).to.have.sent(expected_msg._sQ);
2076 });
2077
2078 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
2079 client._enabledContinuousUpdates = true;
2080 client._supportsContinuousUpdates = true;
2081
2082 client._sock._websocket._receive_data(new Uint8Array([150]));
2083
2084 expect(client._enabledContinuousUpdates).to.be.false;
2085 });
2086
2087 it('should update continuous updates on resize', function () {
2088 var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
2089 RFB.messages.enableContinuousUpdates(expected_msg, true, 0, 0, 90, 700);
2090
91d5c625 2091 client._resize(450, 160);
76a86ff5 2092
2093 expect(client._sock._websocket._get_sent_data()).to.have.length(0);
2094
2095 client._enabledContinuousUpdates = true;
2096
91d5c625 2097 client._resize(90, 700);
76a86ff5 2098
2099 expect(client._sock).to.have.sent(expected_msg._sQ);
2100 });
2101
b1dee947 2102 it('should fail on an unknown message type', function () {
3bb12056 2103 sinon.spy(client, "_fail");
b1dee947 2104 client._sock._websocket._receive_data(new Uint8Array([87]));
3bb12056 2105 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
2106 });
2107 });
2108
2109 describe('Asynchronous Events', function () {
057b8fec
PO
2110 var client;
2111 beforeEach(function () {
2112 client = make_rfb();
057b8fec 2113 });
b1dee947 2114
057b8fec 2115 describe('Mouse event handlers', function () {
b1dee947 2116 it('should not send button messages in view-only mode', function () {
747b4623 2117 client._viewOnly = true;
057b8fec 2118 sinon.spy(client._sock, 'flush');
747b4623 2119 client._handleMouseButton(0, 0, 1, 0x001);
9ff86fb7 2120 expect(client._sock.flush).to.not.have.been.called;
b1dee947
SR
2121 });
2122
2123 it('should not send movement messages in view-only mode', function () {
747b4623 2124 client._viewOnly = true;
057b8fec 2125 sinon.spy(client._sock, 'flush');
747b4623 2126 client._handleMouseMove(0, 0);
9ff86fb7 2127 expect(client._sock.flush).to.not.have.been.called;
b1dee947
SR
2128 });
2129
2130 it('should send a pointer event on mouse button presses', function () {
747b4623 2131 client._handleMouseButton(10, 12, 1, 0x001);
89d2837f 2132 var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: function () {}};
9ff86fb7
SR
2133 RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x001);
2134 expect(client._sock).to.have.sent(pointer_msg._sQ);
b1dee947
SR
2135 });
2136
d02a99f0 2137 it('should send a mask of 1 on mousedown', function () {
747b4623 2138 client._handleMouseButton(10, 12, 1, 0x001);
89d2837f 2139 var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: function () {}};
cf0236de 2140 RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x001);
9ff86fb7 2141 expect(client._sock).to.have.sent(pointer_msg._sQ);
d02a99f0
SR
2142 });
2143
2144 it('should send a mask of 0 on mouseup', function () {
3b4fd003 2145 client._mouse_buttonMask = 0x001;
747b4623 2146 client._handleMouseButton(10, 12, 0, 0x001);
89d2837f 2147 var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: function () {}};
9ff86fb7
SR
2148 RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
2149 expect(client._sock).to.have.sent(pointer_msg._sQ);
d02a99f0
SR
2150 });
2151
b1dee947 2152 it('should send a pointer event on mouse movement', function () {
747b4623 2153 client._handleMouseMove(10, 12);
89d2837f 2154 var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: function () {}};
9ff86fb7
SR
2155 RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
2156 expect(client._sock).to.have.sent(pointer_msg._sQ);
b1dee947
SR
2157 });
2158
2159 it('should set the button mask so that future mouse movements use it', function () {
747b4623
PO
2160 client._handleMouseButton(10, 12, 1, 0x010);
2161 client._handleMouseMove(13, 9);
89d2837f 2162 var pointer_msg = {_sQ: new Uint8Array(12), _sQlen: 0, flush: function () {}};
9ff86fb7
SR
2163 RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x010);
2164 RFB.messages.pointerEvent(pointer_msg, 13, 9, 0x010);
2165 expect(client._sock).to.have.sent(pointer_msg._sQ);
b1dee947 2166 });
b1dee947
SR
2167 });
2168
2169 describe('Keyboard Event Handlers', function () {
b1dee947 2170 it('should send a key message on a key press', function () {
747b4623 2171 client._handleKeyEvent(0x41, 'KeyA', true);
89d2837f 2172 var key_msg = {_sQ: new Uint8Array(8), _sQlen: 0, flush: function () {}};
d0703d1b 2173 RFB.messages.keyEvent(key_msg, 0x41, 1);
9ff86fb7 2174 expect(client._sock).to.have.sent(key_msg._sQ);
b1dee947
SR
2175 });
2176
2177 it('should not send messages in view-only mode', function () {
747b4623 2178 client._viewOnly = true;
057b8fec 2179 sinon.spy(client._sock, 'flush');
747b4623 2180 client._handleKeyEvent('a', 'KeyA', true);
9ff86fb7 2181 expect(client._sock.flush).to.not.have.been.called;
b1dee947
SR
2182 });
2183 });
2184
2185 describe('WebSocket event handlers', function () {
b1dee947
SR
2186 // message events
2187 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
b1dee947 2188 client._normal_msg = sinon.spy();
38781d93 2189 client._sock._websocket._receive_data(new Uint8Array([]));
b1dee947
SR
2190 expect(client._normal_msg).to.not.have.been.called;
2191 });
2192
c2a4d3ef 2193 it('should handle a message in the connected state as a normal message', function () {
b1dee947 2194 client._normal_msg = sinon.spy();
38781d93 2195 client._sock._websocket._receive_data(new Uint8Array([1, 2, 3]));
b1dee947
SR
2196 expect(client._normal_msg).to.have.been.calledOnce;
2197 });
2198
2199 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2f4516f2 2200 client._rfb_connection_state = 'connecting';
c00ee156 2201 client._rfb_init_state = 'ProtocolVersion';
b1dee947 2202 client._init_msg = sinon.spy();
38781d93 2203 client._sock._websocket._receive_data(new Uint8Array([1, 2, 3]));
b1dee947
SR
2204 expect(client._init_msg).to.have.been.calledOnce;
2205 });
2206
9535539b 2207 it('should process all normal messages directly', function () {
e89eef94
PO
2208 var spy = sinon.spy();
2209 client.addEventListener("bell", spy);
b1dee947 2210 client._sock._websocket._receive_data(new Uint8Array([0x02, 0x02]));
e89eef94 2211 expect(spy).to.have.been.calledTwice;
b1dee947
SR
2212 });
2213
2214 // open events
c2a4d3ef 2215 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
9b84f516 2216 client = new RFB(document.createElement('div'), 'wss://host:8675');
2f4516f2 2217 this.clock.tick();
b1dee947 2218 client._sock._websocket._open();
c00ee156 2219 expect(client._rfb_init_state).to.equal('ProtocolVersion');
b1dee947
SR
2220 });
2221
2222 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
3bb12056 2223 sinon.spy(client, "_fail");
bb25d3d6 2224 client._rfb_connection_state = 'connected';
b1dee947 2225 client._sock._websocket._open();
3bb12056 2226 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
2227 });
2228
2229 // close events
c2a4d3ef 2230 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
bb25d3d6
PO
2231 var real = client._sock._websocket.close;
2232 client._sock._websocket.close = function () {};
2233 client.disconnect();
2234 expect(client._rfb_connection_state).to.equal('disconnecting');
2235 client._sock._websocket.close = real;
b1dee947 2236 client._sock._websocket.close();
c00ee156 2237 expect(client._rfb_connection_state).to.equal('disconnected');
b1dee947
SR
2238 });
2239
b45905ab 2240 it('should fail if we get a close event while connecting', function () {
3bb12056 2241 sinon.spy(client, "_fail");
b45905ab 2242 client._rfb_connection_state = 'connecting';
b1dee947 2243 client._sock._websocket.close();
3bb12056 2244 expect(client._fail).to.have.been.calledOnce;
b1dee947
SR
2245 });
2246
155d78b3
JS
2247 it('should unregister close event handler', function () {
2248 sinon.spy(client._sock, 'off');
bb25d3d6 2249 client.disconnect();
155d78b3
JS
2250 client._sock._websocket.close();
2251 expect(client._sock.off).to.have.been.calledWith('close');
2252 });
2253
b1dee947
SR
2254 // error events do nothing
2255 });
2256 });
2257});