openEventsPlatformApp/plugins/cordova-plugin-camera/appium-tests/helpers/cameraHelper.js
plugins/cordova-plugin-camera/appium-tests/helpers/cameraHelper.jsRaw
1/*jshint node: true */
2/* global Q, resolveLocalFileSystemURL, Camera, cordova */
3/*
4 *
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 *
22*/
23
24'use strict';
25
26var cameraConstants = require('../../www/CameraConstants');
27
28function findKeyByValue(set, value) {
29 for (var k in set) {
30 if (set.hasOwnProperty(k)) {
31 if (set[k] == value) {
32 return k;
33 }
34 }
35 }
36 return undefined;
37}
38
39function getDescription(spec) {
40 var desc = '';
41
42 desc += 'sourceType: ' + findKeyByValue(cameraConstants.PictureSourceType, spec.options.sourceType);
43 desc += ', destinationType: ' + findKeyByValue(cameraConstants.DestinationType, spec.options.destinationType);
44 desc += ', encodingType: ' + findKeyByValue(cameraConstants.EncodingType, spec.options.encodingType);
45 desc += ', allowEdit: ' + spec.options.allowEdit.toString();
46 desc += ', correctOrientation: ' + spec.options.correctOrientation.toString();
47
48 return desc;
49}
50
51module.exports.generateSpecs = function (sourceTypes, destinationTypes, encodingTypes, allowEditOptions, correctOrientationOptions) {
52 var destinationType,
53 sourceType,
54 encodingType,
55 allowEdit,
56 correctOrientation,
57 specs = [],
58 id = 1;
59 for (destinationType in destinationTypes) {
60 if (destinationTypes.hasOwnProperty(destinationType)) {
61 for (sourceType in sourceTypes) {
62 if (sourceTypes.hasOwnProperty(sourceType)) {
63 for (encodingType in encodingTypes) {
64 if (encodingTypes.hasOwnProperty(encodingType)) {
65 for (allowEdit in allowEditOptions) {
66 if (allowEditOptions.hasOwnProperty(allowEdit)) {
67 for (correctOrientation in correctOrientationOptions) {
68 // if taking picture from photolibrary, don't vary 'correctOrientation' option
69 if ((sourceTypes[sourceType] === cameraConstants.PictureSourceType.PHOTOLIBRARY ||
70 sourceTypes[sourceType] === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) &&
71 correctOrientation === true) { continue; }
72 var spec = {
73 'id': id++,
74 'options': {
75 'destinationType': destinationTypes[destinationType],
76 'sourceType': sourceTypes[sourceType],
77 'encodingType': encodingTypes[encodingType],
78 'allowEdit': allowEditOptions[allowEdit],
79 'saveToPhotoAlbum': false,
80 'correctOrientation': correctOrientationOptions[correctOrientation]
81 }
82 };
83 spec.description = getDescription(spec);
84 specs.push(spec);
85 }
86 }
87 }
88 }
89 }
90 }
91 }
92 }
93 }
94 return specs;
95};
96
97// calls getPicture() and saves the result in promise
98// note that this function is executed in the context of tested app
99// and not in the context of tests
100module.exports.getPicture = function (opts, pid) {
101 if (navigator._appiumPromises[pid - 1]) {
102 navigator._appiumPromises[pid - 1] = null;
103 }
104 navigator._appiumPromises[pid] = Q.defer();
105 navigator.camera.getPicture(function (result) {
106 navigator._appiumPromises[pid].resolve(result);
107 }, function (err) {
108 navigator._appiumPromises[pid].reject(err);
109 }, opts);
110};
111
112// verifies taken picture when the promise is resolved,
113// calls a callback with 'OK' if everything is good,
114// calls a callback with 'ERROR: <error message>' if something is wrong
115// note that this function is executed in the context of tested app
116// and not in the context of tests
117module.exports.checkPicture = function (pid, options, cb) {
118 var isIos = cordova.platformId === "ios";
119 var isAndroid = cordova.platformId === "android";
120 // skip image type check if it's unmodified on Android:
121 // https://github.com/apache/cordova-plugin-camera/#android-quirks-1
122 var skipFileTypeCheckAndroid = isAndroid && options.quality === 100 &&
123 !options.targetWidth && !options.targetHeight &&
124 !options.correctOrientation;
125
126 // Skip image type check if destination is NATIVE_URI and source - device's photoalbum
127 // https://github.com/apache/cordova-plugin-camera/#ios-quirks-1
128 var skipFileTypeCheckiOS = isIos && options.destinationType === Camera.DestinationType.NATIVE_URI &&
129 (options.sourceType === Camera.PictureSourceType.PHOTOLIBRARY ||
130 options.sourceType === Camera.PictureSourceType.SAVEDPHOTOALBUM);
131
132 var skipFileTypeCheck = skipFileTypeCheckAndroid || skipFileTypeCheckiOS;
133
134 var desiredType = 'JPEG';
135 var mimeType = 'image/jpeg';
136 if (options.encodingType === Camera.EncodingType.PNG) {
137 desiredType = 'PNG';
138 mimeType = 'image/png';
139 }
140
141 function errorCallback(msg) {
142 if (msg.hasOwnProperty('message')) {
143 msg = msg.message;
144 }
145 cb('ERROR: ' + msg);
146 }
147
148 // verifies the image we get from plugin
149 function verifyResult(result) {
150 if (result.length === 0) {
151 errorCallback('The result is empty.');
152 return;
153 } else if (isIos && options.destinationType === Camera.DestinationType.NATIVE_URI && result.indexOf('assets-library:') !== 0) {
154 errorCallback('Expected "' + result.substring(0, 150) + '"to start with "assets-library:"');
155 return;
156 } else if (isIos && options.destinationType === Camera.DestinationType.FILE_URI && result.indexOf('file:') !== 0) {
157 errorCallback('Expected "' + result.substring(0, 150) + '"to start with "file:"');
158 return;
159 }
160
161 try {
162 window.atob(result);
163 // if we got here it is a base64 string (DATA_URL)
164 result = "data:" + mimeType + ";base64," + result;
165 } catch (e) {
166 // not DATA_URL
167 if (options.destinationType === Camera.DestinationType.DATA_URL) {
168 errorCallback('Expected ' + result.substring(0, 150) + 'not to be DATA_URL');
169 return;
170 }
171 }
172
173 try {
174 if (result.indexOf('file:') === 0 ||
175 result.indexOf('content:') === 0 ||
176 result.indexOf('assets-library:') === 0) {
177
178 if (!window.resolveLocalFileSystemURL) {
179 errorCallback('Cannot read file. Please install cordova-plugin-file to fix this.');
180 return;
181 }
182 resolveLocalFileSystemURL(result, function (entry) {
183 if (skipFileTypeCheck) {
184 displayFile(entry);
185 } else {
186 verifyFile(entry);
187 }
188 }, function (err) {
189 errorCallback(err);
190 });
191 } else {
192 displayImage(result);
193 }
194 } catch (e) {
195 errorCallback(e);
196 }
197 }
198
199 // verifies that the file type matches the requested type
200 function verifyFile(entry) {
201 try {
202 var reader = new FileReader();
203 reader.onloadend = function(e) {
204 var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
205 var header = '';
206 for(var i = 0; i < arr.length; i++) {
207 header += arr[i].toString(16);
208 }
209 var actualType = 'unknown';
210
211 switch (header) {
212 case "89504e47":
213 actualType = 'PNG';
214 break;
215 case 'ffd8ffe0':
216 case 'ffd8ffe1':
217 case 'ffd8ffe2':
218 actualType = 'JPEG';
219 break;
220 }
221
222 if (actualType === desiredType) {
223 displayFile(entry);
224 } else {
225 errorCallback('File type mismatch. Expected ' + desiredType + ', got ' + actualType);
226 }
227 };
228 reader.onerror = function (e) {
229 errorCallback(e);
230 };
231 entry.file(function (file) {
232 reader.readAsArrayBuffer(file);
233 }, function (e) {
234 errorCallback(e);
235 });
236 } catch (e) {
237 errorCallback(e);
238 }
239 }
240
241 // reads the file, then displays the image
242 function displayFile(entry) {
243 function onFileReceived(file) {
244 var reader = new FileReader();
245 reader.onerror = function (e) {
246 errorCallback(e);
247 };
248 reader.onloadend = function (evt) {
249 displayImage(evt.target.result);
250 };
251 reader.readAsDataURL(file);
252 }
253
254 entry.file(onFileReceived, function (e) {
255 errorCallback(e);
256 });
257 }
258
259 function displayImage(image) {
260 try {
261 var imgEl = document.getElementById('camera_test_image');
262 if (!imgEl) {
263 imgEl = document.createElement('img');
264 imgEl.id = 'camera_test_image';
265 document.body.appendChild(imgEl);
266 }
267 var timedOut = false;
268 var loadTimeout = setTimeout(function () {
269 timedOut = true;
270 imgEl.src = '';
271 errorCallback('The image did not load: ' + image.substring(0, 150));
272 }, 10000);
273 var done = function (status) {
274 if (!timedOut) {
275 clearTimeout(loadTimeout);
276 imgEl.src = '';
277 cb(status);
278 }
279 };
280 imgEl.onload = function () {
281 try {
282 // aspect ratio is preserved so only one dimension should match
283 if ((typeof options.targetWidth === 'number' && imgEl.naturalWidth !== options.targetWidth) &&
284 (typeof options.targetHeight === 'number' && imgEl.naturalHeight !== options.targetHeight))
285 {
286 done('ERROR: Wrong image size: ' + imgEl.naturalWidth + 'x' + imgEl.naturalHeight +
287 '. Requested size: ' + options.targetWidth + 'x' + options.targetHeight);
288 } else {
289 done('OK');
290 }
291 } catch (e) {
292 errorCallback(e);
293 }
294 };
295 imgEl.src = image;
296 } catch (e) {
297 errorCallback(e);
298 }
299 }
300
301 navigator._appiumPromises[pid].promise
302 .then(function (result) {
303 verifyResult(result);
304 })
305 .fail(function (e) {
306 errorCallback(e);
307 });
308};