Newer
Older
SmTIAS-Capture / test / services / permission_service_test.dart
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:permission_handler/permission_handler.dart';

import 'package:smtias_capture/services/permission_service.dart';

/// permission_handler が使うプラットフォームチャネル名.
const _channel = MethodChannel('flutter.baseflow.com/permissions/methods');

/// プラットフォームチャネルの応答を差し替えるテスト用のフェイク.
///
/// `checkPermissionStatus` には [checkStatus] を,
/// `requestPermissions` には [requestStatus] を返す.
class _FakePermissionPlatform {
  PermissionStatus checkStatus = PermissionStatus.denied;
  PermissionStatus requestStatus = PermissionStatus.denied;

  final List<String> calls = [];

  Future<Object?> handle(MethodCall call) async {
    calls.add(call.method);
    switch (call.method) {
      case 'checkPermissionStatus':
        return checkStatus.index;
      case 'requestPermissions':
        final permissions = (call.arguments as List).cast<int>();
        return {for (final p in permissions) p: requestStatus.index};
      case 'openAppSettings':
        return true;
    }
    return null;
  }
}

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  late _FakePermissionPlatform platform;

  setUp(() {
    platform = _FakePermissionPlatform();
    TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
        .setMockMethodCallHandler(_channel, platform.handle);
  });

  tearDown(() {
    TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
        .setMockMethodCallHandler(_channel, null);
  });

  group('PermissionService', () {
    group('requestCamera()', () {
      test('許可された場合,true を返す', () async {
        platform.requestStatus = PermissionStatus.granted;
        final service = PermissionService();

        expect(await service.requestCamera(), isTrue);
        expect(platform.calls, contains('requestPermissions'));
      });

      test('拒否された場合,false を返す', () async {
        platform.requestStatus = PermissionStatus.denied;
        final service = PermissionService();

        expect(await service.requestCamera(), isFalse);
      });

      test('永久拒否された場合,false を返す', () async {
        platform.requestStatus = PermissionStatus.permanentlyDenied;
        final service = PermissionService();

        expect(await service.requestCamera(), isFalse);
      });
    });

    group('requestStorage()', () {
      test('許可された場合,true を返す', () async {
        platform.requestStatus = PermissionStatus.granted;
        final service = PermissionService();

        expect(await service.requestStorage(), isTrue);
      });

      test('拒否された場合,false を返す', () async {
        platform.requestStatus = PermissionStatus.denied;
        final service = PermissionService();

        expect(await service.requestStorage(), isFalse);
      });
    });

    group('isCameraPermanentlyDenied()(requestCamera 未実行)', () {
      test('status が permanentlyDenied の場合,true を返す', () async {
        platform.checkStatus = PermissionStatus.permanentlyDenied;
        final service = PermissionService();

        expect(await service.isCameraPermanentlyDenied(), isTrue);
        expect(platform.calls, ['checkPermissionStatus']);
      });

      test('status が denied の場合,false を返す', () async {
        platform.checkStatus = PermissionStatus.denied;
        final service = PermissionService();

        expect(await service.isCameraPermanentlyDenied(), isFalse);
      });

      test('status が granted の場合,false を返す', () async {
        platform.checkStatus = PermissionStatus.granted;
        final service = PermissionService();

        expect(await service.isCameraPermanentlyDenied(), isFalse);
      });
    });

    group('isCameraPermanentlyDenied()(requestCamera 実行後)', () {
      test(
        'request 結果が permanentlyDenied の場合,status が denied でも true を返す',
        () async {
          // permission_handler_android 14.1.0 以降の挙動を再現する:
          // status では永久拒否が分からず,request の結果でのみ分かる.
          platform.requestStatus = PermissionStatus.permanentlyDenied;
          platform.checkStatus = PermissionStatus.denied;
          final service = PermissionService();

          await service.requestCamera();

          expect(await service.isCameraPermanentlyDenied(), isTrue);
        },
      );

      test(
        'request 結果が denied の場合,status が permanentlyDenied でも false を返す',
        () async {
          platform.requestStatus = PermissionStatus.denied;
          platform.checkStatus = PermissionStatus.permanentlyDenied;
          final service = PermissionService();

          await service.requestCamera();

          expect(await service.isCameraPermanentlyDenied(), isFalse);
        },
      );

      test('request 結果が granted の場合,false を返す', () async {
        platform.requestStatus = PermissionStatus.granted;
        platform.checkStatus = PermissionStatus.permanentlyDenied;
        final service = PermissionService();

        await service.requestCamera();

        expect(await service.isCameraPermanentlyDenied(), isFalse);
      });

      test('request 後の判定ではプラットフォームの status を問い合わせない', () async {
        platform.requestStatus = PermissionStatus.permanentlyDenied;
        final service = PermissionService();

        await service.requestCamera();
        platform.calls.clear();
        await service.isCameraPermanentlyDenied();

        expect(platform.calls, isNot(contains('checkPermissionStatus')));
      });

      test('request を複数回実行した場合,直近の結果で判定する', () async {
        final service = PermissionService();

        platform.requestStatus = PermissionStatus.permanentlyDenied;
        await service.requestCamera();
        expect(await service.isCameraPermanentlyDenied(), isTrue);

        // 設定画面で許可して戻った後の再要求を想定する.
        platform.requestStatus = PermissionStatus.granted;
        await service.requestCamera();
        expect(await service.isCameraPermanentlyDenied(), isFalse);
      });

      test('requestStorage の結果はカメラの永久拒否判定に影響しない', () async {
        platform.checkStatus = PermissionStatus.denied;
        platform.requestStatus = PermissionStatus.permanentlyDenied;
        final service = PermissionService();

        await service.requestStorage();

        expect(await service.isCameraPermanentlyDenied(), isFalse);
      });
    });

    group('openSettings()', () {
      test('アプリの設定画面を開く要求をプラットフォームへ送る', () async {
        final service = PermissionService();

        await service.openSettings();

        expect(platform.calls, contains('openAppSettings'));
      });
    });
  });
}