Compare commits

..

1 Commits
2.4.0 ... 2.3.x

Author SHA1 Message Date
Shazron Abdullah
35b98bd0f0 CB-12236 - Fixed RELEASENOTES for cordova-plugin-camera 2016-12-12 11:05:32 -08:00
8 changed files with 151 additions and 400 deletions

View File

@@ -20,18 +20,6 @@
-->
# Release Notes
### 2.4.0 (Feb 28, 2017)
* [CB-12501](https://issues.apache.org/jira/browse/CB-12501) **Android**: Appium tests don't use `XPath` selectors anymore
* [CB-12469](https://issues.apache.org/jira/browse/CB-12469) Appium tests can now run on **iOS 10**
* [CB-12005](https://issues.apache.org/jira/browse/CB-12005) Changing the `getOrientation` method to return the defined enumerated `EXIF` instead of orientation in degrees for Consistency
* [CB-12368](https://issues.apache.org/jira/browse/CB-12368) Fix permission check on **Android**
* [CB-12353](https://issues.apache.org/jira/browse/CB-12353) Corrected merges usage in `plugin.xml`
* [CB-12369](https://issues.apache.org/jira/browse/CB-12369) Add plugin typings from `DefinitelyTyped`
* [CB-12363](https://issues.apache.org/jira/browse/CB-12363) Added build badges for **iOS 9.3** and **iOS 10.0**
* [CB-12312](https://issues.apache.org/jira/browse/CB-12312) [Appium] [Android] A few changes to the tests: - updated comments on how to run the tests. extra comments around functionality at certain points in the automation. - stub of a resolution checker on test startup - still need to figure out acceptable values. - moved session shutdown to an afterAll clause. - changed resolution determiner from using webview-based values to using the native windows dimensions - this helps as the webview values may be scaled down intentionally by manufacturers (via changing devicePixelRatio). furthermore, since the screen dimension automation is used purely for native UI automation, better to use the dimensions reported by the native context rather than the web context. - when finding elements by XPath, use multiple calls to avoid a Windows emulator + Android bug. Made this pattern consistent in the entire test.
* [CB-12236](https://issues.apache.org/jira/browse/CB-12236) - Fixed RELEASENOTES for cordova-plugin-camera
* [CB-12230](https://issues.apache.org/jira/browse/CB-12230) Removed Windows 8.1 build badges
### 2.3.1 (Dec 07, 2016)
* [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 2.3.1
* Fix missing license headers.

View File

@@ -21,12 +21,10 @@
*
*/
// these tests are meant to be executed by Cordova ParaMedic Appium runner
// you can find it here: https://github.com/apache/cordova-paramedic/
// these tests are meant to be executed by Cordova Medic Appium runner
// you can find it here: https://github.com/apache/cordova-medic/
// it is not necessary to do a full CI setup to run these tests
// Run:
// node cordova-paramedic/main.js --platform android --plugin cordova-plugin-camera --skipMainTests --target <emulator name>
// Please note only Android 5.1 and 4.4 are supported at this point.
// just run "node cordova-medic/medic/medic.js appium --platform android --plugins cordova-plugin-camera"
'use strict';
@@ -42,7 +40,6 @@ var DEFAULT_SCREEN_WIDTH = 360;
var DEFAULT_SCREEN_HEIGHT = 567;
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW';
var PROMISE_PREFIX = 'appium_camera_promise_';
var CONTEXT_NATIVE_APP = 'NATIVE_APP';
describe('Camera tests Android.', function () {
var driver;
@@ -59,8 +56,6 @@ describe('Camera tests Android.', function () {
var appiumSessionStarted = false;
// determine if camera is present on the device/emulator
var cameraAvailable = false;
// determine if emulator is within a range of acceptable resolutions able to run these tests
var isResolutionBad = true;
// a path to the image we add to the gallery before test run
var fillerImagePath;
@@ -73,9 +68,10 @@ describe('Camera tests Android.', function () {
return PROMISE_PREFIX + promiseCount;
}
function gracefullyFail(error) {
function saveScreenshotAndFail(error) {
fail(error);
return driver
return screenshotHelper
.saveScreenshot(driver)
.quit()
.then(function () {
return getDriver();
@@ -108,7 +104,7 @@ describe('Camera tests Android.', function () {
return driver
.context(webviewContext)
.execute(cameraHelper.getPicture, [options, promiseId])
.context(CONTEXT_NATIVE_APP)
.context('NATIVE_APP')
.then(function () {
if (skipUiInteractions) {
return;
@@ -125,7 +121,7 @@ describe('Camera tests Android.', function () {
y: Math.round(screenHeight / 4)
});
swipeRight
.press({x: 10, y: Math.round(screenHeight / 4)})
.press({x: 10, y: 150})
.wait(300)
.moveTo({x: Math.round(screenWidth - (screenWidth / 8)), y: 0})
.wait(1500)
@@ -138,12 +134,19 @@ describe('Camera tests Android.', function () {
.performTouchAction(tapTile);
}
return driver
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery");', 20000)
.waitForElementByXPath('//android.widget.TextView[@text="Gallery"]', 20000)
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.fail(function () {
// If the Gallery button is not present, swipe right to reveal the Gallery button!
return driver
.performTouchAction(swipeRight)
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery");', 20000)
.waitForElementByXPath('//android.widget.TextView[@text="Gallery"]', 20000)
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.elementByXPath('//android.widget.TextView[@text="Gallery"]');
})
.click()
// always wait before performing touchAction
@@ -152,9 +155,13 @@ describe('Camera tests Android.', function () {
}
// taking a picture from camera
return driver
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*shutter.*")', MINUTE / 2)
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]', MINUTE / 2)
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
.click()
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*done.*")', MINUTE / 2)
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]', MINUTE / 2)
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
.click();
})
.then(function () {
@@ -163,7 +170,7 @@ describe('Camera tests Android.', function () {
}
if (options.allowEdit) {
return driver
.waitForElementByAndroidUIAutomator('new UiSelector().text("Save")', MINUTE)
.waitForElementByXPath('//*[contains(@resource-id,\'save\')]', MINUTE)
.click();
}
})
@@ -196,19 +203,16 @@ describe('Camera tests Android.', function () {
// deletes the latest image from the gallery
function deleteImage() {
var holdTile = new wd.TouchAction();
holdTile
.press({x: Math.round(screenWidth / 4), y: Math.round(screenHeight / 5)})
.wait(1000)
.release();
holdTile.press({x: Math.round(screenWidth / 4), y: Math.round(screenHeight / 5)}).wait(1000).release();
return driver
// always wait before performing touchAction
.sleep(7000)
.performTouchAction(holdTile)
.elementByAndroidUIAutomator('new UiSelector().text("Delete")')
.elementByXPath('//android.widget.TextView[@text="Delete"]')
.then(function (element) {
return element
.click()
.elementByAndroidUIAutomator('new UiSelector().text("OK")')
.elementByXPath('//android.widget.Button[@text="OK"]')
.click();
}, function () {
// couldn't find Delete menu item. Possibly there is no image.
@@ -258,7 +262,7 @@ describe('Camera tests Android.', function () {
.then(spec);
});
})
.fail(gracefullyFail);
.fail(saveScreenshotAndFail);
}
// produces a generic spec function which
@@ -276,27 +280,18 @@ describe('Camera tests Android.', function () {
};
}
function checkSession(done, skipResolutionCheck) {
function checkSession(done) {
if (!appiumSessionStarted) {
fail('Failed to start a session ' + (lastFailureReason ? lastFailureReason : ''));
fail('Failed to start a session');
done();
}
if (!skipResolutionCheck && isResolutionBad) {
fail('The resolution of this target device is not within the appropriate range of width: blah-blah and height: bleh-bleh. The target\'s current resolution is: ' + isResolutionBad);
}
}
function checkCamera(pending) {
if (!cameraAvailable) {
pending('This test requires a functioning camera on the Android device/emulator, and this test suite\'s functional camera test failed on your target environment.');
pending('This test requires camera');
}
}
afterAll(function (done) {
checkSession(done);
driver
.quit()
.done(done);
}, MINUTE);
it('camera.ui.util configuring driver and starting a session', function (done) {
getDriver()
@@ -307,19 +302,18 @@ describe('Camera tests Android.', function () {
}, 10 * MINUTE);
it('camera.ui.util determine screen dimensions', function (done) {
checkSession(done, /*skipResolutionCheck?*/ true); // skip the resolution check here since we are about to find out in this spec!
checkSession(done);
driver
.context(CONTEXT_NATIVE_APP)
.getWindowSize()
.context(webviewContext)
.execute(function () {
return {
'width': screen.availWidth,
'height': screen.availHeight
};
}, [])
.then(function (size) {
screenWidth = Number(size.width);
screenHeight = Number(size.height);
isResolutionBad = false;
/*
TODO: what are acceptable resolution values?
need to check what the emulators used in CI return.
and also what local device definitions work and dont
*/
})
.done(done);
}, MINUTE);
@@ -372,12 +366,14 @@ describe('Camera tests Android.', function () {
.then(function () {
return getPicture(options, true);
})
.context(CONTEXT_NATIVE_APP)
.context('NATIVE_APP')
.then(function () {
// try to find "Gallery" menu item
// if there's none, the gallery should be already opened
return driver
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery")', 20000)
.waitForElementByXPath('//android.widget.TextView[@text="Gallery"]', 20000)
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.then(function (element) {
return element.click();
}, function () {
@@ -388,13 +384,13 @@ describe('Camera tests Android.', function () {
// if the gallery is opened on the videos page,
// there should be a "Choose video" caption
return driver
.elementByAndroidUIAutomator('new UiSelector().text("Choose video")')
.elementByXPath('//*[@text="Choose video"]')
.fail(function () {
throw 'Couldn\'t find "Choose video" element.';
});
})
.deviceKeyEvent(BACK_BUTTON)
.elementByAndroidUIAutomator('new UiSelector().text("Gallery")')
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.deviceKeyEvent(BACK_BUTTON)
.finally(function () {
return driver
@@ -441,8 +437,10 @@ describe('Camera tests Android.', function () {
.then(function () {
return getPicture(options, true);
})
.context(CONTEXT_NATIVE_APP)
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*cancel.*")', MINUTE / 2)
.context("NATIVE_APP")
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'cancel\')]', MINUTE / 2)
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'cancel\')]')
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'cancel\')]')
.click()
.then(function () {
return checkPicture(false);
@@ -468,11 +466,18 @@ describe('Camera tests Android.', function () {
.then(function () {
return getPicture(options, true);
})
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*shutter.*")', MINUTE / 2)
.context('NATIVE_APP')
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]', MINUTE / 2)
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
.click()
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*done.*")', MINUTE / 2)
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]', MINUTE / 2)
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
.click()
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*discard.*")', MINUTE / 2)
.waitForElementByXPath('//*[contains(@resource-id,\'discard\')]', MINUTE / 2)
.elementByXPath('//*[contains(@resource-id,\'discard\')]')
.elementByXPath('//*[contains(@resource-id,\'discard\')]')
.click()
.then(function () {
return checkPicture(false);
@@ -602,7 +607,7 @@ describe('Camera tests Android.', function () {
// delete exactly one latest picture
// this should be the picture we've taken in the first spec
driver
.context(CONTEXT_NATIVE_APP)
.context('NATIVE_APP')
.deviceKeyEvent(BACK_BUTTON)
.sleep(1000)
.deviceKeyEvent(BACK_BUTTON)
@@ -610,9 +615,9 @@ describe('Camera tests Android.', function () {
.deviceKeyEvent(BACK_BUTTON)
.elementById('Apps')
.click()
.elementByAndroidUIAutomator('new UiSelector().text("Gallery")')
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.click()
.elementByAndroidUIAutomator('new UiSelector().textContains("Pictures")')
.elementByXPath('//android.widget.TextView[contains(@text,"Pictures")]')
.click()
.then(deleteImage)
.deviceKeyEvent(BACK_BUTTON)
@@ -625,4 +630,10 @@ describe('Camera tests Android.', function () {
}, 3 * MINUTE);
});
it('camera.ui.util Destroy the session', function (done) {
checkSession(done);
driver
.quit()
.done(done);
}, 5 * MINUTE);
});

View File

@@ -21,10 +21,10 @@
*
*/
// these tests are meant to be executed by Cordova Paramedic test runner
// you can find it here: https://github.com/apache/cordova-paramedic/
// these tests are meant to be executed by Cordova Medic Appium runner
// you can find it here: https://github.com/apache/cordova-medic/
// it is not necessary to do a full CI setup to run these tests
// just run "node cordova-paramedic/main.js --platform ios --plugin cordova-plugin-camera"
// just run "node cordova-medic/medic/medic.js appium --platform android --plugins cordova-plugin-camera"
'use strict';
@@ -37,7 +37,6 @@ var cameraHelper = require('../helpers/cameraHelper');
var MINUTE = 60 * 1000;
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW_1';
var PROMISE_PREFIX = 'appium_camera_promise_';
var CONTEXT_NATIVE_APP = 'NATIVE_APP';
describe('Camera tests iOS.', function () {
var driver;
@@ -46,10 +45,6 @@ describe('Camera tests iOS.', function () {
var promiseCount = 0;
// going to set this to false if session is created successfully
var failedToStart = true;
// points out which UI automation to use
var isXCUI = false;
// spec counter to restart the session
var specsRun = 0;
function getNextPromiseId() {
promiseCount += 1;
@@ -60,9 +55,10 @@ describe('Camera tests iOS.', function () {
return PROMISE_PREFIX + promiseCount;
}
function gracefullyFail(error) {
function saveScreenshotAndFail(error) {
fail(error);
return driver
return screenshotHelper
.saveScreenshot(driver)
.quit()
.then(function () {
return getDriver();
@@ -86,43 +82,11 @@ describe('Camera tests iOS.', function () {
.elementByXPath('//*[@label="Use"]')
.click()
.fail(function () {
if (isXCUI) {
return driver
.waitForElementByAccessibilityId('Choose', MINUTE / 3)
.click();
}
// For some reason "Choose" element is not clickable by standard Appium methods on iOS <= 9
// For some reason "Choose" element is not clickable by standard Appium methods
return wdHelper.tapElementByXPath('//UIAButton[@label="Choose"]', driver);
});
}
function clickPhoto() {
if (isXCUI) {
// iOS >=10
return driver
.context(CONTEXT_NATIVE_APP)
.elementsByXPath('//XCUIElementTypeCell')
.then(function(photos) {
if (photos.length == 0) {
return driver
.sleep(0) // driver.source is not a function o.O
.source()
.then(function (src) {
console.log(src);
gracefullyFail('Couldn\'t find an image to click');
});
}
// intentionally clicking the second photo here
// the first one is not clickable for some reason
return photos[1].click();
});
}
// iOS <10
return driver
.elementByXPath('//UIACollectionCell')
.click();
}
function getPicture(options, cancelCamera, skipUiInteractions) {
var promiseId = getNextPromiseId();
if (!options) {
@@ -132,18 +96,17 @@ describe('Camera tests iOS.', function () {
return driver
.context(webviewContext)
.execute(cameraHelper.getPicture, [options, promiseId])
.context(CONTEXT_NATIVE_APP)
.context('NATIVE_APP')
.then(function () {
if (skipUiInteractions) {
return;
}
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.PHOTOLIBRARY) {
return driver
.waitForElementByAccessibilityId('Camera Roll', MINUTE / 2)
.waitForElementByXPath('//*[@label="Camera Roll"]', MINUTE / 2)
.click()
.elementByXPath('//UIACollectionCell')
.click()
.then(function () {
return clickPhoto();
})
.then(function () {
if (!options.allowEdit) {
return driver;
@@ -152,7 +115,9 @@ describe('Camera tests iOS.', function () {
});
}
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) {
return clickPhoto()
return driver
.waitForElementByXPath('//UIACollectionCell', MINUTE / 2)
.click()
.then(function () {
if (!options.allowEdit) {
return driver;
@@ -162,13 +127,15 @@ describe('Camera tests iOS.', function () {
}
if (cancelCamera) {
return driver
.waitForElementByAccessibilityId('Cancel', MINUTE / 2)
.waitForElementByXPath('//*[@label="Cancel"]', MINUTE / 2)
.elementByXPath('//*[@label="Cancel"]')
.elementByXPath('//*[@label="Cancel"]')
.click();
}
return driver
.waitForElementByAccessibilityId('Take Picture', MINUTE / 2)
.waitForElementByXPath('//*[@label="Take Picture"]', MINUTE / 2)
.click()
.waitForElementByAccessibilityId('Use Photo', MINUTE / 2)
.waitForElementByXPath('//*[@label="Use Photo"]', MINUTE / 2)
.click();
})
.fail(fail);
@@ -197,12 +164,7 @@ describe('Camera tests iOS.', function () {
// takes a picture with the specified options
// and then verifies it
function runSpec(options, done, pending) {
if (options.sourceType === cameraConstants.PictureSourceType.CAMERA && !isDevice) {
pending('Camera is not available on iOS simulator');
}
checkSession(done);
specsRun += 1;
function runSpec(options) {
return driver
.then(function () {
return getPicture(options);
@@ -210,11 +172,10 @@ describe('Camera tests iOS.', function () {
.then(function () {
return checkPicture(true, options);
})
.fail(gracefullyFail);
.fail(saveScreenshotAndFail);
}
function getDriver() {
failedToStart = true;
driver = wdHelper.getDriver('iOS');
return wdHelper.getWebviewContext(driver)
.then(function(context) {
@@ -226,42 +187,6 @@ describe('Camera tests iOS.', function () {
})
.then(function () {
return wdHelper.injectLibraries(driver);
})
.sessionCapabilities()
.then(function (caps) {
var platformVersion = parseFloat(caps.platformVersion);
isXCUI = platformVersion >= 10.0;
})
.then(function () {
var options = {
quality: 50,
allowEdit: false,
sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
saveToPhotoAlbum: false,
targetWidth: 210,
targetHeight: 210
};
return driver
.then(function () { return getPicture(options, false, true); })
.context(CONTEXT_NATIVE_APP)
.acceptAlert()
.then(function alertDismissed() {
// TODO: once we move to only XCUITest-based (which is force on you in either iOS 10+ or Xcode 8+)
// UI tests, we will have to:
// a) remove use of autoAcceptAlerts appium capability since it no longer functions in XCUITest
// b) can remove this entire then() clause, as we do not need to explicitly handle the acceptAlert
// failure callback, since we will be guaranteed to hit the permission dialog on startup.
}, function noAlert() {
// in case the contacts permission alert never showed up: no problem, don't freak out.
// This can happen if:
// a) The application-under-test already had photos permissions granted to it
// b) Appium's autoAcceptAlerts capability is provided (and functioning)
})
.elementByAccessibilityId('Cancel', 10000)
.click();
})
.then(function () {
failedToStart = false;
});
}
@@ -274,46 +199,27 @@ describe('Camera tests iOS.', function () {
it('camera.ui.util configure driver and start a session', function (done) {
getDriver()
.fail(fail)
.then(function () {
failedToStart = false;
}, fail)
.done(done);
}, 15 * MINUTE);
}, 10 * MINUTE);
describe('Specs.', function () {
afterEach(function (done) {
if (specsRun >= 15) {
specsRun = 0;
// we need to restart the session regularly because for some reason
// when running against iOS 10 simulator on SauceLabs,
// Appium cannot handle more than ~20 specs at one session
// the error would be as follows:
// "Could not proxy command to remote server. Original error: Error: connect ECONNREFUSED 127.0.0.1:8100"
checkSession(done);
return driver
.quit()
.then(function () {
return getDriver();
})
.done(done);
} else {
done();
}
}, 15 * MINUTE);
// getPicture() with mediaType: VIDEO, sourceType: PHOTOLIBRARY
it('camera.ui.spec.1 Selecting only videos', function (done) {
checkSession(done);
specsRun += 1;
var options = { sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
mediaType: cameraConstants.MediaType.VIDEO };
driver
// skip ui unteractions
.then(function () { return getPicture(options, false, true); })
.waitForElementByXPath('//*[contains(@label,"Videos")]', MINUTE / 2)
.elementByAccessibilityId('Cancel')
.elementByXPath('//*[@label="Cancel"]')
.click()
.fail(gracefullyFail)
.fail(saveScreenshotAndFail)
.done(done);
}, 7 * MINUTE);
}, 3 * MINUTE);
// getPicture(), then dismiss
// wait for the error callback to be called
@@ -322,7 +228,6 @@ describe('Camera tests iOS.', function () {
if (!isDevice) {
pending('Camera is not available on iOS simulator');
}
specsRun += 1;
var options = { sourceType: cameraConstants.PictureSourceType.CAMERA,
saveToPhotoAlbum: false };
driver
@@ -332,11 +237,15 @@ describe('Camera tests iOS.', function () {
.then(function () {
return checkPicture(false);
})
.fail(gracefullyFail)
.fail(saveScreenshotAndFail)
.done(done);
}, 7 * MINUTE);
}, 3 * MINUTE);
it('camera.ui.spec.3 Verifying target image size, sourceType=CAMERA', function (done) {
checkSession(done);
if (!isDevice) {
pending('Camera is not available on iOS simulator');
}
var options = {
quality: 50,
allowEdit: false,
@@ -346,10 +255,11 @@ describe('Camera tests iOS.', function () {
targetHeight: 210
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
it('camera.ui.spec.4 Verifying target image size, sourceType=SAVEDPHOTOALBUM', function (done) {
checkSession(done);
var options = {
quality: 50,
allowEdit: false,
@@ -359,10 +269,11 @@ describe('Camera tests iOS.', function () {
targetHeight: 210
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
it('camera.ui.spec.5 Verifying target image size, sourceType=PHOTOLIBRARY', function (done) {
checkSession(done);
var options = {
quality: 50,
allowEdit: false,
@@ -372,13 +283,17 @@ describe('Camera tests iOS.', function () {
targetHeight: 210
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
it('camera.ui.spec.6 Verifying target image size, sourceType=CAMERA, destinationType=FILE_URL', function (done) {
// remove this line if you don't mind the tests leaving a photo saved on device
pending('Cannot prevent iOS from saving the picture to photo library');
checkSession(done);
if (!isDevice) {
pending('Camera is not available on iOS simulator');
}
var options = {
quality: 50,
allowEdit: false,
@@ -389,10 +304,11 @@ describe('Camera tests iOS.', function () {
targetHeight: 210
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
it('camera.ui.spec.7 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL', function (done) {
checkSession(done);
var options = {
quality: 50,
allowEdit: false,
@@ -403,10 +319,11 @@ describe('Camera tests iOS.', function () {
targetHeight: 210
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
it('camera.ui.spec.8 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL', function (done) {
checkSession(done);
var options = {
quality: 50,
allowEdit: false,
@@ -417,13 +334,17 @@ describe('Camera tests iOS.', function () {
targetHeight: 210
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
it('camera.ui.spec.9 Verifying target image size, sourceType=CAMERA, destinationType=FILE_URL, quality=100', function (done) {
// remove this line if you don't mind the tests leaving a photo saved on device
pending('Cannot prevent iOS from saving the picture to photo library');
checkSession(done);
if (!isDevice) {
pending('Camera is not available on iOS simulator');
}
var options = {
quality: 100,
allowEdit: false,
@@ -433,10 +354,11 @@ describe('Camera tests iOS.', function () {
targetWidth: 305,
targetHeight: 305
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
it('camera.ui.spec.10 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL, quality=100', function (done) {
checkSession(done);
var options = {
quality: 100,
allowEdit: false,
@@ -447,10 +369,11 @@ describe('Camera tests iOS.', function () {
targetHeight: 305
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
it('camera.ui.spec.11 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL, quality=100', function (done) {
checkSession(done);
var options = {
quality: 100,
allowEdit: false,
@@ -461,12 +384,17 @@ describe('Camera tests iOS.', function () {
targetHeight: 305
};
runSpec(options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(options).done(done);
}, 3 * MINUTE);
// combine various options for getPicture()
generateOptions().forEach(function (spec) {
it('camera.ui.spec.12.' + spec.id + ' Combining options. ' + spec.description, function (done) {
checkSession(done);
if (!isDevice && spec.options.sourceType === cameraConstants.PictureSourceType.CAMERA) {
pending('Camera is not available on iOS simulator');
}
// remove this check if you don't mind the tests leaving a photo saved on device
if (spec.options.sourceType === cameraConstants.PictureSourceType.CAMERA &&
spec.options.destinationType === cameraConstants.DestinationType.NATIVE_URI) {
@@ -474,8 +402,8 @@ describe('Camera tests iOS.', function () {
'For more info, see iOS quirks here: https://github.com/apache/cordova-plugin-camera#ios-quirks-1');
}
runSpec(spec.options, done, pending).done(done);
}, 7 * MINUTE);
runSpec(spec.options).done(done);
}, 3 * MINUTE);
});
});

View File

@@ -1,8 +1,7 @@
{
"name": "cordova-plugin-camera",
"version": "2.4.0",
"version": "2.3.1",
"description": "Cordova Camera Plugin",
"types": "./types/index.d.ts",
"cordova": {
"id": "cordova-plugin-camera",
"platforms": [

View File

@@ -22,7 +22,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:rim="http://www.blackberry.com/ns/widgets"
id="cordova-plugin-camera"
version="2.4.0">
version="2.3.1">
<name>Camera</name>
<description>Cordova Camera Plugin</description>
<license>Apache 2.0</license>
@@ -249,7 +249,7 @@
<clobbers target="CameraPopoverHandle" />
</js-module>
<js-module src="src/windows/CameraProxy.js" name="CameraProxy">
<runs />
<merges target="" />
</js-module>
</platform>
@@ -276,8 +276,10 @@
<clobbers target="CameraPopoverHandle" />
</js-module>
<js-module src="src/windows/CameraProxy.js" name="CameraProxy">
<runs />
<merges target="" />
</js-module>
</platform>
</plugin>

View File

@@ -185,7 +185,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
}
else if ((this.srcType == PHOTOLIBRARY) || (this.srcType == SAVEDPHOTOALBUM)) {
// FIXME: Stop always requesting the permission
if(!PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
if(!PermissionHelper.hasPermission(this, permissions[0])) {
PermissionHelper.requestPermission(this, SAVE_TO_ALBUM_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
} else {
this.getImage(this.srcType, destType, encodingType);
@@ -577,9 +577,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
if (this.encodingType == JPEG) {
String exifPath;
exifPath = uri.getPath();
//We just finished rotating it by an arbitrary orientation, just make sure it's normal
if(rotate != ExifInterface.ORIENTATION_NORMAL)
exif.resetOrientation();
exif.createOutFile(exifPath);
exif.writeExifData();
}

View File

@@ -22,10 +22,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:rim="http://www.blackberry.com/ns/widgets"
id="cordova-plugin-camera-tests"
version="2.4.0">
version="2.3.1">
<name>Cordova Camera Plugin Tests</name>
<license>Apache 2.0</license>
<dependency id="cordova-plugin-file" version=">=2.0.0" />
<js-module src="tests.js" name="tests">

174
types/index.d.ts vendored
View File

@@ -1,174 +0,0 @@
// Type definitions for Apache Cordova Camera plugin
// Project: https://github.com/apache/cordova-plugin-camera
// Definitions by: Microsoft Open Technologies Inc <http://msopentech.com>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
//
// Copyright (c) Microsoft Open Technologies Inc
// Licensed under the MIT license.
interface Navigator {
/**
* This plugin provides an API for taking pictures and for choosing images from the system's image library.
*/
camera: Camera;
}
/**
* This plugin provides an API for taking pictures and for choosing images from the system's image library.
*/
interface Camera {
/**
* Removes intermediate photos taken by the camera from temporary storage.
* @param onSuccess Success callback, that called when cleanup succeeds.
* @param onError Error callback, that get an error message.
*/
cleanup(
onSuccess: () => void,
onError: (message: string) => void): void;
/**
* Takes a photo using the camera, or retrieves a photo from the device's image gallery.
* @param cameraSuccess Success callback, that get the image
* as a base64-encoded String, or as the URI for the image file.
* @param cameraError Error callback, that get an error message.
* @param cameraOptions Optional parameters to customize the camera settings.
*/
getPicture(
cameraSuccess: (data: string) => void,
cameraError: (message: string) => void,
cameraOptions?: CameraOptions): void;
// Next will work only on iOS
//getPicture(
// cameraSuccess: (data: string) => void,
// cameraError: (message: string) => void,
// cameraOptions?: CameraOptions): CameraPopoverHandle;
}
interface CameraOptions {
/** Picture quality in range 0-100. Default is 50 */
quality?: number;
/**
* Choose the format of the return value.
* Defined in navigator.camera.DestinationType. Default is FILE_URI.
* DATA_URL : 0, Return image as base64-encoded string
* FILE_URI : 1, Return image file URI
* NATIVE_URI : 2 Return image native URI
* (e.g., assets-library:// on iOS or content:// on Android)
*/
destinationType?: number;
/**
* Set the source of the picture.
* Defined in navigator.camera.PictureSourceType. Default is CAMERA.
* PHOTOLIBRARY : 0,
* CAMERA : 1,
* SAVEDPHOTOALBUM : 2
*/
sourceType?: number;
/** Allow simple editing of image before selection. */
allowEdit?: boolean;
/**
* Choose the returned image file's encoding.
* Defined in navigator.camera.EncodingType. Default is JPEG
* JPEG : 0 Return JPEG encoded image
* PNG : 1 Return PNG encoded image
*/
encodingType?: number;
/**
* Width in pixels to scale image. Must be used with targetHeight.
* Aspect ratio remains constant.
*/
targetWidth?: number;
/**
* Height in pixels to scale image. Must be used with targetWidth.
* Aspect ratio remains constant.
*/
targetHeight?: number;
/**
* Set the type of media to select from. Only works when PictureSourceType
* is PHOTOLIBRARY or SAVEDPHOTOALBUM. Defined in nagivator.camera.MediaType
* PICTURE: 0 allow selection of still pictures only. DEFAULT.
* Will return format specified via DestinationType
* VIDEO: 1 allow selection of video only, WILL ALWAYS RETURN FILE_URI
* ALLMEDIA : 2 allow selection from all media types
*/
mediaType?: number;
/** Rotate the image to correct for the orientation of the device during capture. */
correctOrientation?: boolean;
/** Save the image to the photo album on the device after capture. */
saveToPhotoAlbum?: boolean;
/**
* Choose the camera to use (front- or back-facing).
* Defined in navigator.camera.Direction. Default is BACK.
* FRONT: 0
* BACK: 1
*/
cameraDirection?: number;
/** iOS-only options that specify popover location in iPad. Defined in CameraPopoverOptions. */
popoverOptions?: CameraPopoverOptions;
}
/**
* A handle to the popover dialog created by navigator.camera.getPicture. Used on iOS only.
*/
interface CameraPopoverHandle {
/**
* Set the position of the popover.
* @param popoverOptions the CameraPopoverOptions that specify the new position.
*/
setPosition(popoverOptions: CameraPopoverOptions): void;
}
/**
* iOS-only parameters that specify the anchor element location and arrow direction
* of the popover when selecting images from an iPad's library or album.
*/
interface CameraPopoverOptions {
x: number;
y: number;
width: number;
height: number;
/**
* Direction the arrow on the popover should point. Defined in Camera.PopoverArrowDirection
* Matches iOS UIPopoverArrowDirection constants.
* ARROW_UP : 1,
* ARROW_DOWN : 2,
* ARROW_LEFT : 4,
* ARROW_RIGHT : 8,
* ARROW_ANY : 15
*/
arrowDir : number;
}
declare var Camera: {
// Camera constants, defined in Camera plugin
DestinationType: {
DATA_URL: number;
FILE_URI: number;
NATIVE_URI: number
}
Direction: {
BACK: number;
FRONT: number;
}
EncodingType: {
JPEG: number;
PNG: number;
}
MediaType: {
PICTURE: number;
VIDEO: number;
ALLMEDIA: number;
}
PictureSourceType: {
PHOTOLIBRARY: number;
CAMERA: number;
SAVEDPHOTOALBUM: number;
}
// Used only on iOS
PopoverArrowDirection: {
ARROW_UP: number;
ARROW_DOWN: number;
ARROW_LEFT: number;
ARROW_RIGHT: number;
ARROW_ANY: number;
}
};