]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.display.js
Use textContent instead of innerHTML
[mirror_novnc.git] / tests / test.display.js
CommitLineData
1e13775b 1// requires local modules: util, base64, display
2c9623b5 2// requires test modules: assertions
1e13775b
SR
3/* jshint expr: true */
4var expect = chai.expect;
5
1e13775b
SR
6describe('Display/Canvas Helper', function () {
7 var checked_data = [
8 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
9 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
10 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
11 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
12 ];
13 checked_data = new Uint8Array(checked_data);
14
15 var basic_data = [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255];
16 basic_data = new Uint8Array(basic_data);
17
18 function make_image_canvas (input_data) {
19 var canvas = document.createElement('canvas');
20 canvas.width = 4;
21 canvas.height = 4;
22 var ctx = canvas.getContext('2d');
23 var data = ctx.createImageData(4, 4);
24 for (var i = 0; i < checked_data.length; i++) { data.data[i] = input_data[i]; }
25 ctx.putImageData(data, 0, 0);
26 return canvas;
27 }
28
74e39051
PO
29 function make_image_png (input_data) {
30 var canvas = make_image_canvas(input_data);
31 var url = canvas.toDataURL();
32 var data = url.split(",")[1];
33 return Base64.decode(data);
34 }
35
53762c31
SR
36 describe('checking for cursor uri support', function () {
37 beforeEach(function () {
58ded70d 38 this._old_browser_supports_cursor_uris = Util.browserSupportsCursorURIs;
53762c31
SR
39 });
40
41 it('should disable cursor URIs if there is no support', function () {
58ded70d 42 Util.browserSupportsCursorURIs = function () { return false; };
53762c31
SR
43 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
44 expect(display._cursor_uri).to.be.false;
45 });
46
47 it('should enable cursor URIs if there is support', function () {
58ded70d 48 Util.browserSupportsCursorURIs = function () { return true; };
53762c31
SR
49 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
50 expect(display._cursor_uri).to.be.true;
51 });
52
53 it('respect the cursor_uri option if there is support', function () {
58ded70d 54 Util.browserSupportsCursorURIs = function () { return false; };
53762c31
SR
55 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false, cursor_uri: false });
56 expect(display._cursor_uri).to.be.false;
57 });
58
59 afterEach(function () {
58ded70d 60 Util.browserSupportsCursorURIs = this._old_browser_supports_cursor_uris;
53762c31
SR
61 });
62 });
63
1e13775b
SR
64 describe('viewport handling', function () {
65 var display;
66 beforeEach(function () {
67 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
68 display.resize(5, 5);
636be753 69 display.viewportChangeSize(3, 3);
70 display.viewportChangePos(1, 1);
1e13775b
SR
71 });
72
73 it('should take viewport location into consideration when drawing images', function () {
e549ae07 74 display.resize(4, 4);
636be753 75 display.viewportChangeSize(2, 2);
1e13775b 76 display.drawImage(make_image_canvas(basic_data), 1, 1);
2ba767a7 77 display.flip();
1e13775b
SR
78
79 var expected = new Uint8Array(16);
80 var i;
81 for (i = 0; i < 8; i++) { expected[i] = basic_data[i]; }
82 for (i = 8; i < 16; i++) { expected[i] = 0; }
83 expect(display).to.have.displayed(expected);
84 });
85
3f781f2a 86 it('should resize the target canvas when resizing the viewport', function() {
2ba767a7 87 display.viewportChangeSize(2, 2);
3f781f2a
PO
88 expect(display._target.width).to.equal(2);
89 expect(display._target.height).to.equal(2);
1e13775b
SR
90 });
91
adf345fd
PO
92 it('should move the viewport if necessary', function() {
93 display.viewportChangeSize(5, 5);
94 expect(display.absX(0)).to.equal(0);
95 expect(display.absY(0)).to.equal(0);
96 expect(display._target.width).to.equal(5);
97 expect(display._target.height).to.equal(5);
98 });
99
100 it('should limit the viewport to the framebuffer size', function() {
101 display.viewportChangeSize(6, 6);
102 expect(display._target.width).to.equal(5);
103 expect(display._target.height).to.equal(5);
104 });
105
2ba767a7
PO
106 it('should redraw when moving the viewport', function () {
107 display.flip = sinon.spy();
108 display.viewportChangePos(-1, 1);
109 expect(display.flip).to.have.been.calledOnce;
1e13775b
SR
110 });
111
2ba767a7
PO
112 it('should redraw when resizing the viewport', function () {
113 display.flip = sinon.spy();
114 display.viewportChangeSize(2, 2);
115 expect(display.flip).to.have.been.calledOnce;
1e13775b 116 });
fdedbafb 117
3f781f2a 118 it('should report clipping when framebuffer > viewport', function () {
fdedbafb 119 var clipping = display.clippingDisplay();
120 expect(clipping).to.be.true;
121 });
122
3f781f2a
PO
123 it('should report not clipping when framebuffer = viewport', function () {
124 display.viewportChangeSize(5, 5);
fdedbafb 125 var clipping = display.clippingDisplay();
126 expect(clipping).to.be.false;
127 });
adf345fd
PO
128
129 it('should show the entire framebuffer when disabling the viewport', function() {
130 display.set_viewport(false);
131 expect(display.absX(0)).to.equal(0);
132 expect(display.absY(0)).to.equal(0);
133 expect(display._target.width).to.equal(5);
134 expect(display._target.height).to.equal(5);
135 });
136
137 it('should ignore viewport changes when the viewport is disabled', function() {
138 display.set_viewport(false);
139 display.viewportChangeSize(2, 2);
140 display.viewportChangePos(1, 1);
141 expect(display.absX(0)).to.equal(0);
142 expect(display.absY(0)).to.equal(0);
143 expect(display._target.width).to.equal(5);
144 expect(display._target.height).to.equal(5);
145 });
146
147 it('should show the entire framebuffer just after enabling the viewport', function() {
148 display.set_viewport(false);
149 display.set_viewport(true);
150 expect(display.absX(0)).to.equal(0);
151 expect(display.absY(0)).to.equal(0);
152 expect(display._target.width).to.equal(5);
153 expect(display._target.height).to.equal(5);
154 });
fdedbafb 155 });
156
1e13775b
SR
157 describe('resizing', function () {
158 var display;
159 beforeEach(function () {
adf345fd
PO
160 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false });
161 display.resize(4, 4);
1e13775b
SR
162 });
163
164 it('should change the size of the logical canvas', function () {
165 display.resize(5, 7);
166 expect(display._fb_width).to.equal(5);
167 expect(display._fb_height).to.equal(7);
168 });
169
2ba767a7 170 it('should keep the framebuffer data', function () {
adf345fd 171 display.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
2ba767a7
PO
172 display.resize(2, 2);
173 display.flip();
174 var expected = [];
175 for (var i = 0; i < 4 * 2*2; i += 4) {
176 expected[i] = 0xff;
177 expected[i+1] = expected[i+2] = 0;
178 expected[i+3] = 0xff;
179 }
180 expect(display).to.have.displayed(new Uint8Array(expected));
181 });
adf345fd
PO
182
183 describe('viewport', function () {
184 beforeEach(function () {
185 display.set_viewport(true);
186 display.viewportChangeSize(3, 3);
187 display.viewportChangePos(1, 1);
188 });
189
190 it('should keep the viewport position and size if possible', function () {
191 display.resize(6, 6);
192 expect(display.absX(0)).to.equal(1);
193 expect(display.absY(0)).to.equal(1);
194 expect(display._target.width).to.equal(3);
195 expect(display._target.height).to.equal(3);
196 });
197
198 it('should move the viewport if necessary', function () {
199 display.resize(3, 3);
200 expect(display.absX(0)).to.equal(0);
201 expect(display.absY(0)).to.equal(0);
202 expect(display._target.width).to.equal(3);
203 expect(display._target.height).to.equal(3);
204 });
205
206 it('should shrink the viewport if necessary', function () {
207 display.resize(2, 2);
208 expect(display.absX(0)).to.equal(0);
209 expect(display.absY(0)).to.equal(0);
210 expect(display._target.width).to.equal(2);
211 expect(display._target.height).to.equal(2);
212 });
213 });
1e13775b
SR
214 });
215
72747869
SR
216 describe('rescaling', function () {
217 var display;
218 var canvas;
219
220 beforeEach(function () {
221 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
adf345fd
PO
222 display.resize(4, 4);
223 display.viewportChangeSize(3, 3);
224 display.viewportChangePos(1, 1);
72747869
SR
225 canvas = display.get_target();
226 document.body.appendChild(canvas);
227 });
228
229 afterEach(function () {
230 document.body.removeChild(canvas);
231 });
232
233 it('should not change the bitmap size of the canvas', function () {
adf345fd
PO
234 display.set_scale(2.0);
235 expect(canvas.width).to.equal(3);
72747869
SR
236 expect(canvas.height).to.equal(3);
237 });
238
239 it('should change the effective rendered size of the canvas', function () {
adf345fd
PO
240 display.set_scale(2.0);
241 expect(canvas.clientWidth).to.equal(6);
242 expect(canvas.clientHeight).to.equal(6);
243 });
244
245 it('should not change when resizing', function () {
246 display.set_scale(2.0);
247 display.resize(5, 5);
248 expect(display.get_scale()).to.equal(2.0);
249 expect(canvas.width).to.equal(3);
250 expect(canvas.height).to.equal(3);
251 expect(canvas.clientWidth).to.equal(6);
252 expect(canvas.clientHeight).to.equal(6);
72747869
SR
253 });
254 });
255
256 describe('autoscaling', function () {
257 var display;
258 var canvas;
259
260 beforeEach(function () {
261 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
262 display.resize(4, 3);
263 canvas = display.get_target();
264 document.body.appendChild(canvas);
265 });
266
267 afterEach(function () {
268 document.body.removeChild(canvas);
269 });
270
271 it('should preserve aspect ratio while autoscaling', function () {
272 display.autoscale(16, 9);
273 expect(canvas.clientWidth / canvas.clientHeight).to.equal(4 / 3);
274 });
275
276 it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
277 expect(display.autoscale(9, 16)).to.equal(9 / 4);
278 expect(canvas.clientWidth).to.equal(9);
279 expect(canvas.clientHeight).to.equal(7); // round 9 / (4 / 3)
280 });
281
282 it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
283 expect(display.autoscale(16, 9)).to.equal(3); // 9 / 3
284 expect(canvas.clientWidth).to.equal(12); // 16 * (4 / 3)
285 expect(canvas.clientHeight).to.equal(9);
286
287 });
288
289 it('should not change the bitmap size of the canvas', function () {
290 display.autoscale(16, 9);
291 expect(canvas.width).to.equal(4);
292 expect(canvas.height).to.equal(3);
293 });
294
295 it('should not upscale when downscaleOnly is true', function () {
296 expect(display.autoscale(2, 2, true)).to.equal(0.5);
297 expect(canvas.clientWidth).to.equal(2);
298 expect(canvas.clientHeight).to.equal(2);
299
300 expect(display.autoscale(16, 9, true)).to.equal(1.0);
301 expect(canvas.clientWidth).to.equal(4);
302 expect(canvas.clientHeight).to.equal(3);
303 });
304 });
305
1e13775b
SR
306 describe('drawing', function () {
307
308 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
309 // basic cases
310 function drawing_tests (pref_js) {
311 var display;
312 beforeEach(function () {
313 display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js });
314 display.resize(4, 4);
315 });
316
317 it('should clear the screen on #clear without a logo set', function () {
318 display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
319 display._logo = null;
320 display.clear();
321 display.resize(4, 4);
322 var empty = [];
323 for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
324 expect(display).to.have.displayed(new Uint8Array(empty));
325 });
326
327 it('should draw the logo on #clear with a logo set', function (done) {
74e39051
PO
328 display._logo = { width: 4, height: 4, type: "image/png", data: make_image_png(checked_data) };
329 display.clear();
330 display.set_onFlush(function () {
1e13775b 331 expect(display).to.have.displayed(checked_data);
74e39051
PO
332 expect(display._fb_width).to.equal(4);
333 expect(display._fb_height).to.equal(4);
1e13775b 334 done();
74e39051
PO
335 });
336 display.flush();
1e13775b
SR
337 });
338
2ba767a7
PO
339 it('should not draw directly on the target canvas', function () {
340 display.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
341 display.flip();
342 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
343 var expected = [];
344 for (var i = 0; i < 4 * display._fb_width * display._fb_height; i += 4) {
345 expected[i] = 0xff;
346 expected[i+1] = expected[i+2] = 0;
347 expected[i+3] = 0xff;
348 }
349 expect(display).to.have.displayed(new Uint8Array(expected));
350 });
351
1e13775b
SR
352 it('should support filling a rectangle with particular color via #fillRect', function () {
353 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
354 display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
355 display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
2ba767a7 356 display.flip();
1e13775b
SR
357 expect(display).to.have.displayed(checked_data);
358 });
359
360 it('should support copying an portion of the canvas via #copyImage', function () {
361 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
362 display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
363 display.copyImage(0, 0, 2, 2, 2, 2);
2ba767a7 364 display.flip();
1e13775b
SR
365 expect(display).to.have.displayed(checked_data);
366 });
367
74e39051
PO
368 it('should support drawing images via #imageRect', function (done) {
369 display.imageRect(0, 0, "image/png", make_image_png(checked_data));
370 display.flip();
371 display.set_onFlush(function () {
372 expect(display).to.have.displayed(checked_data);
373 done();
374 });
375 display.flush();
376 });
377
1e13775b
SR
378 it('should support drawing tile data with a background color and sub tiles', function () {
379 display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
380 display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
381 display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
382 display.finishTile();
2ba767a7 383 display.flip();
1e13775b
SR
384 expect(display).to.have.displayed(checked_data);
385 });
386
387 it('should support drawing BGRX blit images with true color via #blitImage', function () {
388 var data = [];
389 for (var i = 0; i < 16; i++) {
390 data[i * 4] = checked_data[i * 4 + 2];
391 data[i * 4 + 1] = checked_data[i * 4 + 1];
392 data[i * 4 + 2] = checked_data[i * 4];
393 data[i * 4 + 3] = checked_data[i * 4 + 3];
394 }
395 display.blitImage(0, 0, 4, 4, data, 0);
2ba767a7 396 display.flip();
1e13775b
SR
397 expect(display).to.have.displayed(checked_data);
398 });
399
400 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
401 var data = [];
402 for (var i = 0; i < 16; i++) {
403 data[i * 3] = checked_data[i * 4];
404 data[i * 3 + 1] = checked_data[i * 4 + 1];
405 data[i * 3 + 2] = checked_data[i * 4 + 2];
406 }
407 display.blitRgbImage(0, 0, 4, 4, data, 0);
2ba767a7 408 display.flip();
1e13775b
SR
409 expect(display).to.have.displayed(checked_data);
410 });
411
1e13775b
SR
412 it('should support drawing solid colors with color maps', function () {
413 display._true_color = false;
414 display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] });
a369a80c
SR
415 display.fillRect(0, 0, 4, 4, 1);
416 display.fillRect(0, 0, 2, 2, 0);
417 display.fillRect(2, 2, 2, 2, 0);
2ba767a7 418 display.flip();
1e13775b
SR
419 expect(display).to.have.displayed(checked_data);
420 });
421
422 it('should support drawing blit images with color maps', function () {
423 display._true_color = false;
424 display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] });
425 var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; });
426 display.blitImage(0, 0, 4, 4, data, 0);
2ba767a7 427 display.flip();
1e13775b
SR
428 expect(display).to.have.displayed(checked_data);
429 });
430
431 it('should support drawing an image object via #drawImage', function () {
432 var img = make_image_canvas(checked_data);
433 display.drawImage(img, 0, 0);
2ba767a7 434 display.flip();
1e13775b
SR
435 expect(display).to.have.displayed(checked_data);
436 });
437 }
438
439 describe('(prefering native methods)', function () { drawing_tests.call(this, false); });
440 describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); });
441 });
442
443 describe('the render queue processor', function () {
444 var display;
445 beforeEach(function () {
446 display = new Display({ target: document.createElement('canvas'), prefer_js: false });
447 display.resize(4, 4);
448 sinon.spy(display, '_scan_renderQ');
1e13775b
SR
449 });
450
451 afterEach(function () {
e4fef7be 452 window.requestAnimationFrame = this.old_requestAnimationFrame;
1e13775b
SR
453 });
454
455 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
1578fa68 456 display._renderQ_push({ type: 'noop' }); // does nothing
1e13775b
SR
457 expect(display._scan_renderQ).to.have.been.calledOnce;
458 });
459
460 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
461 display._renderQ.length = 2;
1578fa68 462 display._renderQ_push({ type: 'noop' });
1e13775b
SR
463 expect(display._scan_renderQ).to.not.have.been.called;
464 });
465
466 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
bb6965f2 467 var img = { complete: false, addEventListener: sinon.spy() }
1e13775b
SR
468 display._renderQ = [{ type: 'img', x: 3, y: 4, img: img },
469 { type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }];
470 display.drawImage = sinon.spy();
471 display.fillRect = sinon.spy();
472
473 display._scan_renderQ();
474 expect(display.drawImage).to.not.have.been.called;
475 expect(display.fillRect).to.not.have.been.called;
bb6965f2 476 expect(img.addEventListener).to.have.been.calledOnce;
1e13775b
SR
477
478 display._renderQ[0].img.complete = true;
bb6965f2 479 display._scan_renderQ();
1e13775b
SR
480 expect(display.drawImage).to.have.been.calledOnce;
481 expect(display.fillRect).to.have.been.calledOnce;
bb6965f2 482 expect(img.addEventListener).to.have.been.calledOnce;
1e13775b
SR
483 });
484
3181a032
PO
485 it('should call callback when queue is flushed', function () {
486 display.set_onFlush(sinon.spy());
487 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
488 expect(display.get_onFlush()).to.not.have.been.called;
489 display.flush();
490 expect(display.get_onFlush()).to.have.been.calledOnce;
491 });
492
1e13775b
SR
493 it('should draw a blit image on type "blit"', function () {
494 display.blitImage = sinon.spy();
1578fa68 495 display._renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
1e13775b
SR
496 expect(display.blitImage).to.have.been.calledOnce;
497 expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
498 });
499
500 it('should draw a blit RGB image on type "blitRgb"', function () {
501 display.blitRgbImage = sinon.spy();
1578fa68 502 display._renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
1e13775b
SR
503 expect(display.blitRgbImage).to.have.been.calledOnce;
504 expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
505 });
506
507 it('should copy a region on type "copy"', function () {
508 display.copyImage = sinon.spy();
1578fa68 509 display._renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 });
1e13775b
SR
510 expect(display.copyImage).to.have.been.calledOnce;
511 expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6);
512 });
513
514 it('should fill a rect with a given color on type "fill"', function () {
515 display.fillRect = sinon.spy();
1578fa68 516 display._renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]});
1e13775b
SR
517 expect(display.fillRect).to.have.been.calledOnce;
518 expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]);
519 });
520
521 it('should draw an image from an image object on type "img" (if complete)', function () {
522 display.drawImage = sinon.spy();
1578fa68 523 display._renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } });
1e13775b
SR
524 expect(display.drawImage).to.have.been.calledOnce;
525 expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4);
526 });
527 });
528});