import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:spotiflac_android/l10n/app_localizations.dart'; import 'package:spotiflac_android/widgets/audio_output_button.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); const channel = MethodChannel('com.zarz.spotiflac/audio_output'); final messenger = TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; Widget app({ValueChanged? onChanged}) => MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: Scaffold(body: AudioOutputButton(onPickerChanged: onChanged)), ); tearDown(() { messenger.setMockMethodCallHandler(channel, null); messenger.setMockMethodCallHandler(SystemChannels.platform_views, null); }); testWidgets( 'iOS embeds the native audio route control and forwards its presentation', (tester) async { int? viewId; final visibility = []; final updates = []; MethodChannel? viewChannel; messenger.setMockMethodCallHandler(SystemChannels.platform_views, ( call, ) async { if (call.method == 'create') { final args = call.arguments as Map; expect(args['viewType'], 'com.zarz.spotiflac/audio_output'); viewId = args['id'] as int; viewChannel = MethodChannel( 'com.zarz.spotiflac/audio_output/$viewId', ); messenger.setMockMethodCallHandler( viewChannel!, (call) async => updates.add(call), ); } return null; }); await tester.pumpWidget(app(onChanged: visibility.add)); await tester.pumpAndSettle(); expect(viewId, isNotNull); expect(updates.last.arguments, containsPair('label', 'Audio Output')); for (final open in [true, false]) { await messenger.handlePlatformMessage( viewChannel!.name, const StandardMethodCodec().encodeMethodCall( MethodCall('pickerChanged', open), ), (_) {}, ); } expect(visibility, [true, false]); await tester.pumpWidget(const SizedBox()); messenger.setMockMethodCallHandler(viewChannel!, null); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS), ); testWidgets('opens native output picker once and reports failure visibly', ( tester, ) async { final completion = Completer(); var requests = 0; final visibility = []; messenger.setMockMethodCallHandler(channel, (call) { expect(call.method, 'show'); requests++; return completion.future; }); await tester.pumpWidget(app(onChanged: visibility.add)); await tester.tap(find.byTooltip('Audio Output')); await tester.tap(find.byTooltip('Audio Output')); expect(requests, 1); expect(visibility, [true]); completion.complete(false); await tester.pumpAndSettle(); expect(visibility, [true, false]); expect(find.text('Unable to open audio output settings.'), findsOneWidget); }, variant: TargetPlatformVariant.only(TargetPlatform.android)); testWidgets('successful native request has no error and disposal is safe', ( tester, ) async { messenger.setMockMethodCallHandler(channel, (_) async => true); await tester.pumpWidget(app()); await tester.tap(find.byTooltip('Audio Output')); await tester.pumpAndSettle(); expect(find.byType(SnackBar), findsNothing); final completion = Completer(); messenger.setMockMethodCallHandler(channel, (_) => completion.future); await tester.tap(find.byTooltip('Audio Output')); await tester.pumpWidget(const SizedBox()); completion.complete(false); await tester.pumpAndSettle(); expect(tester.takeException(), isNull); }, variant: TargetPlatformVariant.only(TargetPlatform.android)); testWidgets('unsupported desktop does not expose an inert control', ( tester, ) async { await tester.pumpWidget(app()); expect(find.byType(IconButton), findsNothing); expect(find.byType(UiKitView), findsNothing); }, variant: TargetPlatformVariant.only(TargetPlatform.linux)); }