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