mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
200 lines
6.7 KiB
Dart
200 lines
6.7 KiB
Dart
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/message.dart';
|
|
import 'package:meshcore_sar_app/providers/app_provider.dart';
|
|
import 'package:meshcore_sar_app/providers/channels_provider.dart';
|
|
import 'package:meshcore_sar_app/providers/connection_provider.dart';
|
|
import 'package:meshcore_sar_app/providers/contacts_provider.dart';
|
|
import 'package:meshcore_sar_app/providers/drawing_provider.dart';
|
|
import 'package:meshcore_sar_app/providers/image_provider.dart' as ip;
|
|
import 'package:meshcore_sar_app/providers/messages_provider.dart';
|
|
import 'package:meshcore_sar_app/providers/voice_provider.dart';
|
|
import 'package:meshcore_sar_app/services/voice_codec_service.dart';
|
|
import 'package:meshcore_sar_app/services/voice_player_service.dart';
|
|
import 'package:meshcore_sar_app/widgets/messages/message_bubble.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
testWidgets('received bubbles show signal chips without tapping', (
|
|
tester,
|
|
) async {
|
|
final harness = await _TestHarness.create();
|
|
try {
|
|
final message = Message(
|
|
id: 'received-signal',
|
|
messageType: MessageType.contact,
|
|
senderPublicKeyPrefix: _prefix(1),
|
|
pathLen: 1,
|
|
textType: MessageTextType.plain,
|
|
senderTimestamp: 1700000000,
|
|
text: 'Inbound message',
|
|
receivedAt: DateTime.fromMillisecondsSinceEpoch(1700000000500),
|
|
deliveryStatus: MessageDeliveryStatus.received,
|
|
lastEchoRssiDbm: -84,
|
|
lastEchoSnrRaw: 24,
|
|
);
|
|
|
|
await tester.pumpWidget(_buildApp(harness, message));
|
|
await tester.pump(const Duration(milliseconds: 60));
|
|
|
|
expect(find.text('1 hop'), findsOneWidget);
|
|
expect(find.text('Fair'), findsOneWidget);
|
|
expect(find.text('-84'), findsOneWidget);
|
|
expect(find.text('6.0'), findsOneWidget);
|
|
} finally {
|
|
await _disposeHarness(tester, harness);
|
|
}
|
|
});
|
|
|
|
testWidgets('delivered direct bubbles show timing chips without tapping', (
|
|
tester,
|
|
) async {
|
|
final harness = await _TestHarness.create();
|
|
try {
|
|
final message = Message(
|
|
id: 'sent-direct-signal',
|
|
messageType: MessageType.contact,
|
|
senderPublicKeyPrefix: _prefix(11),
|
|
recipientPublicKey: _key(77),
|
|
pathLen: 0,
|
|
textType: MessageTextType.plain,
|
|
senderTimestamp: 1700000000,
|
|
text: 'Outbound message',
|
|
receivedAt: DateTime.fromMillisecondsSinceEpoch(1700000000500),
|
|
deliveryStatus: MessageDeliveryStatus.delivered,
|
|
roundTripTimeMs: 320,
|
|
);
|
|
|
|
await tester.pumpWidget(_buildApp(harness, message));
|
|
await tester.pump(const Duration(milliseconds: 60));
|
|
|
|
expect(find.text('Direct'), findsOneWidget);
|
|
expect(find.text('320ms'), findsOneWidget);
|
|
} finally {
|
|
await _disposeHarness(tester, harness);
|
|
}
|
|
});
|
|
|
|
testWidgets('sent channel bubbles show echo chips without tapping', (
|
|
tester,
|
|
) async {
|
|
final harness = await _TestHarness.create();
|
|
try {
|
|
final message = Message(
|
|
id: 'sent-channel-signal',
|
|
messageType: MessageType.channel,
|
|
senderPublicKeyPrefix: _prefix(21),
|
|
channelIdx: 0,
|
|
pathLen: 0,
|
|
textType: MessageTextType.plain,
|
|
senderTimestamp: 1700000000,
|
|
text: 'Broadcast message',
|
|
receivedAt: DateTime.fromMillisecondsSinceEpoch(1700000000500),
|
|
deliveryStatus: MessageDeliveryStatus.sent,
|
|
echoCount: 2,
|
|
lastEchoRssiDbm: -76,
|
|
lastEchoSnrRaw: 20,
|
|
);
|
|
|
|
await tester.pumpWidget(_buildApp(harness, message));
|
|
await tester.pump(const Duration(milliseconds: 60));
|
|
|
|
expect(find.text('x2'), findsOneWidget);
|
|
expect(find.text('-76'), findsOneWidget);
|
|
expect(find.text('5.0'), findsOneWidget);
|
|
} finally {
|
|
await _disposeHarness(tester, harness);
|
|
}
|
|
});
|
|
}
|
|
|
|
Widget _buildApp(_TestHarness harness, Message message) {
|
|
return MultiProvider(
|
|
providers: [
|
|
ChangeNotifierProvider.value(value: harness.connectionProvider),
|
|
ChangeNotifierProvider.value(value: harness.contactsProvider),
|
|
ChangeNotifierProvider.value(value: harness.messagesProvider),
|
|
ChangeNotifierProvider.value(value: harness.drawingProvider),
|
|
ChangeNotifierProvider.value(value: harness.channelsProvider),
|
|
ChangeNotifierProvider.value(value: harness.voiceProvider),
|
|
ChangeNotifierProvider.value(value: harness.imageProvider),
|
|
ChangeNotifierProvider.value(value: harness.appProvider),
|
|
],
|
|
child: MaterialApp(
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
home: Scaffold(body: MessageBubble(message: message)),
|
|
),
|
|
);
|
|
}
|
|
|
|
class _TestHarness {
|
|
final connectionProvider = ConnectionProvider();
|
|
final contactsProvider = ContactsProvider();
|
|
final messagesProvider = MessagesProvider();
|
|
final drawingProvider = DrawingProvider();
|
|
final channelsProvider = ChannelsProvider()..initializePublicChannel();
|
|
final voiceProvider = VoiceProvider(
|
|
codec: VoiceCodecService(),
|
|
player: VoicePlayerService(),
|
|
);
|
|
final imageProvider = ip.ImageProvider();
|
|
|
|
late final AppProvider appProvider;
|
|
|
|
static Future<_TestHarness> create() async {
|
|
final harness = _TestHarness();
|
|
await harness.messagesProvider.initialize();
|
|
await harness.drawingProvider.initialize();
|
|
harness.appProvider = AppProvider(
|
|
connectionProvider: harness.connectionProvider,
|
|
contactsProvider: harness.contactsProvider,
|
|
messagesProvider: harness.messagesProvider,
|
|
drawingProvider: harness.drawingProvider,
|
|
channelsProvider: harness.channelsProvider,
|
|
voiceProvider: harness.voiceProvider,
|
|
imageProvider: harness.imageProvider,
|
|
);
|
|
return harness;
|
|
}
|
|
|
|
bool _isDisposed = false;
|
|
|
|
void dispose() {
|
|
if (_isDisposed) {
|
|
return;
|
|
}
|
|
_isDisposed = true;
|
|
appProvider.dispose();
|
|
voiceProvider.dispose();
|
|
imageProvider.dispose();
|
|
drawingProvider.dispose();
|
|
messagesProvider.dispose();
|
|
contactsProvider.dispose();
|
|
connectionProvider.dispose();
|
|
channelsProvider.dispose();
|
|
}
|
|
}
|
|
|
|
Uint8List _prefix(int seed) =>
|
|
Uint8List.fromList(List<int>.generate(6, (index) => seed + index));
|
|
|
|
Uint8List _key(int seed) =>
|
|
Uint8List.fromList(List<int>.generate(32, (index) => seed + index));
|
|
|
|
Future<void> _disposeHarness(WidgetTester tester, _TestHarness harness) async {
|
|
await tester.pumpWidget(const SizedBox.shrink());
|
|
harness.dispose();
|
|
await tester.pump();
|
|
}
|