feat(video_calls): call ended push, recall button for message

This commit is contained in:
misha
2026-06-30 16:11:18 +03:00
parent c0c7e2e658
commit f493e07f9f
30 changed files with 671 additions and 83 deletions

View File

@@ -54,7 +54,7 @@ void main() {
isRead: false,
isCurrentUserAuthor: true,
roomId: 'room-1',
type: 'text',
type: ChatMessageType.text,
);
final json = model.toJson();

View File

@@ -48,7 +48,7 @@ void main() {
isRead: false,
isCurrentUserAuthor: true,
roomId: 'room-1',
type: 'text',
type: ChatMessageType.text,
);
const meta = ChatMetaModel(lastPage: 1);

View File

@@ -336,7 +336,7 @@ void main() {
isRead: false,
isCurrentUserAuthor: true,
roomId: chatId,
type: 'text',
type: ChatMessageType.text,
);
// Используем рефлексию для вызова приватного метода через тестовый доступ
@@ -390,7 +390,7 @@ void main() {
isRead: false,
isCurrentUserAuthor: true,
roomId: chatId,
type: 'text',
type: ChatMessageType.text,
);
final streamController = StreamController<ChatMessageModel>();

View File

@@ -112,4 +112,24 @@ void main() {
expect(restored, model);
});
});
group('VideoCallAcceptResponse', () {
test('fromJson десериализует sent и devices_notified', () {
final json = FixtureReader.readAsMap('video_calls/video_call_accept_response.json');
final model = VideoCallAcceptResponse.fromJson(json);
expect(model.sent, isTrue);
expect(model.devicesNotified, 2);
});
test('round-trip JSON сохраняет значения', () {
final json = FixtureReader.readAsMap('video_calls/video_call_accept_response.json');
final model = VideoCallAcceptResponse.fromJson(json);
final restored = VideoCallAcceptResponse.fromJson(model.toJson());
expect(restored, model);
});
});
}

View File

@@ -27,6 +27,9 @@ void main() {
managerListener =
invocation.positionalArguments[0] as VideoCallManagerListener;
});
when(
mockManager.notifyIncomingCallAccepted(any),
).thenAnswer((_) async {});
cubit = _TestVideoCallSessionCubit(videoCallManager: mockManager);
});

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/features/video_calls/services/video_call_ended_push_payload.dart';
import 'package:eatvio_app/features/video_calls/services/video_call_push_payload.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -40,14 +41,21 @@ void main() {
expect(payload.counterpartName, 'Ivan');
});
test('ignores ended video call push', () {
expect(
VideoCallPushPayload.tryParse({
'type': 'video_call_ended',
'conversation_uuid': 'abc-123',
}),
isNull,
);
test('parses ended video call push with type only', () {
final payload = VideoCallEndedPushPayload.tryParse({
'type': 'video_call_ended',
});
expect(payload, isNotNull);
});
test('parses ended video call push with optional conversation uuid', () {
final payload = VideoCallEndedPushPayload.tryParse({
'type': 'video_call_ended',
'conversation_uuid': 'abc-123',
});
expect(payload, isNotNull);
});
test('ignores regular chat push', () {

View File

@@ -90,5 +90,25 @@ void main() {
throwsA(isA<Exception>()),
);
});
test('notifyCallAccepted - отправляет hw_id и получает ответ', () async {
const conversationUuid = 'conv-uuid-123';
const body = <String, dynamic>{'hw_id': 'device-android-001'};
final json = FixtureReader.readAsMap('video_calls/video_call_accept_response.json');
final expected = VideoCallAcceptResponse.fromJson(json);
when(mockService.notifyCallAccepted(conversationUuid: conversationUuid, body: body))
.thenAnswer((_) async => expected);
final result = await mockService.notifyCallAccepted(
conversationUuid: conversationUuid,
body: body,
);
expect(result, isA<VideoCallAcceptResponse>());
expect(result.sent, isTrue);
expect(result.devicesNotified, 2);
verify(mockService.notifyCallAccepted(conversationUuid: conversationUuid, body: body)).called(1);
});
});
}

View File

@@ -1,6 +1,7 @@
import 'package:eatvio_app/features/video_calls/presentation/widgets/video_call_top_overlay.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_svg/flutter_svg.dart';
void main() {
testWidgets('insertBanner shows message and can be removed manually', (
@@ -67,4 +68,36 @@ void main() {
await tester.pump();
expect(find.text('Recording notice'), findsNothing);
});
testWidgets('showPoorConnectionIcon renders compact icon chip', (tester) async {
await tester.pumpWidget(
const MaterialApp(home: Scaffold(body: SizedBox.shrink())),
);
final context = tester.element(find.byType(SizedBox));
VideoCallTopOverlay.showPoorConnectionIcon(context);
await tester.pump();
expect(find.byType(SvgPicture), findsOneWidget);
expect(find.text('Poor connection'), findsNothing);
expect(VideoCallTopOverlay.isPoorConnectionIconVisible, isTrue);
});
testWidgets('hidePoorConnectionIcon removes poor connection chip', (
tester,
) async {
await tester.pumpWidget(
const MaterialApp(home: Scaffold(body: SizedBox.shrink())),
);
final context = tester.element(find.byType(SizedBox));
VideoCallTopOverlay.showPoorConnectionIcon(context);
await tester.pump();
expect(VideoCallTopOverlay.isPoorConnectionIconVisible, isTrue);
VideoCallTopOverlay.hidePoorConnectionIcon();
await tester.pump();
expect(VideoCallTopOverlay.isPoorConnectionIconVisible, isFalse);
expect(find.byType(SvgPicture), findsNothing);
});
}