]> git.proxmox.com Git - mirror_novnc.git/blob - tests/test.display.js
Fixed Cursor URI Support Detection
[mirror_novnc.git] / tests / test.display.js
1 // requires local modules: util, base64, display
2 // requires test modules: assertions
3 /* jshint expr: true */
4 var expect = chai.expect;
5
6 describe('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
29 describe('checking for cursor uri support', function () {
30 beforeEach(function () {
31 this._old_change_cursor = Display.changeCursor;
32 });
33
34 it('should disable cursor URIs if there is no support', function () {
35 Display.changeCursor = function(target) {
36 target.style.cursor = undefined;
37 };
38 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
39 expect(display._cursor_uri).to.be.false;
40 });
41
42 it('should enable cursor URIs if there is support', function () {
43 Display.changeCursor = function(target) {
44 target.style.cursor = 'pointer';
45 };
46 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
47 expect(display._cursor_uri).to.be.true;
48 });
49
50 it('respect the cursor_uri option if there is support', function () {
51 Display.changeCursor = function(target) {
52 target.style.cursor = 'pointer';
53 };
54 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false, cursor_uri: false });
55 expect(display._cursor_uri).to.be.false;
56 });
57
58 afterEach(function () {
59 Display.changeCursor = this._old_change_cursor;
60 });
61 });
62
63 describe('viewport handling', function () {
64 var display;
65 beforeEach(function () {
66 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
67 display.resize(5, 5);
68 display.viewportChange(1, 1, 3, 3);
69 display.getCleanDirtyReset();
70 });
71
72 it('should take viewport location into consideration when drawing images', function () {
73 display.resize(4, 4);
74 display.viewportChange(0, 0, 2, 2);
75 display.drawImage(make_image_canvas(basic_data), 1, 1);
76
77 var expected = new Uint8Array(16);
78 var i;
79 for (i = 0; i < 8; i++) { expected[i] = basic_data[i]; }
80 for (i = 8; i < 16; i++) { expected[i] = 0; }
81 expect(display).to.have.displayed(expected);
82 });
83
84 it('should redraw the left side when shifted left', function () {
85 display.viewportChange(-1, 0, 3, 3);
86 var cdr = display.getCleanDirtyReset();
87 expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 2, h: 3 });
88 expect(cdr.dirtyBoxes).to.have.length(1);
89 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 1, w: 2, h: 3 });
90 });
91
92 it('should redraw the right side when shifted right', function () {
93 display.viewportChange(1, 0, 3, 3);
94 var cdr = display.getCleanDirtyReset();
95 expect(cdr.cleanBox).to.deep.equal({ x: 2, y: 1, w: 2, h: 3 });
96 expect(cdr.dirtyBoxes).to.have.length(1);
97 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 4, y: 1, w: 1, h: 3 });
98 });
99
100 it('should redraw the top part when shifted up', function () {
101 display.viewportChange(0, -1, 3, 3);
102 var cdr = display.getCleanDirtyReset();
103 expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 3, h: 2 });
104 expect(cdr.dirtyBoxes).to.have.length(1);
105 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 0, w: 3, h: 1 });
106 });
107
108 it('should redraw the bottom part when shifted down', function () {
109 display.viewportChange(0, 1, 3, 3);
110 var cdr = display.getCleanDirtyReset();
111 expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 2 });
112 expect(cdr.dirtyBoxes).to.have.length(1);
113 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 4, w: 3, h: 1 });
114 });
115
116 it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () {
117 display.viewportChange(0, 1, 3, 3);
118 var cdr1 = display.getCleanDirtyReset();
119 var cdr2 = display.getCleanDirtyReset();
120 expect(cdr1).to.not.deep.equal(cdr2);
121 expect(cdr2.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 3 });
122 expect(cdr2.dirtyBoxes).to.be.empty;
123 });
124
125 it('should simply mark the whole display area as dirty if not using viewports', function () {
126 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false });
127 display.resize(5, 5);
128 var cdr = display.getCleanDirtyReset();
129 expect(cdr.cleanBox).to.deep.equal({ x: 0, y: 0, w: 0, h: 0 });
130 expect(cdr.dirtyBoxes).to.have.length(1);
131 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 0, w: 5, h: 5 });
132 });
133 });
134
135 describe('resizing', function () {
136 var display;
137 beforeEach(function () {
138 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
139 display.resize(4, 3);
140 });
141
142 it('should change the size of the logical canvas', function () {
143 display.resize(5, 7);
144 expect(display._fb_width).to.equal(5);
145 expect(display._fb_height).to.equal(7);
146 });
147
148 it('should update the viewport dimensions', function () {
149 sinon.spy(display, 'viewportChange');
150 display.resize(2, 2);
151 expect(display.viewportChange).to.have.been.calledOnce;
152 });
153 });
154
155 describe('drawing', function () {
156
157 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
158 // basic cases
159 function drawing_tests (pref_js) {
160 var display;
161 beforeEach(function () {
162 display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js });
163 display.resize(4, 4);
164 });
165
166 it('should clear the screen on #clear without a logo set', function () {
167 display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
168 display._logo = null;
169 display.clear();
170 display.resize(4, 4);
171 var empty = [];
172 for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
173 expect(display).to.have.displayed(new Uint8Array(empty));
174 });
175
176 it('should draw the logo on #clear with a logo set', function (done) {
177 display._logo = { width: 4, height: 4, data: make_image_canvas(checked_data).toDataURL() };
178 display._drawCtx._act_drawImg = display._drawCtx.drawImage;
179 display._drawCtx.drawImage = function (img, x, y) {
180 this._act_drawImg(img, x, y);
181 expect(display).to.have.displayed(checked_data);
182 done();
183 };
184 display.clear();
185 expect(display._fb_width).to.equal(4);
186 expect(display._fb_height).to.equal(4);
187 });
188
189 it('should support filling a rectangle with particular color via #fillRect', function () {
190 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
191 display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
192 display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
193 expect(display).to.have.displayed(checked_data);
194 });
195
196 it('should support copying an portion of the canvas via #copyImage', function () {
197 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
198 display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
199 display.copyImage(0, 0, 2, 2, 2, 2);
200 expect(display).to.have.displayed(checked_data);
201 });
202
203 it('should support drawing tile data with a background color and sub tiles', function () {
204 display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
205 display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
206 display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
207 display.finishTile();
208 expect(display).to.have.displayed(checked_data);
209 });
210
211 it('should support drawing BGRX blit images with true color via #blitImage', function () {
212 var data = [];
213 for (var i = 0; i < 16; i++) {
214 data[i * 4] = checked_data[i * 4 + 2];
215 data[i * 4 + 1] = checked_data[i * 4 + 1];
216 data[i * 4 + 2] = checked_data[i * 4];
217 data[i * 4 + 3] = checked_data[i * 4 + 3];
218 }
219 display.blitImage(0, 0, 4, 4, data, 0);
220 expect(display).to.have.displayed(checked_data);
221 });
222
223 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
224 var data = [];
225 for (var i = 0; i < 16; i++) {
226 data[i * 3] = checked_data[i * 4];
227 data[i * 3 + 1] = checked_data[i * 4 + 1];
228 data[i * 3 + 2] = checked_data[i * 4 + 2];
229 }
230 display.blitRgbImage(0, 0, 4, 4, data, 0);
231 expect(display).to.have.displayed(checked_data);
232 });
233
234 it('should support drawing blit images from a data URL via #blitStringImage', function (done) {
235 var img_url = make_image_canvas(checked_data).toDataURL();
236 display._drawCtx._act_drawImg = display._drawCtx.drawImage;
237 display._drawCtx.drawImage = function (img, x, y) {
238 this._act_drawImg(img, x, y);
239 expect(display).to.have.displayed(checked_data);
240 done();
241 };
242 display.blitStringImage(img_url, 0, 0);
243 });
244
245 it('should support drawing solid colors with color maps', function () {
246 display._true_color = false;
247 display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] });
248 display.fillRect(0, 0, 4, 4, [1]);
249 display.fillRect(0, 0, 2, 2, [0]);
250 display.fillRect(2, 2, 2, 2, [0]);
251 expect(display).to.have.displayed(checked_data);
252 });
253
254 it('should support drawing blit images with color maps', function () {
255 display._true_color = false;
256 display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] });
257 var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; });
258 display.blitImage(0, 0, 4, 4, data, 0);
259 expect(display).to.have.displayed(checked_data);
260 });
261
262 it('should support drawing an image object via #drawImage', function () {
263 var img = make_image_canvas(checked_data);
264 display.drawImage(img, 0, 0);
265 expect(display).to.have.displayed(checked_data);
266 });
267 }
268
269 describe('(prefering native methods)', function () { drawing_tests.call(this, false); });
270 describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); });
271 });
272
273 describe('the render queue processor', function () {
274 var display;
275 beforeEach(function () {
276 display = new Display({ target: document.createElement('canvas'), prefer_js: false });
277 display.resize(4, 4);
278 sinon.spy(display, '_scan_renderQ');
279 this.old_requestAnimFrame = window.requestAnimFrame;
280 window.requestAnimFrame = function (cb) {
281 this.next_frame_cb = cb;
282 }.bind(this);
283 this.next_frame = function () { this.next_frame_cb(); };
284 });
285
286 afterEach(function () {
287 window.requestAnimFrame = this.old_requestAnimFrame;
288 });
289
290 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
291 display.renderQ_push({ type: 'noop' }); // does nothing
292 expect(display._scan_renderQ).to.have.been.calledOnce;
293 });
294
295 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
296 display._renderQ.length = 2;
297 display.renderQ_push({ type: 'noop' });
298 expect(display._scan_renderQ).to.not.have.been.called;
299 });
300
301 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
302 var img = { complete: false };
303 display._renderQ = [{ type: 'img', x: 3, y: 4, img: img },
304 { type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }];
305 display.drawImage = sinon.spy();
306 display.fillRect = sinon.spy();
307
308 display._scan_renderQ();
309 expect(display.drawImage).to.not.have.been.called;
310 expect(display.fillRect).to.not.have.been.called;
311
312 display._renderQ[0].img.complete = true;
313 this.next_frame();
314 expect(display.drawImage).to.have.been.calledOnce;
315 expect(display.fillRect).to.have.been.calledOnce;
316 });
317
318 it('should draw a blit image on type "blit"', function () {
319 display.blitImage = sinon.spy();
320 display.renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
321 expect(display.blitImage).to.have.been.calledOnce;
322 expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
323 });
324
325 it('should draw a blit RGB image on type "blitRgb"', function () {
326 display.blitRgbImage = sinon.spy();
327 display.renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
328 expect(display.blitRgbImage).to.have.been.calledOnce;
329 expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
330 });
331
332 it('should copy a region on type "copy"', function () {
333 display.copyImage = sinon.spy();
334 display.renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 });
335 expect(display.copyImage).to.have.been.calledOnce;
336 expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6);
337 });
338
339 it('should fill a rect with a given color on type "fill"', function () {
340 display.fillRect = sinon.spy();
341 display.renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]});
342 expect(display.fillRect).to.have.been.calledOnce;
343 expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]);
344 });
345
346 it('should draw an image from an image object on type "img" (if complete)', function () {
347 display.drawImage = sinon.spy();
348 display.renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } });
349 expect(display.drawImage).to.have.been.calledOnce;
350 expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4);
351 });
352 });
353 });