tests and mocks for e2e testing (#78)
* tests and mocks for e2e testing * formatting * fix analyze errors
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
@Timeout(Duration(seconds: 5))
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
|
||||
import '../mock/e2e_container.dart';
|
||||
import '../mock/test_data.dart';
|
||||
import '../mock/websocket_mock.dart';
|
||||
import 'signal_client_test.dart';
|
||||
|
||||
void main() {
|
||||
late E2EContainer container;
|
||||
late Room room;
|
||||
late MockWebSocketConnector ws;
|
||||
setUp(() async {
|
||||
container = E2EContainer();
|
||||
room = container.room;
|
||||
ws = container.wsConnector;
|
||||
await container.connectRoom();
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await container.dispose();
|
||||
});
|
||||
|
||||
group('connection', () {
|
||||
test('disconnect and connect', () async {
|
||||
await room.disconnect();
|
||||
await container.connectRoom();
|
||||
expect(room.connectionState, ConnectionState.connected);
|
||||
expect(room.localParticipant?.sid, joinResponse.join.participant.sid);
|
||||
}, skip: 'todo');
|
||||
});
|
||||
|
||||
group('room updates', () {
|
||||
test('participant join', () async {
|
||||
expect(
|
||||
room.events.streamCtrl.stream,
|
||||
emits(predicate<ParticipantConnectedEvent>(
|
||||
(event) => event.participant.sid == remoteParticipantData.sid,
|
||||
)),
|
||||
);
|
||||
ws.onData(participantJoinResponse.writeToBuffer());
|
||||
|
||||
await room.events.waitFor<ParticipantConnectedEvent>(
|
||||
duration: const Duration(seconds: 1));
|
||||
expect(room.participants.length, 1);
|
||||
});
|
||||
|
||||
test('participant disconnect', () async {
|
||||
ws.onData(participantJoinResponse.writeToBuffer());
|
||||
await room.events.waitFor<ParticipantConnectedEvent>(
|
||||
duration: const Duration(seconds: 1));
|
||||
|
||||
ws.onData(participantDisconnectResponse.writeToBuffer());
|
||||
expect(
|
||||
room.events.streamCtrl.stream,
|
||||
emitsInOrder(<Matcher>[
|
||||
predicate<TrackUnpublishedEvent>(
|
||||
(event) => event.participant.sid == remoteParticipantData.sid),
|
||||
predicate<ParticipantDisconnectedEvent>(
|
||||
(event) => event.participant.sid == remoteParticipantData.sid),
|
||||
]),
|
||||
);
|
||||
|
||||
await room.events.waitFor<ParticipantDisconnectedEvent>(
|
||||
duration: const Duration(seconds: 1));
|
||||
expect(room.participants.length, 0);
|
||||
});
|
||||
|
||||
test('participant metadata changed', () async {
|
||||
ws.onData(participantJoinResponse.writeToBuffer());
|
||||
await room.events.waitFor<ParticipantConnectedEvent>(
|
||||
duration: const Duration(seconds: 1));
|
||||
|
||||
ws.onData(participantMetadataChangedResponse.writeToBuffer());
|
||||
expect(
|
||||
room.events.streamCtrl.stream,
|
||||
emits(
|
||||
predicate<ParticipantMetadataUpdatedEvent>((event) =>
|
||||
event.participant.metadata ==
|
||||
participantMetadataChangedResponse
|
||||
.update.participants[0].metadata),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('room update', () async {
|
||||
ws.onData(roomUpdateResponse.writeToBuffer());
|
||||
// TODO: room update event and handling
|
||||
}, skip: 'todo');
|
||||
|
||||
test('connection quality', () async {
|
||||
expect(
|
||||
room.events.streamCtrl.stream,
|
||||
emits(predicate<ParticipantConnectionQualityUpdatedEvent>((event) =>
|
||||
event.participant.sid == localParticipantData.sid &&
|
||||
event.connectionQuality == ConnectionQuality.excellent)),
|
||||
);
|
||||
ws.onData(connectionQualityResponse.writeToBuffer());
|
||||
});
|
||||
|
||||
test('active speakers changed', () async {
|
||||
ws.onData(participantJoinResponse.writeToBuffer());
|
||||
await room.events.waitFor<ParticipantConnectedEvent>(
|
||||
duration: const Duration(seconds: 1));
|
||||
|
||||
expect(
|
||||
room.events.streamCtrl.stream,
|
||||
emits(
|
||||
predicate<ActiveSpeakersChangedEvent>(
|
||||
(event) => event.speakers[0].sid == remoteParticipantData.sid),
|
||||
),
|
||||
);
|
||||
ws.onData(activeSpeakerResponse.writeToBuffer());
|
||||
});
|
||||
|
||||
test('leave', () async {
|
||||
expect(
|
||||
room.events.streamCtrl.stream,
|
||||
emits(predicate<RoomDisconnectedEvent>((event) =>
|
||||
room.connectionState == ConnectionState.disconnected)));
|
||||
ws.onData(leaveResponse.writeToBuffer());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
@Timeout(Duration(seconds: 5))
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
import 'package:livekit_client/src/core/signal_client.dart';
|
||||
import 'package:livekit_client/src/internal/events.dart';
|
||||
import 'package:livekit_client/src/proto/livekit_models.pb.dart' as lk_models;
|
||||
import 'package:livekit_client/src/proto/livekit_rtc.pb.dart' as lk_rtc;
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
|
||||
import '../mock/test_data.dart';
|
||||
import '../mock/websocket_mock.dart';
|
||||
|
||||
void main() {
|
||||
late SignalClient client;
|
||||
late MockWebSocketConnector connector;
|
||||
setUp(() async {
|
||||
connector = MockWebSocketConnector();
|
||||
client = SignalClient(connector.connect);
|
||||
});
|
||||
|
||||
group('connection', () {
|
||||
test('connect', () async {
|
||||
expect(
|
||||
client.events.streamCtrl.stream,
|
||||
emitsInOrder(<Matcher>[
|
||||
predicate<SignalConnectionStateUpdatedEvent>(
|
||||
(event) => event.connectionState == ConnectionState.connecting),
|
||||
predicate<SignalConnectionStateUpdatedEvent>(
|
||||
(event) => event.connectionState == ConnectionState.connected),
|
||||
]));
|
||||
await client.connect(exampleUri, token);
|
||||
});
|
||||
test('reconnect', () async {
|
||||
expect(
|
||||
client.events.streamCtrl.stream,
|
||||
emitsInOrder(<Matcher>[
|
||||
predicate<SignalConnectionStateUpdatedEvent>((event) =>
|
||||
event.connectionState == ConnectionState.reconnecting),
|
||||
predicate<SignalConnectionStateUpdatedEvent>((event) =>
|
||||
event.connectionState == ConnectionState.connected &&
|
||||
event.didReconnect == true),
|
||||
]));
|
||||
await client.connect(exampleUri, token, reconnect: true);
|
||||
});
|
||||
});
|
||||
|
||||
group('messaging', () {
|
||||
test('join', () async {
|
||||
await client.connect(exampleUri, token);
|
||||
expect(client.events.streamCtrl.stream,
|
||||
emits(isA<SignalJoinResponseEvent>()));
|
||||
connector.handlers?.onData!(joinResponse.writeToBuffer());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
final lk_rtc.SignalResponse joinResponse = lk_rtc.SignalResponse(
|
||||
join: lk_rtc.JoinResponse(
|
||||
room: lk_models.Room(
|
||||
name: 'room_name',
|
||||
sid: 'room_sid',
|
||||
),
|
||||
participant: localParticipantData,
|
||||
subscriberPrimary: true,
|
||||
serverVersion: '99.999',
|
||||
),
|
||||
);
|
||||
|
||||
final lk_rtc.SignalResponse offerResponse = lk_rtc.SignalResponse(
|
||||
offer: lk_rtc.SessionDescription(
|
||||
sdp: 'remote_offer',
|
||||
type: 'offer',
|
||||
));
|
||||
|
||||
final lk_rtc.SignalResponse participantJoinResponse = lk_rtc.SignalResponse(
|
||||
update: lk_rtc.ParticipantUpdate(
|
||||
participants: [remoteParticipantData],
|
||||
),
|
||||
);
|
||||
|
||||
final lk_rtc.SignalResponse participantDisconnectResponse =
|
||||
lk_rtc.SignalResponse(
|
||||
update: lk_rtc.ParticipantUpdate(
|
||||
participants: [
|
||||
remoteParticipantData.deepCopy()
|
||||
..state = lk_models.ParticipantInfo_State.DISCONNECTED,
|
||||
],
|
||||
),
|
||||
);
|
||||
final lk_rtc.SignalResponse participantMetadataChangedResponse =
|
||||
lk_rtc.SignalResponse(
|
||||
update: lk_rtc.ParticipantUpdate(
|
||||
participants: [
|
||||
remoteParticipantData.deepCopy()..metadata = 'metadata_changed',
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final lk_rtc.SignalResponse roomUpdateResponse = lk_rtc.SignalResponse(
|
||||
roomUpdate: lk_rtc.RoomUpdate(
|
||||
room: lk_models.Room(
|
||||
metadata: 'changed_metadata',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final lk_rtc.SignalResponse connectionQualityResponse = lk_rtc.SignalResponse(
|
||||
connectionQuality: lk_rtc.ConnectionQualityUpdate(updates: [
|
||||
lk_rtc.ConnectionQualityInfo(
|
||||
participantSid: localParticipantData.sid,
|
||||
quality: lk_models.ConnectionQuality.EXCELLENT,
|
||||
)
|
||||
]),
|
||||
);
|
||||
|
||||
final lk_rtc.SignalResponse activeSpeakerResponse = lk_rtc.SignalResponse(
|
||||
speakersChanged: lk_rtc.SpeakersChanged(
|
||||
speakers: [remoteSpeakerInfo],
|
||||
),
|
||||
);
|
||||
final lk_rtc.SignalResponse leaveResponse =
|
||||
lk_rtc.SignalResponse(leave: lk_rtc.LeaveRequest());
|
||||
const exampleUri = 'ws://www.example.com';
|
||||
const token = 'token';
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
|
||||
class MockDataChannel extends RTCDataChannel {
|
||||
final String? _label;
|
||||
RTCDataChannelState? _state = RTCDataChannelState.RTCDataChannelOpen;
|
||||
|
||||
MockDataChannel(this._label);
|
||||
|
||||
@override
|
||||
String? get label => _label;
|
||||
|
||||
@override
|
||||
Future<void> send(RTCDataChannelMessage message) async {}
|
||||
|
||||
@override
|
||||
RTCDataChannelState? get state => _state;
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
_state = RTCDataChannelState.RTCDataChannelClosing;
|
||||
_state = RTCDataChannelState.RTCDataChannelClosed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
import 'package:livekit_client/src/core/engine.dart';
|
||||
import 'package:livekit_client/src/core/signal_client.dart';
|
||||
|
||||
import '../core/signal_client_test.dart';
|
||||
import 'peerconnection_mock.dart';
|
||||
import 'websocket_mock.dart';
|
||||
|
||||
class E2EContainer {
|
||||
late MockWebSocketConnector wsConnector;
|
||||
late SignalClient client;
|
||||
late Room room;
|
||||
late Engine engine;
|
||||
|
||||
E2EContainer() {
|
||||
wsConnector = MockWebSocketConnector();
|
||||
client = SignalClient(wsConnector.connect);
|
||||
engine = Engine(
|
||||
signalClient: client, peerConnectionCreate: MockPeerConnection.create);
|
||||
room = Room(engine: engine);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await room.dispose();
|
||||
}
|
||||
|
||||
Future<void> connectRoom() async {
|
||||
final connectFuture = room.connect(exampleUri, token);
|
||||
Future.delayed(const Duration(milliseconds: 1), () {
|
||||
wsConnector.onData(joinResponse.writeToBuffer());
|
||||
wsConnector.onData(offerResponse.writeToBuffer());
|
||||
});
|
||||
await connectFuture;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
@Timeout(Duration(seconds: 5))
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
|
||||
import '../core/signal_client_test.dart';
|
||||
import '../mock/e2e_container.dart';
|
||||
|
||||
void main() {
|
||||
late E2EContainer container;
|
||||
late Room room;
|
||||
setUp(() async {
|
||||
container = E2EContainer();
|
||||
room = container.room;
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await container.dispose();
|
||||
});
|
||||
|
||||
test('connect', () async {
|
||||
await container.connectRoom();
|
||||
expect(room.connectionState, ConnectionState.connected);
|
||||
expect(room.localParticipant?.sid, joinResponse.join.participant.sid);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
|
||||
import 'datachannel_mock.dart';
|
||||
|
||||
class MockPeerConnection extends RTCPeerConnection {
|
||||
static const _offerType = 'offer';
|
||||
static const _answerType = 'answer';
|
||||
|
||||
bool closed = false;
|
||||
RTCSessionDescription? _localDescription;
|
||||
RTCSessionDescription? _remoteDescription;
|
||||
|
||||
RTCPeerConnectionState _connectionState =
|
||||
RTCPeerConnectionState.RTCPeerConnectionStateNew;
|
||||
RTCIceConnectionState _iceConnectionState =
|
||||
RTCIceConnectionState.RTCIceConnectionStateNew;
|
||||
RTCIceGatheringState _iceGatheringState =
|
||||
RTCIceGatheringState.RTCIceGatheringStateNew;
|
||||
|
||||
@override
|
||||
Future<RTCSessionDescription?> getLocalDescription() async =>
|
||||
_localDescription;
|
||||
|
||||
@override
|
||||
Future<void> setLocalDescription(RTCSessionDescription description) async {
|
||||
_localDescription = description;
|
||||
_handleIceConnection();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RTCSessionDescription?> getRemoteDescription() async =>
|
||||
_remoteDescription;
|
||||
|
||||
@override
|
||||
Future<void> setRemoteDescription(RTCSessionDescription description) async {
|
||||
_remoteDescription = description;
|
||||
_handleIceConnection();
|
||||
}
|
||||
|
||||
void _handleIceConnection() {
|
||||
if ((_localDescription?.type == _offerType &&
|
||||
_remoteDescription?.type == _answerType) ||
|
||||
(_localDescription?.type == _answerType &&
|
||||
_remoteDescription?.type == _offerType)) {
|
||||
iceConnectionState = RTCIceConnectionState.RTCIceConnectionStateCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
RTCPeerConnectionState get connectionState => _connectionState;
|
||||
|
||||
set connectionState(RTCPeerConnectionState newState) {
|
||||
if (newState != _connectionState) {
|
||||
_connectionState = newState;
|
||||
onConnectionState?.call(newState);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
RTCIceConnectionState get iceConnectionState => _iceConnectionState;
|
||||
|
||||
set iceConnectionState(RTCIceConnectionState newState) {
|
||||
if (newState != _iceConnectionState) {
|
||||
_iceConnectionState = newState;
|
||||
onIceConnectionState?.call(newState);
|
||||
|
||||
switch (newState) {
|
||||
case RTCIceConnectionState.RTCIceConnectionStateNew:
|
||||
connectionState = RTCPeerConnectionState.RTCPeerConnectionStateNew;
|
||||
break;
|
||||
case RTCIceConnectionState.RTCIceConnectionStateChecking:
|
||||
connectionState =
|
||||
RTCPeerConnectionState.RTCPeerConnectionStateConnecting;
|
||||
break;
|
||||
case RTCIceConnectionState.RTCIceConnectionStateConnected:
|
||||
case RTCIceConnectionState.RTCIceConnectionStateCompleted:
|
||||
connectionState =
|
||||
RTCPeerConnectionState.RTCPeerConnectionStateConnected;
|
||||
break;
|
||||
case RTCIceConnectionState.RTCIceConnectionStateFailed:
|
||||
connectionState = RTCPeerConnectionState.RTCPeerConnectionStateFailed;
|
||||
break;
|
||||
case RTCIceConnectionState.RTCIceConnectionStateDisconnected:
|
||||
connectionState =
|
||||
RTCPeerConnectionState.RTCPeerConnectionStateDisconnected;
|
||||
break;
|
||||
case RTCIceConnectionState.RTCIceConnectionStateClosed:
|
||||
connectionState = RTCPeerConnectionState.RTCPeerConnectionStateClosed;
|
||||
break;
|
||||
case RTCIceConnectionState.RTCIceConnectionStateCount:
|
||||
throw Exception("This state shouldn't exist (not in RFC).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
RTCIceGatheringState get iceGatheringState => _iceGatheringState;
|
||||
|
||||
set iceGatheringState(RTCIceGatheringState newState) {
|
||||
if (newState != _iceGatheringState) {
|
||||
_iceGatheringState = newState;
|
||||
onIceGatheringState?.call(newState);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addCandidate(RTCIceCandidate candidate) async {}
|
||||
|
||||
@override
|
||||
Future<void> addStream(MediaStream stream) async {}
|
||||
|
||||
@override
|
||||
Future<RTCRtpSender> addTrack(MediaStreamTrack track,
|
||||
[MediaStream? stream]) async {
|
||||
// TODO: implement addTrack
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RTCRtpTransceiver> addTransceiver(
|
||||
{MediaStreamTrack? track,
|
||||
RTCRtpMediaType? kind,
|
||||
RTCRtpTransceiverInit? init}) {
|
||||
// TODO: implement addTransceiver
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RTCSessionDescription> createAnswer(
|
||||
[Map<String, dynamic>? constraints]) async {
|
||||
return RTCSessionDescription('local_answer', 'answer');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RTCSessionDescription> createOffer(
|
||||
[Map<String, dynamic>? constraints]) async {
|
||||
return RTCSessionDescription('local_offer', 'offer');
|
||||
}
|
||||
|
||||
@override
|
||||
RTCSignalingState? get signalingState {
|
||||
if (closed) {
|
||||
return RTCSignalingState.RTCSignalingStateClosed;
|
||||
}
|
||||
|
||||
if ((_localDescription?.type == null && _remoteDescription?.type == null) ||
|
||||
(_localDescription?.type == _offerType &&
|
||||
_remoteDescription?.type == _answerType) ||
|
||||
(_localDescription?.type == _answerType &&
|
||||
_remoteDescription?.type == _offerType)) {
|
||||
return RTCSignalingState.RTCSignalingStateStable;
|
||||
}
|
||||
|
||||
if (_localDescription?.type == _offerType &&
|
||||
_remoteDescription?.type == null) {
|
||||
return RTCSignalingState.RTCSignalingStateHaveLocalOffer;
|
||||
}
|
||||
if (_remoteDescription?.type == _offerType &&
|
||||
_localDescription?.type == null) {
|
||||
return RTCSignalingState.RTCSignalingStateHaveRemoteOffer;
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
'Illegal signalling state? localDesc: $_localDescription, remoteDesc: $_remoteDescription');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RTCDataChannel> createDataChannel(
|
||||
String label, RTCDataChannelInit dataChannelDict) async {
|
||||
return MockDataChannel(label);
|
||||
}
|
||||
|
||||
@override
|
||||
RTCDTMFSender createDtmfSender(MediaStreamTrack track) {
|
||||
// TODO: implement createDtmfSender
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
await close();
|
||||
}
|
||||
|
||||
@override
|
||||
// TODO: implement getConfiguration
|
||||
Map<String, dynamic> get getConfiguration => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
List<MediaStream?> getLocalStreams() => List.empty();
|
||||
|
||||
@override
|
||||
Future<List<RTCRtpReceiver>> getReceivers() async => List.empty();
|
||||
|
||||
@override
|
||||
List<MediaStream?> getRemoteStreams() => List.empty();
|
||||
|
||||
@override
|
||||
Future<List<RTCRtpSender>> getSenders() async => List.empty();
|
||||
|
||||
@override
|
||||
Future<List<StatsReport>> getStats([MediaStreamTrack? track]) async =>
|
||||
List.empty();
|
||||
|
||||
@override
|
||||
Future<List<RTCRtpTransceiver>> getTransceivers() async => List.empty();
|
||||
|
||||
@override
|
||||
Future<void> removeStream(MediaStream stream) async {}
|
||||
|
||||
@override
|
||||
Future<bool> removeTrack(RTCRtpSender sender) async => true;
|
||||
|
||||
@override
|
||||
Future<void> setConfiguration(Map<String, dynamic> configuration) {
|
||||
// TODO: implement setConfiguration
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
static Future<RTCPeerConnection> create(Map<String, dynamic> configuration,
|
||||
[Map<String, dynamic>? constraints]) async =>
|
||||
MockPeerConnection();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
import 'package:livekit_client/src/proto/livekit_models.pb.dart' as lk_models;
|
||||
|
||||
final localAudioTrack = lk_models.TrackInfo(
|
||||
sid: 'local_audio_track_sid',
|
||||
type: TrackType.AUDIO,
|
||||
);
|
||||
|
||||
final remoteAudioTrack = lk_models.TrackInfo(
|
||||
sid: 'remote_audio_track_sid',
|
||||
type: TrackType.AUDIO,
|
||||
);
|
||||
|
||||
final localParticipantData = lk_models.ParticipantInfo(
|
||||
sid: 'local_participant_sid',
|
||||
identity: 'local_participant_identity',
|
||||
state: lk_models.ParticipantInfo_State.ACTIVE,
|
||||
);
|
||||
|
||||
final remoteParticipantData = lk_models.ParticipantInfo(
|
||||
sid: 'remote_participant_sid',
|
||||
identity: 'remote_participant_identity',
|
||||
state: lk_models.ParticipantInfo_State.ACTIVE,
|
||||
tracks: [remoteAudioTrack],
|
||||
);
|
||||
|
||||
final remoteSpeakerInfo = lk_models.SpeakerInfo(
|
||||
sid: remoteParticipantData.sid,
|
||||
level: 1.0,
|
||||
active: true,
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'package:livekit_client/src/support/websocket.dart';
|
||||
|
||||
class MockWebSocket extends LiveKitWebSocket {
|
||||
@override
|
||||
void send(List<int> data) {}
|
||||
}
|
||||
|
||||
class MockWebSocketConnector {
|
||||
WebSocketEventHandlers? handlers;
|
||||
|
||||
WebSocketOnData get onData => handlers!.onData!;
|
||||
|
||||
WebSocketOnDispose get onDispose => handlers!.onDispose!;
|
||||
|
||||
WebSocketOnError get onError => handlers!.onError!;
|
||||
|
||||
Future<LiveKitWebSocket> connect(Uri uri,
|
||||
[WebSocketEventHandlers? options]) async {
|
||||
handlers = options;
|
||||
return MockWebSocket();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user