yumi-flutter/test/room_seat_widget_test.dart
2026-06-02 19:57:41 +08:00

286 lines
9.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:yumi/app/constants/sc_room_msg_type.dart';
import 'package:yumi/modules/room/seat/sc_seat_item.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/shared/business_logic/models/res/join_room_res.dart';
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
import 'package:yumi/shared/business_logic/models/res/mic_res.dart';
import 'package:yumi/shared/business_logic/models/res/room_res.dart';
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
import 'package:yumi/ui_kit/widgets/room/seat/room_seat_widget.dart';
void main() {
test('starPlayEmoji creates a new event for repeated same emoji path', () {
final rtcProvider =
RealTimeCommunicationManager()
..roomWheatMap = {
1: MicRes(micIndex: 1, user: SocialChatUserProfile(id: 'user-1')),
};
final msg = Msg(
groupId: 'room-1',
msg: 'https://cdn.example.com/emoji/fluent_emoji_01.webp',
type: SCRoomMsgType.emoticons,
number: 1,
);
rtcProvider.starPlayEmoji(msg);
final firstVersion = rtcProvider.emojiEventVersionForSeat(1);
expect(firstVersion, greaterThan(0));
expect(rtcProvider.roomWheatMap[1]?.emojiPath, msg.msg);
rtcProvider.consumeSeatEmojiEvent(1, firstVersion);
expect(rtcProvider.roomWheatMap[1]?.emojiPath, isNull);
rtcProvider.starPlayEmoji(msg);
final secondVersion = rtcProvider.emojiEventVersionForSeat(1);
expect(secondVersion, greaterThan(firstVersion));
expect(rtcProvider.roomWheatMap[1]?.emojiPath, msg.msg);
});
testWidgets(
'RoomSeatWidget falls back to configured mikeSize when mic list is empty',
(tester) async {
await tester.binding.setSurfaceSize(const Size(1600, 1200));
addTearDown(() async {
await tester.binding.setSurfaceSize(null);
});
final rtcProvider =
RealTimeCommunicationManager()
..currenRoom = JoinRoomRes(
roomProfile: RoomProfile(roomSetting: RoomSetting(mikeSize: 10)),
);
await tester.pumpWidget(
ScreenUtilInit(
designSize: const Size(375, 812),
builder:
(_, __) =>
ChangeNotifierProvider<RealTimeCommunicationManager>.value(
value: rtcProvider,
child: MaterialApp(home: Scaffold(body: RoomSeatWidget())),
),
),
);
expect(find.byType(RoomSeatWidget), findsOneWidget);
expect(find.byType(SCSeatItem), findsNWidgets(10));
},
);
testWidgets(
'RoomSeatWidget uses configured mikeSize after expanding the room model',
(tester) async {
await tester.binding.setSurfaceSize(const Size(1600, 1200));
addTearDown(() async {
await tester.binding.setSurfaceSize(null);
});
final rtcProvider =
RealTimeCommunicationManager()
..currenRoom = JoinRoomRes(
roomProfile: RoomProfile(roomSetting: RoomSetting(mikeSize: 20)),
)
..roomWheatMap = {
0: MicRes(micIndex: 0, user: SocialChatUserProfile(id: 'user-1')),
1: MicRes(micIndex: 1, user: SocialChatUserProfile(id: 'user-2')),
2: MicRes(micIndex: 2),
3: MicRes(micIndex: 3),
4: MicRes(micIndex: 4),
};
await tester.pumpWidget(
ScreenUtilInit(
designSize: const Size(375, 812),
builder:
(_, __) =>
ChangeNotifierProvider<RealTimeCommunicationManager>.value(
value: rtcProvider,
child: MaterialApp(home: Scaffold(body: RoomSeatWidget())),
),
),
);
expect(find.byType(SCSeatItem), findsNWidgets(20));
},
);
test(
'updateRoomSetting expands seat placeholders without clearing users',
() {
final rtcProvider =
RealTimeCommunicationManager()
..currenRoom = JoinRoomRes(
roomProfile: RoomProfile(
roomSetting: RoomSetting(mikeSize: 5),
roomProfile: RoomProfile2(id: 'room-1'),
),
)
..roomWheatMap = {
0: MicRes(micIndex: 0, user: SocialChatUserProfile(id: 'user-1')),
1: MicRes(micIndex: 1, user: SocialChatUserProfile(id: 'user-2')),
};
rtcProvider.updateRoomSetting(RoomSetting(mikeSize: 20));
expect(rtcProvider.roomWheatMap[0]?.user?.id, 'user-1');
expect(rtcProvider.roomWheatMap[1]?.user?.id, 'user-2');
expect(rtcProvider.roomWheatMap[19]?.micIndex, 19);
},
);
test('micChange keeps a recent up-mic snapshot over stale mic list data', () {
final rtcProvider = RealTimeCommunicationManager();
final user = SocialChatUserProfile(id: 'user-1', userNickname: 'Alice');
rtcProvider.micChange([MicRes(micIndex: 0, user: user)]);
expect(rtcProvider.roomWheatMap[0]?.user?.id, 'user-1');
rtcProvider.debugApplyMicListSnapshot([
MicRes(micIndex: 0),
], notifyIfUnchanged: false);
expect(rtcProvider.roomWheatMap[0]?.user?.id, 'user-1');
});
test(
'micChange keeps a recent down-mic snapshot over stale mic list data',
() {
final rtcProvider =
RealTimeCommunicationManager()
..roomWheatMap = {
0: MicRes(
micIndex: 0,
user: SocialChatUserProfile(
id: 'user-1',
userNickname: 'Alice',
),
),
};
rtcProvider.micChange([MicRes(micIndex: 0)]);
expect(rtcProvider.roomWheatMap[0]?.user, isNull);
rtcProvider.debugApplyMicListSnapshot([
MicRes(
micIndex: 0,
user: SocialChatUserProfile(id: 'user-1', userNickname: 'Alice'),
),
], notifyIfUnchanged: false);
expect(rtcProvider.roomWheatMap[0]?.user, isNull);
},
);
testWidgets('RoomSeatWidget derives seat count from highest mic index', (
tester,
) async {
await tester.binding.setSurfaceSize(const Size(1600, 1200));
addTearDown(() async {
await tester.binding.setSurfaceSize(null);
});
final rtcProvider =
RealTimeCommunicationManager()
..currenRoom = JoinRoomRes(roomProfile: RoomProfile())
..roomWheatMap = {19: MicRes(micIndex: 19)};
await tester.pumpWidget(
ScreenUtilInit(
designSize: const Size(375, 812),
builder:
(_, __) =>
ChangeNotifierProvider<RealTimeCommunicationManager>.value(
value: rtcProvider,
child: MaterialApp(home: Scaffold(body: RoomSeatWidget())),
),
),
);
expect(find.byType(SCSeatItem), findsNWidgets(20));
});
testWidgets(
'RoomSeatWidget keeps default room seats visible while room data catches up',
(tester) async {
await tester.binding.setSurfaceSize(const Size(1600, 1200));
addTearDown(() async {
await tester.binding.setSurfaceSize(null);
});
final rtcProvider =
RealTimeCommunicationManager()
..currenRoom = JoinRoomRes(roomProfile: RoomProfile());
await tester.pumpWidget(
ScreenUtilInit(
designSize: const Size(375, 812),
builder:
(_, __) =>
ChangeNotifierProvider<RealTimeCommunicationManager>.value(
value: rtcProvider,
child: MaterialApp(home: Scaffold(body: RoomSeatWidget())),
),
),
);
expect(find.byType(SCSeatItem), findsNWidgets(10));
},
);
testWidgets('SCSeatItem keeps height stable when an empty seat gets a user', (
tester,
) async {
await tester.binding.setSurfaceSize(const Size(1600, 1200));
addTearDown(() async {
await tester.binding.setSurfaceSize(null);
});
final rtcProvider =
RealTimeCommunicationManager()
..currenRoom = JoinRoomRes(
roomProfile: RoomProfile(roomSetting: RoomSetting(mikeSize: 10)),
)
..roomWheatMap = {0: MicRes(micIndex: 0)};
Future<void> pumpSeat() {
return tester.pumpWidget(
ScreenUtilInit(
designSize: const Size(375, 812),
builder:
(_, __) =>
ChangeNotifierProvider<RealTimeCommunicationManager>.value(
value: rtcProvider,
child: const MaterialApp(
home: Scaffold(body: Center(child: SCSeatItem(index: 0))),
),
),
),
);
}
await pumpSeat();
final emptySeatSize = tester.getSize(find.byType(SCSeatItem));
rtcProvider.roomWheatMap = {
0: MicRes(
micIndex: 0,
user: SocialChatUserProfile(
id: 'user-1',
userAvatar: '',
userNickname: 'Alice',
heartbeatVal: 1234,
),
),
};
await pumpSeat();
expect(tester.getSize(find.byType(SCSeatItem)), emptySeatSize);
});
}