From f27e83612d0d8cca3039df6d2bd6c5bdc2c8b110 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sat, 2 May 2026 19:36:27 -0400 Subject: [PATCH] fix(flutter): two test environment issues from CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit smoke_test was hanging in pumpAndSettle because real flutter_secure_storage MethodChannel calls don't resolve in widget tests (no platform channel registered). Override secureStorageProvider with a mocktail FlutterSecureStorage that returns null for every read, so the GoRouter redirect resolves and ServerUrlScreen renders. player_provider_test second case was crashing because constructing MinstrelAudioHandler() instantiates just_audio's AudioPlayer, which calls setMethodCallHandler during construction — that asserts unless the WidgetsFlutterBinding is initialized. Add TestWidgetsFlutterBinding.ensureInitialized() at the top of main(). Both are unit-test plumbing fixes; no production code change. --- .../test/player/player_provider_test.dart | 6 +++++ flutter_client/test/smoke_test.dart | 23 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/flutter_client/test/player/player_provider_test.dart b/flutter_client/test/player/player_provider_test.dart index 28746dab..be31d391 100644 --- a/flutter_client/test/player/player_provider_test.dart +++ b/flutter_client/test/player/player_provider_test.dart @@ -5,6 +5,12 @@ import 'package:minstrel/player/audio_handler.dart'; import 'package:minstrel/player/player_provider.dart'; void main() { + // MinstrelAudioHandler() instantiates just_audio's AudioPlayer, which calls + // setMethodCallHandler on a platform channel during construction. Without an + // initialized binding the call asserts. We're not exercising real audio in + // the test — this just gives us a working binary messenger. + TestWidgetsFlutterBinding.ensureInitialized(); + test('audioHandlerProvider must be overridden — bare read throws', () { final container = ProviderContainer(); addTearDown(container.dispose); diff --git a/flutter_client/test/smoke_test.dart b/flutter_client/test/smoke_test.dart index 001cb05e..2c66efcc 100644 --- a/flutter_client/test/smoke_test.dart +++ b/flutter_client/test/smoke_test.dart @@ -1,11 +1,32 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:minstrel/app.dart'; +import 'package:minstrel/auth/auth_provider.dart'; + +class _NullStorage extends Mock implements FlutterSecureStorage {} void main() { + setUpAll(() { + registerFallbackValue(''); + }); + testWidgets('cold launch lands on server-url screen when no URL stored', (tester) async { - await tester.pumpWidget(const ProviderScope(child: MinstrelApp())); + // Override secureStorageProvider so all reads return null deterministically. + // Without this the real flutter_secure_storage MethodChannel hangs in + // widget tests (no platform channel), so the GoRouter redirect never + // resolves and ServerUrlScreen never renders. + final storage = _NullStorage(); + when(() => storage.read(key: any(named: 'key'))).thenAnswer((_) async => null); + + await tester.pumpWidget(ProviderScope( + overrides: [ + secureStorageProvider.overrideWithValue(storage), + ], + child: const MinstrelApp(), + )); await tester.pumpAndSettle(); expect(find.text('Connect to your Minstrel'), findsOneWidget); });