Commit e56b7118 by Maurits van Beusekom Committed by GitHub

Merge pull request #485 from Baseflow/implementing_tests

Added tests
parents 3828b56f 376317fc
## 3.1.1
* Fixed conversion issue where `PermissionStatus.denied` was not translated to the correct index.
* Added unit-tests to guard API against breaking changes.
## 3.1.0 ## 3.1.0
* Added support for bluetooth permissions. * Added support for bluetooth permissions.
......
# permission_handler_platform_interface # permission_handler_platform_interface
[![pub package](https://img.shields.io/pub/v/permission_handler_platform_interface.svg)](https://pub.dartlang.org/packages/permission_handler_platform_interface) ![Build status](https://github.com/Baseflow/flutter-permission-handler/workflows/platform_interface_package/badge.svg?branch=master) [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart) [![pub package](https://img.shields.io/pub/v/permission_handler_platform_interface.svg)](https://pub.dartlang.org/packages/permission_handler_platform_interface) ![Build status](https://github.com/Baseflow/flutter-permission-handler/workflows/platform_interface_package/badge.svg?branch=master) [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart)[![codecov](https://codecov.io/gh/Baseflow/flutter-permission-handler/branch/master/graph/badge.svg)](https://codecov.io/gh/Baseflow/flutter-permission-handler)
A common platform interface for the [`permission_handler`][1] plugin. A common platform interface for the [`permission_handler`][1] plugin.
......
library permission_handler_platform_interface; library permission_handler_platform_interface;
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'src/method_channel/method_channel_permission_handler.dart'; import 'src/method_channel/method_channel_permission_handler.dart';
......
...@@ -2,12 +2,12 @@ part of permission_handler_platform_interface; ...@@ -2,12 +2,12 @@ part of permission_handler_platform_interface;
/// Defines the state of a [Permission]. /// Defines the state of a [Permission].
enum PermissionStatus { enum PermissionStatus {
/// The user granted access to the requested feature.
granted,
/// The user denied access to the requested feature. /// The user denied access to the requested feature.
denied, denied,
/// The user granted access to the requested feature.
granted,
/// The OS denied access to the requested feature. The user cannot change /// The OS denied access to the requested feature. The user cannot change
/// this app's status, possibly due to active restrictions such as parental /// this app's status, possibly due to active restrictions such as parental
/// controls being in place. /// controls being in place.
...@@ -34,11 +34,9 @@ extension PermissionStatusValue on PermissionStatus { ...@@ -34,11 +34,9 @@ extension PermissionStatusValue on PermissionStatus {
return 1; return 1;
case PermissionStatus.restricted: case PermissionStatus.restricted:
return 2; return 2;
case PermissionStatus.denied: case PermissionStatus.limited:
return 3; return 3;
case PermissionStatus.permanentlyDenied: case PermissionStatus.permanentlyDenied:
return 5;
case PermissionStatus.limited:
return 4; return 4;
default: default:
throw UnimplementedError(); throw UnimplementedError();
...@@ -57,12 +55,12 @@ extension PermissionStatusValue on PermissionStatus { ...@@ -57,12 +55,12 @@ extension PermissionStatusValue on PermissionStatus {
} }
extension PermissionStatusGetters on PermissionStatus { extension PermissionStatusGetters on PermissionStatus {
/// If the user granted access to the requested feature.
bool get isGranted => this == PermissionStatus.granted;
/// If the user denied access to the requested feature. /// If the user denied access to the requested feature.
bool get isDenied => this == PermissionStatus.denied; bool get isDenied => this == PermissionStatus.denied;
/// If the user granted access to the requested feature.
bool get isGranted => this == PermissionStatus.granted;
/// If the OS denied access to the requested feature. The user cannot change /// If the OS denied access to the requested feature. The user cannot change
/// this app's status, possibly due to active restrictions such as parental /// this app's status, possibly due to active restrictions such as parental
/// controls being in place. /// controls being in place.
......
...@@ -5,6 +5,9 @@ part of permission_handler_platform_interface; ...@@ -5,6 +5,9 @@ part of permission_handler_platform_interface;
/// the related service. /// the related service.
class PermissionWithService extends Permission { class PermissionWithService extends Permission {
const PermissionWithService._(int value) : super._(value); const PermissionWithService._(int value) : super._(value);
@visibleForTesting
const PermissionWithService.private(int value) : super._(value);
} }
/// Defines the permissions which can be checked and requested. /// Defines the permissions which can be checked and requested.
......
...@@ -3,7 +3,7 @@ description: A common platform interface for the permission_handler plugin. ...@@ -3,7 +3,7 @@ description: A common platform interface for the permission_handler plugin.
homepage: https://github.com/baseflow/flutter-permission-handler/tree/master/permission_handler_platform_interface homepage: https://github.com/baseflow/flutter-permission-handler/tree/master/permission_handler_platform_interface
# NOTE: We strongly prefer non-breaking changes, even at the expense of a # NOTE: We strongly prefer non-breaking changes, even at the expense of a
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
version: 3.1.0 version: 3.1.1
dependencies: dependencies:
flutter: flutter:
......
import 'package:flutter/services.dart';
class MethodChannelMock {
final MethodChannel methodChannel;
final String method;
final dynamic result;
final Duration delay;
MethodChannelMock({
required String channelName,
required this.method,
this.result,
this.delay = Duration.zero,
}) : methodChannel = MethodChannel(channelName) {
methodChannel.setMockMethodCallHandler(_handler);
}
Future _handler(MethodCall methodCall) async {
if (methodCall.method != method) {
throw MissingPluginException('No implementation found for method '
'$method on channel ${methodChannel.name}');
}
return Future.delayed(delay, () {
if (result is Exception) {
throw result;
}
return Future.value(result);
});
}
}
import 'package:flutter_test/flutter_test.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
import 'package:permission_handler_platform_interface/src/method_channel/method_channel_permission_handler.dart';
import 'method_channel_mock.dart';
List<Permission> get mockPermissions => List.of(
<Permission>{Permission.calendar, Permission.camera, Permission.contacts});
Map<Permission, PermissionStatus> get mockPermissionMap => {};
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('checkPermissionStatus: When checking for permission', () {
test('Should receive granted if user wants access to the requested feature',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'checkPermissionStatus',
result: PermissionStatus.denied.value,
);
final permissionStatus = await MethodChannelPermissionHandler()
.checkPermissionStatus(Permission.calendar);
expect(permissionStatus, PermissionStatus.denied);
});
test('Should receive denied if user denied access to the requested feature',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'checkPermissionStatus',
result: PermissionStatus.denied.value,
);
final permissionStatus = await MethodChannelPermissionHandler()
.checkPermissionStatus(Permission.calendar);
expect(permissionStatus, PermissionStatus.denied);
});
test(
// ignore: lines_longer_than_80_chars
'Should receive restricted if OS denied rights for to the requested feature',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'checkPermissionStatus',
result: PermissionStatus.restricted.value,
);
final permissionStatus = await MethodChannelPermissionHandler()
.checkPermissionStatus(Permission.calendar);
expect(permissionStatus, PermissionStatus.restricted);
});
test(
// ignore: lines_longer_than_80_chars
'Should receive limited if user has authorized this application for limited access',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'checkPermissionStatus',
result: PermissionStatus.limited.value,
);
final permissionStatus = await MethodChannelPermissionHandler()
.checkPermissionStatus(Permission.calendar);
expect(permissionStatus, PermissionStatus.limited);
});
test(
// ignore: lines_longer_than_80_chars
'Should receive permanentlyDenied if user denied access and selected to never show a request for this permission again',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'checkPermissionStatus',
result: PermissionStatus.permanentlyDenied.value,
);
final permissionStatus = await MethodChannelPermissionHandler()
.checkPermissionStatus(Permission.calendar);
expect(permissionStatus, PermissionStatus.permanentlyDenied);
});
});
group('checkServiceStatus: When checking for service', () {
// ignore: lines_longer_than_80_chars
test(
'Should receive disabled if the service for the permission is disabled',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'checkServiceStatus',
result: ServiceStatus.disabled.value,
);
final serviceStatus = await MethodChannelPermissionHandler()
.checkServiceStatus(Permission.calendar);
expect(serviceStatus, ServiceStatus.disabled);
});
test('Should receive enabled if the service for the permission is enabled',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'checkServiceStatus',
result: ServiceStatus.enabled.value,
);
final serviceStatus = await MethodChannelPermissionHandler()
.checkServiceStatus(Permission.calendar);
expect(serviceStatus, ServiceStatus.enabled);
});
test(
// ignore: lines_longer_than_80_chars
'Should receive notApplicable if the permission does not have an associated service on the current platform',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'checkServiceStatus',
result: ServiceStatus.notApplicable.value,
);
final serviceStatus = await MethodChannelPermissionHandler()
.checkServiceStatus(Permission.calendar);
expect(serviceStatus, ServiceStatus.notApplicable);
});
});
group('openAppSettings: When opening the App settings', () {
test('Should receive true if the page can be opened', () async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'openAppSettings',
result: true,
);
final hasOpenedAppSettings =
await MethodChannelPermissionHandler().openAppSettings();
expect(hasOpenedAppSettings, true);
});
test('Should receive false if an error occurred', () async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'openAppSettings',
result: false,
);
final hasOpenedAppSettings =
await MethodChannelPermissionHandler().openAppSettings();
expect(hasOpenedAppSettings, false);
});
});
group('requestPermissions: When requesting for permission', () {
// ignore: lines_longer_than_80_chars
test('returns a Map with all the PermissionStatus of the given permissions',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'requestPermissions',
result: mockPermissionMap,
);
final result = await MethodChannelPermissionHandler()
.requestPermissions(mockPermissions);
expect(result, isA<Map<Permission, PermissionStatus>>());
});
});
group('shouldShowRequestPermissionRationale:', () {
test(
// ignore: lines_longer_than_80_chars
'should return true when you should show a rationale for requesting permission.',
() async {
MethodChannelMock(
channelName: 'flutter.baseflow.com/permissions/methods',
method: 'shouldShowRequestPermissionRationale',
result: true,
);
final shouldShowRationale = await MethodChannelPermissionHandler()
.shouldShowRequestPermissionRationale(mockPermissions.first);
expect(shouldShowRationale, true);
});
});
}
import 'package:flutter_test/flutter_test.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
import 'package:permission_handler_platform_interface/src/method_channel/utils/codec.dart';
void main() {
group('Codec', () {
test('decodePermissionStatus should return a PermissionStatus', () {
expect(Codec.decodePermissionStatus(0), PermissionStatus.denied);
});
test('decodeServiceStatus should a corresponding ServiceStatus', () {
expect(Codec.decodeServiceStatus(0), ServiceStatus.disabled);
});
test(
'decodePermissionRequestResult should convert a map<int, int>'
'to map<Permission, PermissionStatus>', () {
var value = <int, int>{
1: 1,
};
var permissionMap = Codec.decodePermissionRequestResult(value);
expect(permissionMap.keys.first, isA<Permission>());
expect(permissionMap.values.first, isA<PermissionStatus>());
});
test('encodePermissions should return a list of integers', () {
var permissions = [Permission.accessMediaLocation];
var integers = Codec.encodePermissions(permissions);
expect(integers.first, isA<int>());
});
});
}
import 'package:flutter_test/flutter_test.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
import 'package:permission_handler_platform_interface/src/method_channel/method_channel_permission_handler.dart';
import 'package:mockito/mockito.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('$PermissionHandlerPlatform', () {
test('$MethodChannelPermissionHandler is the default instance', () {
expect(PermissionHandlerPlatform.instance,
isA<MethodChannelPermissionHandler>());
});
test('Cannot be implemented with `implements`', () {
expect(() {
PermissionHandlerPlatform.instance =
ImplementsPermissionHandlerPlatform();
}, throwsNoSuchMethodError);
});
test('Can be extended with `extend`', () {
PermissionHandlerPlatform.instance = ExtendsPermissionHandlerPlatform();
});
test('Can be mocked with `implements`', () {
final mock = MockPermissionHandlerPlatform();
PermissionHandlerPlatform.instance = mock;
});
test(
// ignore: lines_longer_than_80_chars
'Default implementation of checkPermissionStatus should throw unimplemented error',
() {
final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform();
expect(() {
permissionHandlerPlatform
.checkPermissionStatus(Permission.accessMediaLocation);
}, throwsUnimplementedError);
});
test(
// ignore: lines_longer_than_80_chars
'Default implementation of checkServiceStatus should throw unimplemented error',
() {
final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform();
expect(() {
permissionHandlerPlatform
.checkServiceStatus(Permission.accessMediaLocation);
}, throwsUnimplementedError);
});
test(
// ignore: lines_longer_than_80_chars
'Default implementation of openAppSettings should throw unimplemented error',
() {
final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform();
expect(
permissionHandlerPlatform.openAppSettings, throwsUnimplementedError);
});
test(
// ignore: lines_longer_than_80_chars
'Default implementation of requestPermissions should throw unimplemented error',
() {
final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform();
var permission = <Permission>[Permission.accessMediaLocation];
expect(() {
permissionHandlerPlatform.requestPermissions(permission);
}, throwsUnimplementedError);
});
test(
// ignore: lines_longer_than_80_chars
'Default implementation of shouldShowRequestPermissionRationale should throw unimplemented error',
() {
final permissionHandlerPlatform = ExtendsPermissionHandlerPlatform();
expect(() {
permissionHandlerPlatform.shouldShowRequestPermissionRationale(
Permission.accessMediaLocation);
}, throwsUnimplementedError);
});
});
}
class ImplementsPermissionHandlerPlatform implements PermissionHandlerPlatform {
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class ExtendsPermissionHandlerPlatform extends PermissionHandlerPlatform {}
class MockPermissionHandlerPlatform extends Mock
with
// ignore: prefer_mixin
MockPlatformInterfaceMixin
implements
PermissionHandlerPlatform {}
import 'package:flutter_test/flutter_test.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
void main() {
group('PermissionSatus', () {
test('PermissionStatus should contain 5 options', () {
final values = PermissionStatus.values;
expect(values.length, 5);
});
test('PermissionStatus enum should have items in correct index', () {
final values = PermissionStatus.values;
expect(values[0], PermissionStatus.denied);
expect(values[1], PermissionStatus.granted);
expect(values[2], PermissionStatus.restricted);
expect(values[3], PermissionStatus.limited);
expect(values[4], PermissionStatus.permanentlyDenied);
});
});
group('PermissionStatusValue', () {
test('PermissonStatusValue returns right integer', () {
expect(PermissionStatus.denied.value, 0);
expect(PermissionStatus.granted.value, 1);
expect(PermissionStatus.restricted.value, 2);
expect(PermissionStatus.limited.value, 3);
expect(PermissionStatus.permanentlyDenied.value, 4);
});
test(
// ignore: lines_longer_than_80_chars
'statusByValue should return right index int that corresponds with the right PermissionStatus',
() {
expect(PermissionStatusValue.statusByValue(0), PermissionStatus.denied);
expect(PermissionStatusValue.statusByValue(1), PermissionStatus.granted);
expect(
PermissionStatusValue.statusByValue(2), PermissionStatus.restricted);
expect(PermissionStatusValue.statusByValue(3), PermissionStatus.limited);
expect(PermissionStatusValue.statusByValue(4),
PermissionStatus.permanentlyDenied);
});
});
group('PermissionStatusGetters', () {
test('Getters should return true if statement is met', () {
expect(PermissionStatus.denied.isDenied, true);
expect(PermissionStatus.granted.isGranted, true);
expect(PermissionStatus.restricted.isRestricted, true);
expect(PermissionStatus.limited.isLimited, true);
expect(PermissionStatus.permanentlyDenied.isPermanentlyDenied, true);
});
test('Getters should return false if statement is not met', () {
expect(PermissionStatus.denied.isGranted, false);
expect(PermissionStatus.granted.isDenied, false);
expect(PermissionStatus.restricted.isDenied, false);
expect(PermissionStatus.limited.isDenied, false);
expect(PermissionStatus.permanentlyDenied.isDenied, false);
});
});
test('test', () {});
}
import 'package:flutter_test/flutter_test.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
void main() {
test('Permission has the right amount of possible PermissionGroup values',
() {
final values = Permission.values;
expect(values.length, 22);
});
test('check if byValue returns corresponding PermissionGroup value', () {
final values = Permission.values;
for (var i = 0; i < values.length; i++) {
expect(values[i], Permission.byValue(i));
}
});
test('check if toString method returns the corresponding name', () {
var permissionWithService = PermissionWithService.private(0);
var permissionName = permissionWithService.toString();
expect(permissionName, 'Permission.calendar');
});
}
import 'package:flutter_test/flutter_test.dart';
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
void main() {
group('ServiceStatus', () {
test('ServiceStatus should contain 3 options', () {
final values = ServiceStatus.values;
expect(values.length, 3);
});
test('ServiceStatus enum should have items in correct index', () {
final values = ServiceStatus.values;
expect(values[0], ServiceStatus.disabled);
expect(values[1], ServiceStatus.enabled);
expect(values[2], ServiceStatus.notApplicable);
});
});
group('ServiceStatusValue', () {
test('ServiceStatusValue returns right integer', () {
expect(ServiceStatus.disabled.value, 0);
expect(ServiceStatus.enabled.value, 1);
expect(ServiceStatus.notApplicable.value, 2);
});
test(
// ignore: lines_longer_than_80_chars
'statusByValue should return right index int that corresponds with the right PermissionStatus',
() {
expect(ServiceStatusValue.statusByValue(0), ServiceStatus.disabled);
expect(ServiceStatusValue.statusByValue(1), ServiceStatus.enabled);
expect(ServiceStatusValue.statusByValue(2), ServiceStatus.notApplicable);
});
});
group('ServiceStatusGetters', () {
test('Getters should return true if statement is met', () {
expect(ServiceStatus.disabled.isDisabled, true);
expect(ServiceStatus.enabled.isEnabled, true);
expect(ServiceStatus.notApplicable.isNotApplicable, true);
});
test('Getters should return false if statement is not met', () {
expect(ServiceStatus.disabled.isEnabled, false);
expect(ServiceStatus.enabled.isDisabled, false);
expect(ServiceStatus.notApplicable.isDisabled, false);
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment