openEventsPlatformApp/plugins/cordova-plugin-camera/appium-tests/ios/ios.spec.js
plugins/cordova-plugin-camera/appium-tests/ios/ios.spec.jsRaw
1/*jshint node: true, jasmine: true */
2
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// these tests are meant to be executed by Cordova Paramedic test runner
25// you can find it here: https://github.com/apache/cordova-paramedic/
26// it is not necessary to do a full CI setup to run these tests
27// just run "node cordova-paramedic/main.js --platform ios --plugin cordova-plugin-camera"
28
29'use strict';
30
31var wdHelper = global.WD_HELPER;
32var screenshotHelper = global.SCREENSHOT_HELPER;
33var isDevice = global.DEVICE;
34var cameraConstants = require('../../www/CameraConstants');
35var cameraHelper = require('../helpers/cameraHelper');
36
37var MINUTE = 60 * 1000;
38var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW_1';
39var PROMISE_PREFIX = 'appium_camera_promise_';
40var CONTEXT_NATIVE_APP = 'NATIVE_APP';
41
42describe('Camera tests iOS.', function () {
43 var driver;
44 var webviewContext = DEFAULT_WEBVIEW_CONTEXT;
45 // promise count to use in promise ID
46 var promiseCount = 0;
47 // going to set this to false if session is created successfully
48 var failedToStart = true;
49 // points out which UI automation to use
50 var isXCUI = false;
51 // spec counter to restart the session
52 var specsRun = 0;
53
54 function getNextPromiseId() {
55 promiseCount += 1;
56 return getCurrentPromiseId();
57 }
58
59 function getCurrentPromiseId() {
60 return PROMISE_PREFIX + promiseCount;
61 }
62
63 function gracefullyFail(error) {
64 fail(error);
65 return driver
66 .quit()
67 .then(function () {
68 return getDriver();
69 });
70 }
71
72 // generates test specs by combining all the specified options
73 // you can add more options to test more scenarios
74 function generateOptions() {
75 var sourceTypes = cameraConstants.PictureSourceType;
76 var destinationTypes = cameraConstants.DestinationType;
77 var encodingTypes = cameraConstants.EncodingType;
78 var allowEditOptions = [ true, false ];
79 var correctOrientationOptions = [ true, false ];
80
81 return cameraHelper.generateSpecs(sourceTypes, destinationTypes, encodingTypes, allowEditOptions, correctOrientationOptions);
82 }
83
84 function usePicture() {
85 return driver
86 .elementByXPath('//*[@label="Use"]')
87 .click()
88 .fail(function () {
89 if (isXCUI) {
90 return driver
91 .waitForElementByAccessibilityId('Choose', MINUTE / 3)
92 .click();
93 }
94 // For some reason "Choose" element is not clickable by standard Appium methods on iOS <= 9
95 return wdHelper.tapElementByXPath('//UIAButton[@label="Choose"]', driver);
96 });
97 }
98
99 function clickPhoto() {
100 if (isXCUI) {
101 // iOS >=10
102 return driver
103 .context(CONTEXT_NATIVE_APP)
104 .elementsByXPath('//XCUIElementTypeCell')
105 .then(function(photos) {
106 if (photos.length == 0) {
107 return driver
108 .sleep(0) // driver.source is not a function o.O
109 .source()
110 .then(function (src) {
111 console.log(src);
112 gracefullyFail('Couldn\'t find an image to click');
113 });
114 }
115 // intentionally clicking the second photo here
116 // the first one is not clickable for some reason
117 return photos[1].click();
118 });
119 }
120 // iOS <10
121 return driver
122 .elementByXPath('//UIACollectionCell')
123 .click();
124 }
125
126 function getPicture(options, cancelCamera, skipUiInteractions) {
127 var promiseId = getNextPromiseId();
128 if (!options) {
129 options = {};
130 }
131
132 return driver
133 .context(webviewContext)
134 .execute(cameraHelper.getPicture, [options, promiseId])
135 .context(CONTEXT_NATIVE_APP)
136 .then(function () {
137 if (skipUiInteractions) {
138 return;
139 }
140 if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.PHOTOLIBRARY) {
141 return driver
142 .waitForElementByAccessibilityId('Camera Roll', MINUTE / 2)
143 .click()
144 .then(function () {
145 return clickPhoto();
146 })
147 .then(function () {
148 if (!options.allowEdit) {
149 return driver;
150 }
151 return usePicture();
152 });
153 }
154 if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) {
155 return clickPhoto()
156 .then(function () {
157 if (!options.allowEdit) {
158 return driver;
159 }
160 return usePicture();
161 });
162 }
163 if (cancelCamera) {
164 return driver
165 .waitForElementByAccessibilityId('Cancel', MINUTE / 2)
166 .click();
167 }
168 return driver
169 .waitForElementByAccessibilityId('Take Picture', MINUTE / 2)
170 .click()
171 .waitForElementByAccessibilityId('Use Photo', MINUTE / 2)
172 .click();
173 })
174 .fail(fail);
175 }
176
177 // checks if the picture was successfully taken
178 // if shouldLoad is falsy, ensures that the error callback was called
179 function checkPicture(shouldLoad, options) {
180 if (!options) {
181 options = {};
182 }
183 return driver
184 .context(webviewContext)
185 .setAsyncScriptTimeout(MINUTE / 2)
186 .executeAsync(cameraHelper.checkPicture, [getCurrentPromiseId(), options])
187 .then(function (result) {
188 if (shouldLoad) {
189 if (result !== 'OK') {
190 fail(result);
191 }
192 } else if (result.indexOf('ERROR') === -1) {
193 throw 'Unexpected success callback with result: ' + result;
194 }
195 });
196 }
197
198 // takes a picture with the specified options
199 // and then verifies it
200 function runSpec(options, done, pending) {
201 if (options.sourceType === cameraConstants.PictureSourceType.CAMERA && !isDevice) {
202 pending('Camera is not available on iOS simulator');
203 }
204 checkSession(done);
205 specsRun += 1;
206 return driver
207 .then(function () {
208 return getPicture(options);
209 })
210 .then(function () {
211 return checkPicture(true, options);
212 })
213 .fail(gracefullyFail);
214 }
215
216 function getDriver() {
217 failedToStart = true;
218 driver = wdHelper.getDriver('iOS');
219 return wdHelper.getWebviewContext(driver)
220 .then(function(context) {
221 webviewContext = context;
222 return driver.context(webviewContext);
223 })
224 .then(function () {
225 return wdHelper.waitForDeviceReady(driver);
226 })
227 .then(function () {
228 return wdHelper.injectLibraries(driver);
229 })
230 .sessionCapabilities()
231 .then(function (caps) {
232 var platformVersion = parseFloat(caps.platformVersion);
233 isXCUI = platformVersion >= 10.0;
234 })
235 .then(function () {
236 var options = {
237 quality: 50,
238 allowEdit: false,
239 sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
240 saveToPhotoAlbum: false,
241 targetWidth: 210,
242 targetHeight: 210
243 };
244 return driver
245 .then(function () { return getPicture(options, false, true); })
246 .context(CONTEXT_NATIVE_APP)
247 .acceptAlert()
248 .then(function alertDismissed() {
249 // TODO: once we move to only XCUITest-based (which is force on you in either iOS 10+ or Xcode 8+)
250 // UI tests, we will have to:
251 // a) remove use of autoAcceptAlerts appium capability since it no longer functions in XCUITest
252 // b) can remove this entire then() clause, as we do not need to explicitly handle the acceptAlert
253 // failure callback, since we will be guaranteed to hit the permission dialog on startup.
254 }, function noAlert() {
255 // in case the contacts permission alert never showed up: no problem, don't freak out.
256 // This can happen if:
257 // a) The application-under-test already had photos permissions granted to it
258 // b) Appium's autoAcceptAlerts capability is provided (and functioning)
259 })
260 .elementByAccessibilityId('Cancel', 10000)
261 .click();
262 })
263 .then(function () {
264 failedToStart = false;
265 });
266 }
267
268 function checkSession(done) {
269 if (failedToStart) {
270 fail('Failed to start a session');
271 done();
272 }
273 }
274
275 it('camera.ui.util configure driver and start a session', function (done) {
276 getDriver()
277 .fail(fail)
278 .done(done);
279 }, 15 * MINUTE);
280
281 describe('Specs.', function () {
282 afterEach(function (done) {
283 if (specsRun >= 15) {
284 specsRun = 0;
285 // we need to restart the session regularly because for some reason
286 // when running against iOS 10 simulator on SauceLabs,
287 // Appium cannot handle more than ~20 specs at one session
288 // the error would be as follows:
289 // "Could not proxy command to remote server. Original error: Error: connect ECONNREFUSED 127.0.0.1:8100"
290 checkSession(done);
291 return driver
292 .quit()
293 .then(function () {
294 return getDriver();
295 })
296 .done(done);
297 } else {
298 done();
299 }
300 }, 15 * MINUTE);
301
302 // getPicture() with mediaType: VIDEO, sourceType: PHOTOLIBRARY
303 it('camera.ui.spec.1 Selecting only videos', function (done) {
304 checkSession(done);
305 specsRun += 1;
306 var options = { sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
307 mediaType: cameraConstants.MediaType.VIDEO };
308 driver
309 // skip ui unteractions
310 .then(function () { return getPicture(options, false, true); })
311 .waitForElementByXPath('//*[contains(@label,"Videos")]', MINUTE / 2)
312 .elementByAccessibilityId('Cancel')
313 .click()
314 .fail(gracefullyFail)
315 .done(done);
316 }, 7 * MINUTE);
317
318 // getPicture(), then dismiss
319 // wait for the error callback to be called
320 it('camera.ui.spec.2 Dismissing the camera', function (done) {
321 checkSession(done);
322 if (!isDevice) {
323 pending('Camera is not available on iOS simulator');
324 }
325 specsRun += 1;
326 var options = { sourceType: cameraConstants.PictureSourceType.CAMERA,
327 saveToPhotoAlbum: false };
328 driver
329 .then(function () {
330 return getPicture(options, true);
331 })
332 .then(function () {
333 return checkPicture(false);
334 })
335 .fail(gracefullyFail)
336 .done(done);
337 }, 7 * MINUTE);
338
339 it('camera.ui.spec.3 Verifying target image size, sourceType=CAMERA', function (done) {
340 var options = {
341 quality: 50,
342 allowEdit: false,
343 sourceType: cameraConstants.PictureSourceType.CAMERA,
344 saveToPhotoAlbum: false,
345 targetWidth: 210,
346 targetHeight: 210
347 };
348
349 runSpec(options, done, pending).done(done);
350 }, 7 * MINUTE);
351
352 it('camera.ui.spec.4 Verifying target image size, sourceType=SAVEDPHOTOALBUM', function (done) {
353 var options = {
354 quality: 50,
355 allowEdit: false,
356 sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
357 saveToPhotoAlbum: false,
358 targetWidth: 210,
359 targetHeight: 210
360 };
361
362 runSpec(options, done, pending).done(done);
363 }, 7 * MINUTE);
364
365 it('camera.ui.spec.5 Verifying target image size, sourceType=PHOTOLIBRARY', function (done) {
366 var options = {
367 quality: 50,
368 allowEdit: false,
369 sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
370 saveToPhotoAlbum: false,
371 targetWidth: 210,
372 targetHeight: 210
373 };
374
375 runSpec(options, done, pending).done(done);
376 }, 7 * MINUTE);
377
378 it('camera.ui.spec.6 Verifying target image size, sourceType=CAMERA, destinationType=FILE_URL', function (done) {
379 // remove this line if you don't mind the tests leaving a photo saved on device
380 pending('Cannot prevent iOS from saving the picture to photo library');
381
382 var options = {
383 quality: 50,
384 allowEdit: false,
385 sourceType: cameraConstants.PictureSourceType.CAMERA,
386 destinationType: cameraConstants.DestinationType.FILE_URL,
387 saveToPhotoAlbum: false,
388 targetWidth: 210,
389 targetHeight: 210
390 };
391
392 runSpec(options, done, pending).done(done);
393 }, 7 * MINUTE);
394
395 it('camera.ui.spec.7 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL', function (done) {
396 var options = {
397 quality: 50,
398 allowEdit: false,
399 sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
400 destinationType: cameraConstants.DestinationType.FILE_URL,
401 saveToPhotoAlbum: false,
402 targetWidth: 210,
403 targetHeight: 210
404 };
405
406 runSpec(options, done, pending).done(done);
407 }, 7 * MINUTE);
408
409 it('camera.ui.spec.8 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL', function (done) {
410 var options = {
411 quality: 50,
412 allowEdit: false,
413 sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
414 destinationType: cameraConstants.DestinationType.FILE_URL,
415 saveToPhotoAlbum: false,
416 targetWidth: 210,
417 targetHeight: 210
418 };
419
420 runSpec(options, done, pending).done(done);
421 }, 7 * MINUTE);
422
423 it('camera.ui.spec.9 Verifying target image size, sourceType=CAMERA, destinationType=FILE_URL, quality=100', function (done) {
424 // remove this line if you don't mind the tests leaving a photo saved on device
425 pending('Cannot prevent iOS from saving the picture to photo library');
426
427 var options = {
428 quality: 100,
429 allowEdit: false,
430 sourceType: cameraConstants.PictureSourceType.CAMERA,
431 destinationType: cameraConstants.DestinationType.FILE_URL,
432 saveToPhotoAlbum: false,
433 targetWidth: 305,
434 targetHeight: 305
435 };
436 runSpec(options, done, pending).done(done);
437 }, 7 * MINUTE);
438
439 it('camera.ui.spec.10 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL, quality=100', function (done) {
440 var options = {
441 quality: 100,
442 allowEdit: false,
443 sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
444 destinationType: cameraConstants.DestinationType.FILE_URL,
445 saveToPhotoAlbum: false,
446 targetWidth: 305,
447 targetHeight: 305
448 };
449
450 runSpec(options, done, pending).done(done);
451 }, 7 * MINUTE);
452
453 it('camera.ui.spec.11 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL, quality=100', function (done) {
454 var options = {
455 quality: 100,
456 allowEdit: false,
457 sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
458 destinationType: cameraConstants.DestinationType.FILE_URL,
459 saveToPhotoAlbum: false,
460 targetWidth: 305,
461 targetHeight: 305
462 };
463
464 runSpec(options, done, pending).done(done);
465 }, 7 * MINUTE);
466
467 // combine various options for getPicture()
468 generateOptions().forEach(function (spec) {
469 it('camera.ui.spec.12.' + spec.id + ' Combining options. ' + spec.description, function (done) {
470 // remove this check if you don't mind the tests leaving a photo saved on device
471 if (spec.options.sourceType === cameraConstants.PictureSourceType.CAMERA &&
472 spec.options.destinationType === cameraConstants.DestinationType.NATIVE_URI) {
473 pending('Skipping: cannot prevent iOS from saving the picture to photo library and cannot delete it. ' +
474 'For more info, see iOS quirks here: https://github.com/apache/cordova-plugin-camera#ios-quirks-1');
475 }
476
477 runSpec(spec.options, done, pending).done(done);
478 }, 7 * MINUTE);
479 });
480
481 });
482
483 it('camera.ui.util Destroy the session', function (done) {
484 checkSession(done);
485 driver
486 .quit()
487 .done(done);
488 }, 5 * MINUTE);
489});