1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | exports.defineAutoTests = function () {
|
26 | describe('Camera (navigator.camera)', function () {
|
27 | it("should exist", function () {
|
28 | expect(navigator.camera).toBeDefined();
|
29 | });
|
30 |
|
31 | it("should contain a getPicture function", function () {
|
32 | expect(navigator.camera.getPicture).toBeDefined();
|
33 | expect(typeof navigator.camera.getPicture == 'function').toBe(true);
|
34 | });
|
35 | });
|
36 |
|
37 | describe('Camera Constants (window.Camera + navigator.camera)', function () {
|
38 | it("camera.spec.1 window.Camera should exist", function () {
|
39 | expect(window.Camera).toBeDefined();
|
40 | });
|
41 |
|
42 | it("camera.spec.2 should contain three DestinationType constants", function () {
|
43 | expect(Camera.DestinationType.DATA_URL).toBe(0);
|
44 | expect(Camera.DestinationType.FILE_URI).toBe(1);
|
45 | expect(Camera.DestinationType.NATIVE_URI).toBe(2);
|
46 | expect(navigator.camera.DestinationType.DATA_URL).toBe(0);
|
47 | expect(navigator.camera.DestinationType.FILE_URI).toBe(1);
|
48 | expect(navigator.camera.DestinationType.NATIVE_URI).toBe(2);
|
49 | });
|
50 |
|
51 | it("camera.spec.3 should contain two EncodingType constants", function () {
|
52 | expect(Camera.EncodingType.JPEG).toBe(0);
|
53 | expect(Camera.EncodingType.PNG).toBe(1);
|
54 | expect(navigator.camera.EncodingType.JPEG).toBe(0);
|
55 | expect(navigator.camera.EncodingType.PNG).toBe(1);
|
56 | });
|
57 |
|
58 | it("camera.spec.4 should contain three MediaType constants", function () {
|
59 | expect(Camera.MediaType.PICTURE).toBe(0);
|
60 | expect(Camera.MediaType.VIDEO).toBe(1);
|
61 | expect(Camera.MediaType.ALLMEDIA).toBe(2);
|
62 | expect(navigator.camera.MediaType.PICTURE).toBe(0);
|
63 | expect(navigator.camera.MediaType.VIDEO).toBe(1);
|
64 | expect(navigator.camera.MediaType.ALLMEDIA).toBe(2);
|
65 | });
|
66 |
|
67 | it("camera.spec.5 should contain three PictureSourceType constants", function () {
|
68 | expect(Camera.PictureSourceType.PHOTOLIBRARY).toBe(0);
|
69 | expect(Camera.PictureSourceType.CAMERA).toBe(1);
|
70 | expect(Camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2);
|
71 | expect(navigator.camera.PictureSourceType.PHOTOLIBRARY).toBe(0);
|
72 | expect(navigator.camera.PictureSourceType.CAMERA).toBe(1);
|
73 | expect(navigator.camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2);
|
74 | });
|
75 | });
|
76 | };
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | exports.defineManualTests = function (contentEl, createActionButton) {
|
84 | var pictureUrl = null;
|
85 | var fileObj = null;
|
86 | var fileEntry = null;
|
87 | var pageStartTime = +new Date();
|
88 |
|
89 |
|
90 | var camQualityDefault = ['50', 50];
|
91 | var camDestinationTypeDefault = ['FILE_URI', 1];
|
92 | var camPictureSourceTypeDefault = ['CAMERA', 1];
|
93 | var camAllowEditDefault = ['allowEdit', false];
|
94 | var camEncodingTypeDefault = ['JPEG', 0];
|
95 | var camMediaTypeDefault = ['mediaType', 0];
|
96 | var camCorrectOrientationDefault = ['correctOrientation', false];
|
97 | var camSaveToPhotoAlbumDefault = ['saveToPhotoAlbum', true];
|
98 |
|
99 | function log(value) {
|
100 | console.log(value);
|
101 | document.getElementById('camera_status').textContent += (new Date() - pageStartTime) / 1000 + ': ' + value + '\n';
|
102 | }
|
103 |
|
104 | function clearStatus() {
|
105 | document.getElementById('camera_status').innerHTML = '';
|
106 | document.getElementById('camera_image').src = 'about:blank';
|
107 | var canvas = document.getElementById('canvas');
|
108 | canvas.width = canvas.height = 1;
|
109 | pictureUrl = null;
|
110 | fileObj = null;
|
111 | fileEntry = null;
|
112 | }
|
113 |
|
114 | function setPicture(url, callback) {
|
115 | try {
|
116 | window.atob(url);
|
117 |
|
118 | url = "data:image/jpeg;base64," + url;
|
119 | } catch (e) {
|
120 |
|
121 | }
|
122 | log('URL: "' + url.slice(0, 90) + '"');
|
123 |
|
124 | pictureUrl = url;
|
125 | var img = document.getElementById('camera_image');
|
126 | var startTime = new Date();
|
127 | img.src = url;
|
128 | img.onload = function () {
|
129 | log('Img size: ' + img.naturalWidth + 'x' + img.naturalHeight);
|
130 | log('Image tag load time: ' + (new Date() - startTime));
|
131 | if (callback) {
|
132 | callback();
|
133 | }
|
134 | };
|
135 | }
|
136 |
|
137 | function onGetPictureError(e) {
|
138 | log('Error getting picture: ' + (e.code || e));
|
139 | }
|
140 |
|
141 | function getPictureWin(data) {
|
142 | setPicture(data);
|
143 |
|
144 | if (pictureUrl.indexOf('file:') === 0 || pictureUrl.indexOf('content:') === 0 || pictureUrl.indexOf('ms-appdata:') === 0 || pictureUrl.indexOf('assets-library:') === 0) {
|
145 | resolveLocalFileSystemURL(data, function (e) {
|
146 | fileEntry = e;
|
147 | logCallback('resolveLocalFileSystemURL()', true)(e.toURL());
|
148 | readFile();
|
149 | }, logCallback('resolveLocalFileSystemURL()', false));
|
150 | } else if (pictureUrl.indexOf('data:image/jpeg;base64') === 0) {
|
151 |
|
152 | } else {
|
153 | var path = pictureUrl.replace(/^file:\/\/(localhost)?/, '').replace(/%20/g, ' ');
|
154 | fileEntry = new FileEntry('image_name.png', path);
|
155 | }
|
156 | }
|
157 |
|
158 | function getPicture() {
|
159 | clearStatus();
|
160 | var options = extractOptions();
|
161 | log('Getting picture with options: ' + JSON.stringify(options));
|
162 | var popoverHandle = navigator.camera.getPicture(getPictureWin, onGetPictureError, options);
|
163 |
|
164 |
|
165 | window.onorientationchange = function () {
|
166 | var newPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, 0);
|
167 | popoverHandle.setPosition(newPopoverOptions);
|
168 | };
|
169 | }
|
170 |
|
171 | function uploadImage() {
|
172 | var ft = new FileTransfer(),
|
173 | options = new FileUploadOptions();
|
174 | options.fileKey = "photo";
|
175 | options.fileName = 'test.jpg';
|
176 | options.mimeType = "image/jpeg";
|
177 | ft.onprogress = function (progressEvent) {
|
178 | console.log('progress: ' + progressEvent.loaded + ' of ' + progressEvent.total);
|
179 | };
|
180 | var server = "http://cordova-filetransfer.jitsu.com";
|
181 |
|
182 | ft.upload(pictureUrl, server + '/upload', win, fail, options);
|
183 | function win(information_back) {
|
184 | log('upload complete');
|
185 | }
|
186 | function fail(message) {
|
187 | log('upload failed: ' + JSON.stringify(message));
|
188 | }
|
189 | }
|
190 |
|
191 | function logCallback(apiName, success) {
|
192 | return function () {
|
193 | log('Call to ' + apiName + (success ? ' success: ' : ' failed: ') + JSON.stringify([].slice.call(arguments)));
|
194 | };
|
195 | }
|
196 |
|
197 | |
198 |
|
199 |
|
200 |
|
201 | function readFile() {
|
202 | function onFileReadAsDataURL(evt) {
|
203 | var img = document.getElementById('camera_image');
|
204 | img.style.visibility = "visible";
|
205 | img.style.display = "block";
|
206 | img.src = evt.target.result;
|
207 | log("FileReader.readAsDataURL success");
|
208 | }
|
209 |
|
210 | function onFileReceived(file) {
|
211 | log('Got file: ' + JSON.stringify(file));
|
212 | fileObj = file;
|
213 |
|
214 | var reader = new FileReader();
|
215 | reader.onload = function () {
|
216 | log('FileReader.readAsDataURL() - length = ' + reader.result.length);
|
217 | };
|
218 | reader.onerror = logCallback('FileReader.readAsDataURL', false);
|
219 | reader.onloadend = onFileReadAsDataURL;
|
220 | reader.readAsDataURL(file);
|
221 | }
|
222 |
|
223 |
|
224 | if (fileObj) {
|
225 | onFileReceived(fileObj);
|
226 | } else {
|
227 | fileEntry.file(onFileReceived, logCallback('FileEntry.file', false));
|
228 | }
|
229 | }
|
230 |
|
231 | function getFileInfo() {
|
232 |
|
233 | fileEntry.getMetadata(logCallback('FileEntry.getMetadata', true), logCallback('FileEntry.getMetadata', false));
|
234 | fileEntry.setMetadata(logCallback('FileEntry.setMetadata', true), logCallback('FileEntry.setMetadata', false), { "com.apple.MobileBackup": 1 });
|
235 | fileEntry.getParent(logCallback('FileEntry.getParent', true), logCallback('FileEntry.getParent', false));
|
236 | fileEntry.getParent(logCallback('FileEntry.getParent', true), logCallback('FileEntry.getParent', false));
|
237 | }
|
238 |
|
239 | |
240 |
|
241 |
|
242 |
|
243 | function copyImage() {
|
244 | var onFileSystemReceived = function (fileSystem) {
|
245 | var destDirEntry = fileSystem.root;
|
246 | var origName = fileEntry.name;
|
247 |
|
248 |
|
249 | fileEntry.copyTo(destDirEntry, 'copied_file.png', logCallback('FileEntry.copyTo', true), logCallback('FileEntry.copyTo', false));
|
250 | fileEntry.moveTo(destDirEntry, 'moved_file.png', logCallback('FileEntry.moveTo', true), logCallback('FileEntry.moveTo', false));
|
251 |
|
252 |
|
253 |
|
254 | resolveLocalFileSystemURL(destDirEntry.nativeURL+'moved_file.png', function(fileEntry) {
|
255 | fileEntry.moveTo(destDirEntry, origName, logCallback('FileEntry.moveTo', true), logCallback('FileEntry.moveTo', false));
|
256 | console.log('Cleanup: successfully renamed file back to original name');
|
257 | }, function () {
|
258 | console.log('Cleanup: failed to rename file back to original name');
|
259 | });
|
260 |
|
261 |
|
262 | resolveLocalFileSystemURL(destDirEntry.nativeURL+'copied_file.png', function(fileEntry) {
|
263 | fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
|
264 | console.log('Cleanup: successfully removed copied file');
|
265 | }, function () {
|
266 | console.log('Cleanup: failed to remove copied file');
|
267 | });
|
268 | };
|
269 |
|
270 | window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, onFileSystemReceived, null);
|
271 | }
|
272 |
|
273 | |
274 |
|
275 |
|
276 |
|
277 | function writeImage() {
|
278 | var onFileWriterReceived = function (fileWriter) {
|
279 | fileWriter.onwrite = logCallback('FileWriter.write', true);
|
280 | fileWriter.onerror = logCallback('FileWriter.write', false);
|
281 | fileWriter.write("some text!");
|
282 | };
|
283 |
|
284 | var onFileTruncateWriterReceived = function (fileWriter) {
|
285 | fileWriter.onwrite = logCallback('FileWriter.truncate', true);
|
286 | fileWriter.onerror = logCallback('FileWriter.truncate', false);
|
287 | fileWriter.truncate(10);
|
288 | };
|
289 |
|
290 | fileEntry.createWriter(onFileWriterReceived, logCallback('FileEntry.createWriter', false));
|
291 | fileEntry.createWriter(onFileTruncateWriterReceived, null);
|
292 | }
|
293 |
|
294 | function displayImageUsingCanvas() {
|
295 | var canvas = document.getElementById('canvas');
|
296 | var img = document.getElementById('camera_image');
|
297 | var w = img.width;
|
298 | var h = img.height;
|
299 | h = 100 / w * h;
|
300 | w = 100;
|
301 | canvas.width = w;
|
302 | canvas.height = h;
|
303 | var context = canvas.getContext('2d');
|
304 | context.drawImage(img, 0, 0, w, h);
|
305 | }
|
306 |
|
307 | |
308 |
|
309 |
|
310 |
|
311 | function removeImage() {
|
312 | fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
|
313 | }
|
314 |
|
315 | function testInputTag(inputEl) {
|
316 | clearStatus();
|
317 |
|
318 |
|
319 | window.setTimeout(function () {
|
320 | testNativeFile2(inputEl);
|
321 | }, 0);
|
322 | }
|
323 |
|
324 | function testNativeFile2(inputEl) {
|
325 | if (!inputEl.value) {
|
326 | alert('No file selected.');
|
327 | return;
|
328 | }
|
329 | fileObj = inputEl.files[0];
|
330 | if (!fileObj) {
|
331 | alert('Got value but no file.');
|
332 | return;
|
333 | }
|
334 | var URLApi = window.URL || window.webkitURL;
|
335 | if (URLApi) {
|
336 | var blobURL = URLApi.createObjectURL(fileObj);
|
337 | if (blobURL) {
|
338 | setPicture(blobURL, function () {
|
339 | URLApi.revokeObjectURL(blobURL);
|
340 | });
|
341 | } else {
|
342 | log('URL.createObjectURL returned null');
|
343 | }
|
344 | } else {
|
345 | log('URL.createObjectURL() not supported.');
|
346 | }
|
347 | }
|
348 |
|
349 | function extractOptions() {
|
350 | var els = document.querySelectorAll('#image-options select');
|
351 | var ret = {};
|
352 |
|
353 | for (var i = 0, el; el = els[i]; ++i) {
|
354 | var value = el.value;
|
355 | if (value === '') continue;
|
356 | value = +value;
|
357 |
|
358 | if (el.isBool) {
|
359 | ret[el.getAttribute("name")] = !!value;
|
360 | } else {
|
361 | ret[el.getAttribute("name")] = value;
|
362 | }
|
363 | }
|
364 |
|
365 | return ret;
|
366 | }
|
367 |
|
368 | function createOptionsEl(name, values, selectionDefault) {
|
369 | var openDiv = '<div style="display: inline-block">' + name + ': ';
|
370 | var select = '<select name=' + name + ' id="' + name + '">';
|
371 |
|
372 | var defaultOption = '';
|
373 | if (selectionDefault === undefined) {
|
374 | defaultOption = '<option value="">default</option>';
|
375 | }
|
376 |
|
377 | var options = '';
|
378 | if (typeof values == 'boolean') {
|
379 | values = { 'true': 1, 'false': 0 };
|
380 | }
|
381 | for (var k in values) {
|
382 | var isSelected = '';
|
383 | if (selectionDefault) {
|
384 | if (selectionDefault[0] == k) {
|
385 | isSelected = 'selected';
|
386 | }
|
387 | }
|
388 | options += '<option value="' + values[k] + '" ' + isSelected + '>' + k + '</option>';
|
389 | }
|
390 |
|
391 | var closeDiv = '</select></div>';
|
392 |
|
393 | return openDiv + select + defaultOption + options + closeDiv;
|
394 | }
|
395 |
|
396 |
|
397 |
|
398 | var info_div = '<h1>Camera</h1>' +
|
399 | '<div id="info">' +
|
400 | '<b>Status:</b> <div id="camera_status"></div>' +
|
401 | 'img: <img width="100" id="camera_image">' +
|
402 | 'canvas: <canvas id="canvas" width="1" height="1"></canvas>' +
|
403 | '</div>',
|
404 | options_div = '<h2>Cordova Camera API Options</h2>' +
|
405 | '<div id="image-options">' +
|
406 | createOptionsEl('sourceType', Camera.PictureSourceType, camPictureSourceTypeDefault) +
|
407 | createOptionsEl('destinationType', Camera.DestinationType, camDestinationTypeDefault) +
|
408 | createOptionsEl('encodingType', Camera.EncodingType, camEncodingTypeDefault) +
|
409 | createOptionsEl('mediaType', Camera.MediaType, camMediaTypeDefault) +
|
410 | createOptionsEl('quality', { '0': 0, '50': 50, '80': 80, '100': 100 }, camQualityDefault) +
|
411 | createOptionsEl('targetWidth', { '50': 50, '200': 200, '800': 800, '2048': 2048 }) +
|
412 | createOptionsEl('targetHeight', { '50': 50, '200': 200, '800': 800, '2048': 2048 }) +
|
413 | createOptionsEl('allowEdit', true, camAllowEditDefault) +
|
414 | createOptionsEl('correctOrientation', true, camCorrectOrientationDefault) +
|
415 | createOptionsEl('saveToPhotoAlbum', true, camSaveToPhotoAlbumDefault) +
|
416 | createOptionsEl('cameraDirection', Camera.Direction) +
|
417 | '</div>',
|
418 | getpicture_div = '<div id="getpicture"></div>',
|
419 | test_procedure = '<h4>Recommended Test Procedure</h4>' +
|
420 | 'Options not specified should be the default value' +
|
421 | '<br>Status box should update with image and info whenever an image is taken or selected from library' +
|
422 | '</p><div style="background:#B0C4DE;border:1px solid #FFA07A;margin:15px 6px 0px;min-width:295px;max-width:97%;padding:4px 0px 2px 10px;min-height:160px;max-height:200px;overflow:auto">' +
|
423 | '<ol> <li>All default options. Should be able to edit once picture is taken and will be saved to library.</li>' +
|
424 | '</p><li>sourceType=PHOTOLIBRARY<br>Should be able to see picture that was just taken in previous test and edit when selected</li>' +
|
425 | '</p><li>sourceType=Camera<br>allowEdit=false<br>saveToPhotoAlbum=false<br>Should not be able to edit when taken and will not save to library</li>' +
|
426 | '</p><li>encodingType=PNG<br>allowEdit=true<br>saveToPhotoAlbum=true<br>cameraDirection=FRONT<br>Should bring up front camera. Verify in status box info URL that image is encoded as PNG.</li>' +
|
427 | '</p><li>sourceType=SAVEDPHOTOALBUM<br>mediaType=VIDEO<br>Should only be able to select a video</li>' +
|
428 | '</p><li>sourceType=SAVEDPHOTOALBUM<br>mediaType=PICTURE<br>allowEdit=false<br>Should only be able to select a picture and not edit</li>' +
|
429 | '</p><li>sourceType=PHOTOLIBRARY<br>mediaType=ALLMEDIA<br>allowEdit=true<br>Should be able to select pics and videos and edit picture if selected</li>' +
|
430 | '</p><li>sourceType=CAMERA<br>targetWidth & targetHeight=50<br>allowEdit=false<br>Do Get File Metadata test below and take note of size<br>Repeat test but with width and height=800. Size should be significantly larger.</li>' +
|
431 | '</p><li>quality=0<br>targetWidth & targetHeight=default<br>allowEdit=false<br>Do Get File Metadata test below and take note of size<br>Repeat test but with quality=80. Size should be significantly larger.</li>' +
|
432 | '</ol></div>',
|
433 | inputs_div = '<h2>Native File Inputs</h2>' +
|
434 | 'For the following tests, status box should update with file selected' +
|
435 | '</p><div>input type=file <input type="file" class="testInputTag"></div>' +
|
436 | '<div>capture=camera <input type="file" accept="image/*;capture=camera" class="testInputTag"></div>' +
|
437 | '<div>capture=camcorder <input type="file" accept="video/*;capture=camcorder" class="testInputTag"></div>' +
|
438 | '<div>capture=microphone <input type="file" accept="audio/*;capture=microphone" class="testInputTag"></div>',
|
439 | actions_div = '<h2>Actions</h2>' +
|
440 | 'For the following tests, ensure that an image is set in status box' +
|
441 | '</p><div id="metadata"></div>' +
|
442 | 'Expected result: Get metadata about file selected.<br>Status box will show, along with the metadata, "Call to FileEntry.getMetadata success, Call to FileEntry.setMetadata success, Call to FileEntry.getParent success"' +
|
443 | '</p><div id="reader"></div>' +
|
444 | 'Expected result: Read contents of file.<br>Status box will show "Got file: {some metadata}, FileReader.readAsDataURL() - length = someNumber"' +
|
445 | '</p><div id="copy"></div>' +
|
446 | 'Expected result: Copy image to new location and move file to different location.<br>Status box will show "Call to FileEntry.copyTo success:{some metadata}, Call to FileEntry.moveTo success:{some metadata}"' +
|
447 | '</p><div id="write"></div>' +
|
448 | 'Expected result: Write image to library.<br>Status box will show "Call to FileWriter.write success:{some metadata}, Call to FileWriter.truncate success:{some metadata}"' +
|
449 | '</p><div id="upload"></div>' +
|
450 | 'Expected result: Upload image to server.<br>Status box may print out progress. Once finished will show "upload complete"' +
|
451 | '</p><div id="draw_canvas"></div>' +
|
452 | 'Expected result: Display image using canvas.<br>Image will be displayed in status box under "canvas:"' +
|
453 | '</p><div id="remove"></div>' +
|
454 | 'Expected result: Remove image from library.<br>Status box will show "FileEntry.remove success:["OK"]';
|
455 |
|
456 |
|
457 |
|
458 | if (window.MSApp && window.MSApp.execUnsafeLocalFunction) {
|
459 | MSApp.execUnsafeLocalFunction(function() {
|
460 | contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
461 | });
|
462 | } else {
|
463 | contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
464 | }
|
465 |
|
466 | var elements = document.getElementsByClassName("testInputTag");
|
467 | var listener = function (e) {
|
468 | testInputTag(e.target);
|
469 | };
|
470 | for (var i = 0; i < elements.length; ++i) {
|
471 | var item = elements[i];
|
472 | item.addEventListener("change", listener, false);
|
473 | }
|
474 |
|
475 | createActionButton('Get picture', function () {
|
476 | getPicture();
|
477 | }, 'getpicture');
|
478 |
|
479 | createActionButton('Clear Status', function () {
|
480 | clearStatus();
|
481 | }, 'getpicture');
|
482 |
|
483 | createActionButton('Get File Metadata', function () {
|
484 | getFileInfo();
|
485 | }, 'metadata');
|
486 |
|
487 | createActionButton('Read with FileReader', function () {
|
488 | readFile();
|
489 | }, 'reader');
|
490 |
|
491 | createActionButton('Copy Image', function () {
|
492 | copyImage();
|
493 | }, 'copy');
|
494 |
|
495 | createActionButton('Write Image', function () {
|
496 | writeImage();
|
497 | }, 'write');
|
498 |
|
499 | createActionButton('Upload Image', function () {
|
500 | uploadImage();
|
501 | }, 'upload');
|
502 |
|
503 | createActionButton('Draw Using Canvas', function () {
|
504 | displayImageUsingCanvas();
|
505 | }, 'draw_canvas');
|
506 |
|
507 | createActionButton('Remove Image', function () {
|
508 | removeImage();
|
509 | }, 'remove');
|
510 | };
|