arnaucube
arnaucube/openEventsPlatformAppPublic Self-hosted repository
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});