import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:meshcore_sar_app/l10n/app_localizations.dart'; import 'package:meshcore_sar_app/models/device_info.dart' as device_info; import 'package:meshcore_sar_app/providers/connection_provider.dart'; import 'package:meshcore_sar_app/providers/contacts_provider.dart'; import 'package:meshcore_sar_app/screens/discovery_screen.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; class _FakeConnectionProvider extends ConnectionProvider { _FakeConnectionProvider({required bool isConnected}) : _isConnected = isConnected; final bool _isConnected; final List discoveredAdvertTypes = []; @override device_info.DeviceInfo get deviceInfo => device_info.DeviceInfo( connectionState: _isConnected ? device_info.ConnectionState.connected : device_info.ConnectionState.disconnected, ); @override Future discoverNodeType({ required int advertType, bool prefixOnly = false, int since = 0, }) async { discoveredAdvertTypes.add(advertType); } } Future _pumpDiscoveryScreen( WidgetTester tester, { ContactsProvider? contactsProvider, ConnectionProvider? connectionProvider, DiscoveryScreen screen = const DiscoveryScreen(), }) async { final resolvedContactsProvider = contactsProvider ?? ContactsProvider(); final resolvedConnectionProvider = connectionProvider ?? _FakeConnectionProvider(isConnected: true); await tester.pumpWidget( MultiProvider( providers: [ ChangeNotifierProvider.value( value: resolvedContactsProvider, ), ChangeNotifierProvider.value( value: resolvedConnectionProvider, ), ], child: MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: screen, ), ), ); await tester.pumpAndSettle(); } Uint8List _publicKey(int seed) { final bytes = Uint8List(32); bytes[0] = seed; bytes[1] = seed + 1; bytes[2] = seed + 2; return bytes; } void main() { setUp(() { SharedPreferences.setMockInitialValues({}); }); testWidgets( 'discovery actions are shown in the summary card instead of overflow menu', (tester) async { tester.view.physicalSize = const Size(375, 812); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); final connectionProvider = _FakeConnectionProvider(isConnected: true); await _pumpDiscoveryScreen( tester, connectionProvider: connectionProvider, ); expect(tester.takeException(), isNull); expect(find.text('Discover repeaters'), findsOneWidget); expect(find.text('Discover sensors'), findsOneWidget); expect(find.text('Resolve all'), findsOneWidget); expect(find.text('Clear all'), findsOneWidget); expect(find.text('Search discovered nodes'), findsNothing); expect(find.byIcon(Icons.more_vert), findsNothing); expect( find.text( 'Use the discovery actions above to find repeaters and sensors on the mesh.', ), findsOneWidget, ); await tester.tap(find.text('Discover repeaters')); await tester.pump(); expect(connectionProvider.discoveredAdvertTypes, [2]); expect(find.text('Repeater discovery sent'), findsOneWidget); }, ); testWidgets('auto discovery can trigger repeater discovery on open', ( tester, ) async { final connectionProvider = _FakeConnectionProvider(isConnected: true); await _pumpDiscoveryScreen( tester, connectionProvider: connectionProvider, screen: const DiscoveryScreen(autoDiscoverRepeatersOnOpen: true), ); expect(connectionProvider.discoveredAdvertTypes, [2]); expect(find.text('Repeater discovery sent'), findsOneWidget); }); testWidgets('search and inline type filters narrow discovered nodes', ( tester, ) async { tester.view.physicalSize = const Size(390, 1400); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); final contactsProvider = ContactsProvider() ..addOrUpdatePendingAdvertMetadata( publicKey: _publicKey(0x10), typeValue: 2, advName: 'Relay Alpha', ) ..addOrUpdatePendingAdvertMetadata( publicKey: _publicKey(0x20), typeValue: 4, advName: 'WX Station', ) ..addOrUpdatePendingAdvertMetadata( publicKey: _publicKey(0x30), typeValue: 3, advName: 'Ops Room', ); await _pumpDiscoveryScreen( tester, contactsProvider: contactsProvider, connectionProvider: _FakeConnectionProvider(isConnected: true), ); expect(find.text('Search discovered nodes'), findsOneWidget); expect(find.text('All'), findsOneWidget); expect(find.text('Repeaters'), findsOneWidget); expect(find.text('Sensors'), findsOneWidget); expect(find.text('Others'), findsOneWidget); expect(find.text('Relay Alpha'), findsOneWidget); expect(find.text('WX Station'), findsOneWidget); expect(find.text('Ops Room'), findsOneWidget); await tester.tap(find.text('Sensors')); await tester.pumpAndSettle(); expect(find.text('Relay Alpha'), findsNothing); expect(find.text('WX Station'), findsOneWidget); expect(find.text('Ops Room'), findsNothing); expect(find.text('Discovered nodes (1/3)'), findsOneWidget); await tester.tap(find.text('Others')); await tester.pumpAndSettle(); expect(find.text('Relay Alpha'), findsNothing); expect(find.text('WX Station'), findsNothing); expect(find.text('Ops Room'), findsOneWidget); await tester.enterText(find.byType(TextField), 'relay'); await tester.pumpAndSettle(); expect(find.text('No matches'), findsOneWidget); await tester.tap(find.text('All')); await tester.pumpAndSettle(); expect(find.text('Relay Alpha'), findsOneWidget); expect(find.text('Ops Room'), findsNothing); await tester.enterText(find.byType(TextField), 'ops'); await tester.pumpAndSettle(); expect(find.text('Relay Alpha'), findsNothing); expect(find.text('WX Station'), findsNothing); expect(find.text('Ops Room'), findsOneWidget); expect(find.text('Discovered nodes (1/3)'), findsOneWidget); }); }