UNPKG

23.2 kBJavaScriptView Raw
1/*
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20*/
21
22/* globals Camera, resolveLocalFileSystemURL, FileEntry, CameraPopoverOptions, FileTransfer, FileUploadOptions, LocalFileSystem, MSApp */
23/* jshint jasmine: true */
24
25exports.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
83exports.defineManualTests = function (contentEl, createActionButton) {
84 var pictureUrl = null;
85 var fileObj = null;
86 var fileEntry = null;
87 var pageStartTime = +new Date();
88
89 //default camera options
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 // if we got here it is a base64 string (DATA_URL)
118 url = "data:image/jpeg;base64," + url;
119 } catch (e) {
120 // not DATA_URL
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 // TODO: Fix resolveLocalFileSystemURI to work with native-uri.
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 // do nothing
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 // Reposition the popover if the orientation changes.
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 * Select image from library using a NATIVE_URI destination type
199 * This calls FileEntry.getMetadata, FileEntry.setMetadata, FileEntry.getParent, FileEntry.file, and FileReader.readAsDataURL.
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 // Test out onFileReceived when the file object was set via a native <input> elements.
224 if (fileObj) {
225 onFileReceived(fileObj);
226 } else {
227 fileEntry.file(onFileReceived, logCallback('FileEntry.file', false));
228 }
229 }
230
231 function getFileInfo() {
232 // Test FileEntry API here.
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 * Copy image from library using a NATIVE_URI destination type
241 * This calls FileEntry.copyTo and FileEntry.moveTo.
242 */
243 function copyImage() {
244 var onFileSystemReceived = function (fileSystem) {
245 var destDirEntry = fileSystem.root;
246 var origName = fileEntry.name;
247
248 // Test FileEntry API here.
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 //cleanup
253 //rename moved file back to original name so other tests can reference image
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 //remove copied file
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 * Write image to library using a NATIVE_URI destination type
275 * This calls FileEntry.createWriter, FileWriter.write, and FileWriter.truncate.
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 * Remove image from library using a NATIVE_URI destination type
309 * This calls FileEntry.remove.
310 */
311 function removeImage() {
312 fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
313 }
314
315 function testInputTag(inputEl) {
316 clearStatus();
317 // iOS 6 likes to dead-lock in the onchange context if you
318 // do any alerts or try to remote-debug.
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 /*jshint -W084 */
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 /*jshint +W084 */
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 // We need to wrap this code due to Windows security restrictions
457 // see http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx#differences for details
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};