Modify background permissions to be an explicit second step.
This commit is contained in:
parent
389075e83e
commit
b36bad80b5
11 changed files with 205 additions and 104 deletions
|
|
@ -35,8 +35,8 @@ class TogaLocationDelegate(NSObject):
|
|||
@objc_method
|
||||
def locationManagerDidChangeAuthorization_(self, manager) -> None:
|
||||
while self.impl.permission_requests:
|
||||
future = self.impl.permission_requests.pop()
|
||||
future.set_result(self.impl.has_permission())
|
||||
future, permission = self.impl.permission_requests.pop()
|
||||
future.set_result(permission())
|
||||
|
||||
@objc_method
|
||||
def locationManager_didUpdateLocations_(self, manager, locations) -> None:
|
||||
|
|
@ -73,8 +73,8 @@ class Geolocation:
|
|||
|
||||
def has_permission(self):
|
||||
return self.native.authorizationStatus in {
|
||||
CLAuthorizationStatus.AuthorizedAlways.value,
|
||||
CLAuthorizationStatus.AuthorizedWhenInUse.value,
|
||||
CLAuthorizationStatus.AuthorizedAlways.value,
|
||||
}
|
||||
|
||||
def has_background_permission(self):
|
||||
|
|
@ -84,11 +84,11 @@ class Geolocation:
|
|||
)
|
||||
|
||||
def request_permission(self, future):
|
||||
self.permission_requests.append(future)
|
||||
self.permission_requests.append((future, self.has_permission))
|
||||
self.native.requestWhenInUseAuthorization()
|
||||
|
||||
def request_background_permission(self, future):
|
||||
self.permission_requests.append(future)
|
||||
self.permission_requests.append((future, self.has_background_permission))
|
||||
self.native.requestAlwaysAuthorization()
|
||||
|
||||
def current_location(self, result):
|
||||
|
|
|
|||
|
|
@ -25,17 +25,21 @@ class GeolocationProbe(AppProbe):
|
|||
# be granted if requested but, has not been granted *yet*. Unless primed,
|
||||
# permissions will be denied.
|
||||
self._mock_permission = None
|
||||
self._mock_background_permission = None
|
||||
|
||||
# Mock CLLocationManager
|
||||
self._mock_location_manager = Mock()
|
||||
|
||||
# Mock the CLLocationManager.authorizationStatus property
|
||||
def _mock_auth_status():
|
||||
return {
|
||||
2: CLAuthorizationStatus.AuthorizedAlways.value,
|
||||
1: CLAuthorizationStatus.AuthorizedWhenInUse.value,
|
||||
0: CLAuthorizationStatus.Denied.value,
|
||||
}.get(self._mock_permission, CLAuthorizationStatus.NotDetermined.value)
|
||||
if self._mock_background_permission == 1:
|
||||
return CLAuthorizationStatus.AuthorizedAlways.value
|
||||
elif self._mock_permission == 1:
|
||||
return CLAuthorizationStatus.AuthorizedWhenInUse.value
|
||||
elif self._mock_permission == 0:
|
||||
return CLAuthorizationStatus.Denied.value
|
||||
else:
|
||||
return CLAuthorizationStatus.NotDetermined.value
|
||||
|
||||
type(self._mock_location_manager).authorizationStatus = PropertyMock(
|
||||
side_effect=_mock_auth_status
|
||||
|
|
@ -54,8 +58,10 @@ class GeolocationProbe(AppProbe):
|
|||
)
|
||||
|
||||
def _mock_request_always():
|
||||
if self._mock_permission == -2:
|
||||
self._mock_permission = abs(self._mock_permission)
|
||||
if self._mock_background_permission is None:
|
||||
self._mock_background_permission = 0
|
||||
else:
|
||||
self._mock_background_permission = abs(self._mock_background_permission)
|
||||
|
||||
# Trigger delegate handling for permission change
|
||||
self.app.geolocation._impl.delegate.locationManagerDidChangeAuthorization(
|
||||
|
|
@ -104,13 +110,13 @@ class GeolocationProbe(AppProbe):
|
|||
self._mock_permission = -1
|
||||
|
||||
def grant_background_permission(self):
|
||||
self._mock_permission = -2
|
||||
self._mock_background_permission = -1
|
||||
|
||||
def allow_permission(self):
|
||||
self._mock_permission = 1
|
||||
|
||||
def allow_background_permission(self):
|
||||
self._mock_permission = 2
|
||||
self._mock_background_permission = 1
|
||||
|
||||
def reject_permission(self):
|
||||
self._mock_permission = 0
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ class Geolocation:
|
|||
If permission has already been granted, this will return without prompting the
|
||||
user.
|
||||
|
||||
This method will only grant permission to access geolocation services while the
|
||||
app is in the foreground. If you want your application to have permission to
|
||||
track location while the app is in the background, you must call this method,
|
||||
then make an *additional* permission request for background permissions using
|
||||
:any:`GeoLocation.request_background_permission()`.
|
||||
|
||||
**This is an asynchronous method**. If you invoke this method in synchronous
|
||||
context, it will start the process of requesting permissions, but will return
|
||||
*immediately*. The return value can be awaited in an asynchronous context, but
|
||||
|
|
@ -94,6 +100,11 @@ class Geolocation:
|
|||
If permission has already been granted, this will return without prompting the
|
||||
user.
|
||||
|
||||
Before requesting background permission, you must first request and receive
|
||||
foreground geolocation permission using :any:`Geolocation.request_permission`.
|
||||
If you ask for background permission before receiving foreground geolocation
|
||||
permission, a :any:`PermissionError` will be raised.
|
||||
|
||||
**This is an asynchronous method**. If you invoke this method in synchronous
|
||||
context, it will start the process of requesting permissions, but will return
|
||||
*immediately*. The return value can be awaited in an asynchronous context, but
|
||||
|
|
@ -102,10 +113,18 @@ class Geolocation:
|
|||
:returns: An asynchronous result; when awaited, returns True if the app has
|
||||
permission to capture the user's a geolocation while running in the
|
||||
background; False otherwise.
|
||||
:raises PermissionError: If the app has not already requested and received
|
||||
permission to use geolocation services.
|
||||
"""
|
||||
result = PermissionResult(None)
|
||||
|
||||
if has_background_permission := self.has_background_permission:
|
||||
if not self.has_permission:
|
||||
result.set_exception(
|
||||
PermissionError(
|
||||
"Cannot ask for background geolocation permission "
|
||||
"before confirming foreground geolocation permission."
|
||||
)
|
||||
)
|
||||
elif has_background_permission := self.has_background_permission:
|
||||
result.set_result(has_background_permission)
|
||||
else:
|
||||
self._impl.request_background_permission(result)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import contextlib
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
|
@ -77,27 +78,46 @@ def test_request_permission_sync(app):
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"initial, should_request, has_permission",
|
||||
"foreground, initial, raise_error, should_request, has_background_permission",
|
||||
[
|
||||
(-2, True, True),
|
||||
(-1, True, False),
|
||||
(0, True, False),
|
||||
(1, True, False),
|
||||
(2, False, True),
|
||||
(-1, -1, True, False, False),
|
||||
(0, -1, True, False, False),
|
||||
(1, -1, False, True, True),
|
||||
(-1, 0, True, False, False),
|
||||
(0, 0, True, False, False),
|
||||
(1, 0, False, True, False),
|
||||
# -1, 1 can't happen; background can't be approved if foreground isn't confirmed
|
||||
# 0, 1 can't happen; background can't be approved if foreground was rejected
|
||||
(1, 1, False, False, True),
|
||||
],
|
||||
)
|
||||
def test_request_background_permission(app, initial, should_request, has_permission):
|
||||
def test_request_background_permission(
|
||||
app, foreground, initial, raise_error, should_request, has_background_permission
|
||||
):
|
||||
"""An app can request background permission to use geolocation."""
|
||||
# The geolocation instance round-trips the app instance
|
||||
assert app.geolocation.app == app
|
||||
|
||||
# Set initial permission
|
||||
app.geolocation._impl._has_permission = initial
|
||||
# Set initial permissions
|
||||
app.geolocation._impl._has_permission = foreground
|
||||
app.geolocation._impl._has_background_permission = initial
|
||||
|
||||
assert (
|
||||
app.loop.run_until_complete(app.geolocation.request_background_permission())
|
||||
== has_permission
|
||||
)
|
||||
if raise_error:
|
||||
error_context = pytest.raises(
|
||||
PermissionError,
|
||||
match=(
|
||||
r"Cannot ask for background geolocation permission "
|
||||
r"before confirming foreground geolocation permission\."
|
||||
),
|
||||
)
|
||||
else:
|
||||
error_context = contextlib.nullcontext()
|
||||
|
||||
with error_context:
|
||||
assert (
|
||||
app.loop.run_until_complete(app.geolocation.request_background_permission())
|
||||
== has_background_permission
|
||||
)
|
||||
|
||||
if should_request:
|
||||
assert_action_not_performed(app.geolocation, "request permission")
|
||||
|
|
@ -107,13 +127,14 @@ def test_request_background_permission(app, initial, should_request, has_permiss
|
|||
assert_action_not_performed(app.geolocation, "request background permission")
|
||||
|
||||
# As a result of requesting, geolocation permission is as expected
|
||||
assert app.geolocation.has_background_permission == has_permission
|
||||
assert app.geolocation.has_background_permission == has_background_permission
|
||||
|
||||
|
||||
def test_request_background_permission_sync(app):
|
||||
"""An app can synchronously request background permission to use geolocation."""
|
||||
# Set initial permission
|
||||
app.geolocation._impl._has_permission = -2
|
||||
app.geolocation._impl._has_permission = 1
|
||||
app.geolocation._impl._has_background_permission = -1
|
||||
|
||||
result = app.geolocation.request_background_permission()
|
||||
|
||||
|
|
|
|||
|
|
@ -37,13 +37,6 @@ permission APIs are paired with the specific actions performed on those APIs - t
|
|||
to take a photo, you require :any:`Camera.has_permission`, which you can request using
|
||||
:any:`Camera.request_permission()`.
|
||||
|
||||
The calls to request permissions *can* be invoked from a synchronous context (i.e., a
|
||||
non ``async`` method); however, they are non-blocking when used in this way. Invoking a
|
||||
method like :any:`Camera.request_permission()` will start the process of requesting
|
||||
permission, but will return *immediately*, without waiting for the user's response. This
|
||||
allows an app to *request* permissions as part of the startup process, prior to using
|
||||
the camera APIs, without blocking the rest of app startup.
|
||||
|
||||
Toga will confirm whether the app has been granted permission to use the camera before
|
||||
invoking any camera API. If permission has not yet been granted, the platform *may*
|
||||
request access at the time of first camera access; however, this is not guaranteed to be
|
||||
|
|
|
|||
|
|
@ -36,17 +36,14 @@ inside an asynchronous handler:
|
|||
All platforms require some form of permission to access the geolocation service. To
|
||||
confirm if you have permission to use the geolocation service while the app is running,
|
||||
you can call :any:`Geolocation.has_permission`; you can request to permission using
|
||||
:any:`Geolocation.request_permission()`. To confirm if you have permission to use
|
||||
geolocation while the app is in the background, you can call
|
||||
:any:`Geolocation.has_background_permission`; you can request to permission using
|
||||
:any:`Geolocation.request_background_permission()`
|
||||
:any:`Geolocation.request_permission()`.
|
||||
|
||||
The calls to request permissions *can* be invoked from a synchronous context (i.e., a
|
||||
non ``async`` method); however, they are non-blocking when used in this way. Invoking a
|
||||
method like :any:`Geolocation.request_permission()` will start the process of requesting
|
||||
permission, but will return *immediately*, without waiting for the user's response. This
|
||||
allows an app to *request* permissions as part of the startup process, prior to using
|
||||
the geolocation APIs, without blocking the rest of app startup.
|
||||
If you wish to track the location of the user while the app is in the background, you
|
||||
must make a separate request for background location permissions using
|
||||
:meth:`~toga.hardware.Geolocation.request_background_permission()` . This request must
|
||||
be made *after* foreground permissions have been requested and confirmed. To confirm if
|
||||
you have permission to use geolocation while the app is in the background, you can call
|
||||
:any:`Geolocation.has_background_permission`.
|
||||
|
||||
Toga will confirm whether the app has been granted permission to use geolocation
|
||||
services before invoking any geolocation API. If permission has not yet been granted, or
|
||||
|
|
@ -97,17 +94,9 @@ Notes
|
|||
* On macOS, there is no distinction between "background" permissions and "while-running"
|
||||
permissions.
|
||||
|
||||
* On iOS, requesting permission to track location in the background will always require
|
||||
2 interactions from the user - an initial request to use geolocation while the app is
|
||||
running, then a second request to use location in the background. If you call
|
||||
:meth:`~toga.hardware.Geolocation.request_background_permission()` before *any*
|
||||
permissions have been confirmed, the user will be asked immediately for geolocation
|
||||
permissions while the app is running; the request for background tracking will be
|
||||
deferred until the first attempt to use location in the background, or a second call
|
||||
to :meth:`~toga.hardware.Geolocation.request_background_permission()`. Background
|
||||
location tracking will not be permitted unless the user allows geolocation "always"
|
||||
while the app is running. If they only allow "once off" permission while the app is
|
||||
running, requests for background processing will be ignored.
|
||||
* On iOS, if the user has provided "once off" permission for foreground location
|
||||
tracking, requests for background location permission will be rejected.
|
||||
|
||||
|
||||
Reference
|
||||
---------
|
||||
|
|
|
|||
|
|
@ -9,12 +9,11 @@ class Geolocation(LoggedObject):
|
|||
def __init__(self, interface):
|
||||
self.interface = interface
|
||||
|
||||
# -2: background permission *could* be granted, but hasn't been
|
||||
# -1: permission *could* be granted, but hasn't been
|
||||
# 0: permission has been denied, or can't be granted
|
||||
# 1: permission has been granted
|
||||
# 2: background permission has been granted
|
||||
self._has_permission = -1
|
||||
self._has_background_permission = -1
|
||||
self._location = LatLng(10.0, 20.0)
|
||||
self._altitude = 0
|
||||
|
||||
|
|
@ -31,7 +30,7 @@ class Geolocation(LoggedObject):
|
|||
|
||||
def has_background_permission(self):
|
||||
self._action("has background permission")
|
||||
return self._has_permission > 1
|
||||
return self._has_background_permission > 0
|
||||
|
||||
def request_permission(self, future):
|
||||
self._action("request permission")
|
||||
|
|
@ -40,8 +39,8 @@ class Geolocation(LoggedObject):
|
|||
|
||||
def request_background_permission(self, future):
|
||||
self._action("request background permission")
|
||||
self._has_permission = abs(self._has_permission)
|
||||
future.set_result(self._has_permission > 1)
|
||||
self._has_background_permission = abs(self._has_background_permission)
|
||||
future.set_result(self._has_background_permission > 0)
|
||||
|
||||
def current_location(self, result):
|
||||
location, altitude = self._next_location()
|
||||
|
|
|
|||
|
|
@ -5,20 +5,6 @@ from toga.style import Pack
|
|||
|
||||
class ExampleHardwareApp(toga.App):
|
||||
def startup(self):
|
||||
try:
|
||||
# This will provide a prompt for camera permissions at startup.
|
||||
# If permission is denied, the app will continue.
|
||||
self.camera.request_permission()
|
||||
except NotImplementedError:
|
||||
print("The Camera API is not implemented on this platform")
|
||||
|
||||
try:
|
||||
# This will provide a prompt for camera permissions at startup.
|
||||
# If permission is denied, the app will continue.
|
||||
self.geolocation.request_background_permission()
|
||||
except NotImplementedError:
|
||||
print("The Geolocation API is not implemented on this platform")
|
||||
|
||||
#############################################################
|
||||
# Camera
|
||||
#############################################################
|
||||
|
|
@ -108,14 +94,23 @@ class ExampleHardwareApp(toga.App):
|
|||
|
||||
async def take_photo(self, widget, **kwargs):
|
||||
try:
|
||||
if not self.camera.has_permission:
|
||||
await self.camera.request_permission()
|
||||
|
||||
image = await self.camera.take_photo()
|
||||
if image is None:
|
||||
self.photo.image = "resources/default.png"
|
||||
else:
|
||||
self.photo.image = image
|
||||
except NotImplementedError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
"The Camera API is not implemented on this platform",
|
||||
)
|
||||
except PermissionError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!", "You have not granted permission to take photos"
|
||||
"Oh no!",
|
||||
"You have not granted permission to take photos",
|
||||
)
|
||||
|
||||
def location_changed(self, geo, location, altitude, **kwargs):
|
||||
|
|
@ -130,8 +125,15 @@ class ExampleHardwareApp(toga.App):
|
|||
|
||||
async def update_location(self, widget, **kwargs):
|
||||
try:
|
||||
await self.geolocation.request_permission()
|
||||
|
||||
location = await self.geolocation.current_location()
|
||||
self.location_changed(None, location, None)
|
||||
except NotImplementedError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
"The Geolocation API is not implemented on this platform",
|
||||
)
|
||||
except PermissionError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
|
|
@ -140,7 +142,14 @@ class ExampleHardwareApp(toga.App):
|
|||
|
||||
async def start_location_updates(self, widget, **kwargs):
|
||||
try:
|
||||
await self.geolocation.request_permission()
|
||||
|
||||
self.geolocation.start()
|
||||
except NotImplementedError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
"The Geolocation API is not implemented on this platform",
|
||||
)
|
||||
except PermissionError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
|
|
@ -149,7 +158,14 @@ class ExampleHardwareApp(toga.App):
|
|||
|
||||
async def stop_location_updates(self, widget, **kwargs):
|
||||
try:
|
||||
await self.geolocation.request_permission()
|
||||
|
||||
self.geolocation.stop()
|
||||
except NotImplementedError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
"The Geolocation API is not implemented on this platform",
|
||||
)
|
||||
except PermissionError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
|
|
@ -157,10 +173,29 @@ class ExampleHardwareApp(toga.App):
|
|||
)
|
||||
|
||||
async def request_background_location(self, widget, **kwargs):
|
||||
if not await self.geolocation.request_background_permission():
|
||||
try:
|
||||
if self.geolocation.has_background_permission:
|
||||
await self.main_window.info_dialog(
|
||||
"All good!",
|
||||
"Application has permission to perform background geolocation",
|
||||
)
|
||||
else:
|
||||
if not await self.geolocation.request_permission():
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
"You have not granted permission for location tracking",
|
||||
)
|
||||
return
|
||||
|
||||
if not await self.geolocation.request_background_permission():
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
"You have not granted permission for background location tracking",
|
||||
)
|
||||
except NotImplementedError:
|
||||
await self.main_window.info_dialog(
|
||||
"Oh no!",
|
||||
"You have not granted permission for background location tracking",
|
||||
"The Geolocation API is not implemented on this platform",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,11 +38,13 @@ class TogaLocationDelegate(NSObject):
|
|||
@objc_method
|
||||
def locationManagerDidChangeAuthorization_(self, manager) -> None:
|
||||
while self.impl.permission_requests:
|
||||
future = self.impl.permission_requests.pop()
|
||||
future.set_result(self.impl.has_permission())
|
||||
future, permission = self.impl.permission_requests.pop()
|
||||
future.set_result(permission())
|
||||
|
||||
@objc_method
|
||||
def locationManager_didUpdateLocations_(self, manager, locations) -> None:
|
||||
# The API *can* send multiple locations in a single update; they should be
|
||||
# sorted chronologically; only propagate the most recent one
|
||||
toga_loc = toga_location(locations[-1])
|
||||
|
||||
# Set all outstanding location requests with location reported
|
||||
|
|
@ -86,8 +88,8 @@ class Geolocation:
|
|||
|
||||
def has_permission(self):
|
||||
return self.native.authorizationStatus in {
|
||||
CLAuthorizationStatus.AuthorizedAlways.value,
|
||||
CLAuthorizationStatus.AuthorizedWhenInUse.value,
|
||||
CLAuthorizationStatus.AuthorizedAlways.value,
|
||||
}
|
||||
|
||||
def has_background_permission(self):
|
||||
|
|
@ -97,14 +99,14 @@ class Geolocation:
|
|||
)
|
||||
|
||||
def request_permission(self, future):
|
||||
self.permission_requests.append(future)
|
||||
self.permission_requests.append((future, self.has_permission))
|
||||
self.native.requestWhenInUseAuthorization()
|
||||
|
||||
def request_background_permission(self, future):
|
||||
if NSBundle.mainBundle.objectForInfoDictionaryKey(
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription"
|
||||
):
|
||||
self.permission_requests.append(future)
|
||||
self.permission_requests.append((future, self.has_background_permission))
|
||||
|
||||
self.native.requestAlwaysAuthorization()
|
||||
else: # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ NSError = ObjCClass("NSError")
|
|||
|
||||
|
||||
class GeolocationProbe(AppProbe):
|
||||
request_permission_on_first_use = False
|
||||
|
||||
def __init__(self, monkeypatch, app_probe):
|
||||
super().__init__(app_probe.app)
|
||||
|
||||
|
|
@ -27,17 +25,21 @@ class GeolocationProbe(AppProbe):
|
|||
# be granted if requested but, has not been granted *yet*. Unless primed,
|
||||
# permissions will be denied.
|
||||
self._mock_permission = None
|
||||
self._mock_background_permission = None
|
||||
|
||||
# Mock CLLocationManager
|
||||
self._mock_location_manager = Mock()
|
||||
|
||||
# Mock the CLLocationManager.authorizationStatus property
|
||||
def _mock_auth_status():
|
||||
return {
|
||||
2: CLAuthorizationStatus.AuthorizedAlways.value,
|
||||
1: CLAuthorizationStatus.AuthorizedWhenInUse.value,
|
||||
0: CLAuthorizationStatus.Denied.value,
|
||||
}.get(self._mock_permission, CLAuthorizationStatus.NotDetermined.value)
|
||||
if self._mock_background_permission == 1:
|
||||
return CLAuthorizationStatus.AuthorizedAlways.value
|
||||
elif self._mock_permission == 1:
|
||||
return CLAuthorizationStatus.AuthorizedWhenInUse.value
|
||||
elif self._mock_permission == 0:
|
||||
return CLAuthorizationStatus.Denied.value
|
||||
else:
|
||||
return CLAuthorizationStatus.NotDetermined.value
|
||||
|
||||
type(self._mock_location_manager).authorizationStatus = PropertyMock(
|
||||
side_effect=_mock_auth_status
|
||||
|
|
@ -56,8 +58,10 @@ class GeolocationProbe(AppProbe):
|
|||
)
|
||||
|
||||
def _mock_request_always():
|
||||
if self._mock_permission == -2:
|
||||
self._mock_permission = abs(self._mock_permission)
|
||||
if self._mock_background_permission is None:
|
||||
self._mock_background_permission = 0
|
||||
else:
|
||||
self._mock_background_permission = abs(self._mock_background_permission)
|
||||
|
||||
# Trigger delegate handling for permission change
|
||||
self.app.geolocation._impl.delegate.locationManagerDidChangeAuthorization(
|
||||
|
|
@ -106,13 +110,13 @@ class GeolocationProbe(AppProbe):
|
|||
self._mock_permission = -1
|
||||
|
||||
def grant_background_permission(self):
|
||||
self._mock_permission = -2
|
||||
self._mock_background_permission = -1
|
||||
|
||||
def allow_permission(self):
|
||||
self._mock_permission = 1
|
||||
|
||||
def allow_background_permission(self):
|
||||
self._mock_permission = 2
|
||||
self._mock_background_permission = 1
|
||||
|
||||
def reject_permission(self):
|
||||
self._mock_permission = 0
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ async def geolocation_probe(monkeypatch, app_probe):
|
|||
async def test_grant_permission(app, geolocation_probe):
|
||||
"""A user can grant permission to use geolocation."""
|
||||
# Prime the permission system to approve permission requests
|
||||
geolocation_probe.allow_permission()
|
||||
geolocation_probe.grant_permission()
|
||||
|
||||
# Initiate the permission request. As permissions are primed, they will be approved.
|
||||
assert await app.geolocation.request_permission()
|
||||
|
|
@ -56,7 +56,21 @@ async def test_deny_permission(app, geolocation_probe):
|
|||
async def test_grant_background_permission(app, geolocation_probe):
|
||||
"""A user can grant background permission to use geolocation."""
|
||||
# Prime the permission system to approve permission requests
|
||||
geolocation_probe.allow_background_permission()
|
||||
geolocation_probe.grant_background_permission()
|
||||
|
||||
# Foreground permissions haven't been approved, so requesting background permissions
|
||||
# will raise an error
|
||||
with pytest.raises(
|
||||
PermissionError,
|
||||
match=(
|
||||
r"Cannot ask for background geolocation permission "
|
||||
r"before confirming foreground geolocation permission\."
|
||||
),
|
||||
):
|
||||
await app.geolocation.request_background_permission()
|
||||
|
||||
# Pre-approve foreground permissions
|
||||
geolocation_probe.allow_permission()
|
||||
|
||||
# Initiate the permission request. As permissions are primed, they will be approved.
|
||||
assert await app.geolocation.request_background_permission()
|
||||
|
|
@ -75,18 +89,37 @@ async def test_grant_background_permission(app, geolocation_probe):
|
|||
|
||||
async def test_deny_background_permission(app, geolocation_probe):
|
||||
"""A user can deny background permission to use geolocation."""
|
||||
# Initiate the permission request. As permissions are not primed, they will be denied.
|
||||
# Foreground permissions haven't been approved, so requesting background permissions
|
||||
# will raise an error.
|
||||
with pytest.raises(
|
||||
PermissionError,
|
||||
match=(
|
||||
r"Cannot ask for background geolocation permission "
|
||||
r"before confirming foreground geolocation permission\."
|
||||
),
|
||||
):
|
||||
await app.geolocation.request_background_permission()
|
||||
|
||||
# Neither permission does not exist yet
|
||||
assert not app.geolocation.has_permission
|
||||
assert not app.geolocation.has_background_permission
|
||||
|
||||
# Pre-approve foreground permissions
|
||||
geolocation_probe.allow_permission()
|
||||
|
||||
# Initiate the permission request. As background permissions are not primed, they
|
||||
# will be denied.
|
||||
assert not await app.geolocation.request_background_permission()
|
||||
|
||||
# Permission has been denied
|
||||
assert not app.geolocation.has_permission
|
||||
# Background permission has been denied, but foreground permission must exist
|
||||
assert app.geolocation.has_permission
|
||||
assert not app.geolocation.has_background_permission
|
||||
|
||||
# A second request to request permissions is a no-op
|
||||
assert not await app.geolocation.request_background_permission()
|
||||
|
||||
# Permission is still denied
|
||||
assert not app.geolocation.has_permission
|
||||
# Background permission is still denied, but foreground permission must exist
|
||||
assert app.geolocation.has_permission
|
||||
assert not app.geolocation.has_background_permission
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue