All checks were successful
Create Release / build (push) Successful in 6s
Server-side: - Add push credential auto-creation for FCM incoming call notifications - Add queue alert FCM notifications (data-only for background delivery) - Add queue alert cancellation on call accept/disconnect - Fix caller ID to show caller's number instead of Twilio number - Fix FCM token storage when refresh_token is null - Add pre_call_status tracking to revert agent status 30s after call ends - Add SSE fallback polling for mobile app connectivity Mobile app: - Add Android telecom permissions and phone account registration - Add VoiceFirebaseMessagingService for incoming call push handling - Add insistent queue alert notifications with custom sound - Fix caller number display on active call screen - Add caller ID selection dropdown on dashboard - Add phone numbers endpoint and provider support - Add unit tests for CallInfo, QueueState, and CallProvider - Remove local.properties from tracking, add .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
4.3 KiB
Dart
130 lines
4.3 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:twp_softphone/models/call_info.dart';
|
|
|
|
void main() {
|
|
group('CallInfo', () {
|
|
test('default state is idle', () {
|
|
const info = CallInfo();
|
|
expect(info.state, CallState.idle);
|
|
expect(info.callSid, isNull);
|
|
expect(info.callerNumber, isNull);
|
|
expect(info.duration, Duration.zero);
|
|
expect(info.isMuted, false);
|
|
expect(info.isSpeakerOn, false);
|
|
expect(info.isOnHold, false);
|
|
});
|
|
|
|
test('isActive returns true for ringing, connecting, connected', () {
|
|
expect(const CallInfo(state: CallState.ringing).isActive, true);
|
|
expect(const CallInfo(state: CallState.connecting).isActive, true);
|
|
expect(const CallInfo(state: CallState.connected).isActive, true);
|
|
});
|
|
|
|
test('isActive returns false for idle and disconnected', () {
|
|
expect(const CallInfo(state: CallState.idle).isActive, false);
|
|
expect(const CallInfo(state: CallState.disconnected).isActive, false);
|
|
});
|
|
|
|
test('copyWith preserves unmodified fields', () {
|
|
const original = CallInfo(
|
|
state: CallState.connected,
|
|
callSid: 'CA123',
|
|
callerNumber: '+1234567890',
|
|
isMuted: true,
|
|
);
|
|
|
|
final modified = original.copyWith(isSpeakerOn: true);
|
|
expect(modified.state, CallState.connected);
|
|
expect(modified.callSid, 'CA123');
|
|
expect(modified.callerNumber, '+1234567890');
|
|
expect(modified.isMuted, true);
|
|
expect(modified.isSpeakerOn, true);
|
|
});
|
|
|
|
test('copyWith can change state', () {
|
|
const info = CallInfo(state: CallState.connecting);
|
|
final updated = info.copyWith(state: CallState.connected);
|
|
expect(updated.state, CallState.connected);
|
|
});
|
|
|
|
test('copyWith with callerNumber preserves it', () {
|
|
const info = CallInfo(callerNumber: '+19095737372');
|
|
final updated = info.copyWith(state: CallState.connected);
|
|
expect(updated.callerNumber, '+19095737372');
|
|
});
|
|
|
|
test('reset to idle clears all fields', () {
|
|
// Verify a complex state exists
|
|
const connected = CallInfo(
|
|
state: CallState.connected,
|
|
callSid: 'CA123',
|
|
callerNumber: '+1234567890',
|
|
isMuted: true,
|
|
isSpeakerOn: true,
|
|
isOnHold: true,
|
|
duration: Duration(seconds: 30),
|
|
);
|
|
expect(connected.isActive, true);
|
|
|
|
// Simulating what callEnded does
|
|
const reset = CallInfo();
|
|
expect(reset.state, CallState.idle);
|
|
expect(reset.callSid, isNull);
|
|
expect(reset.callerNumber, isNull);
|
|
expect(reset.isActive, false);
|
|
});
|
|
});
|
|
|
|
group('CallState transitions', () {
|
|
test('outbound call flow: idle -> connecting -> connected -> idle', () {
|
|
var info = const CallInfo();
|
|
expect(info.state, CallState.idle);
|
|
|
|
// makeCall sets connecting + callerNumber
|
|
info = info.copyWith(state: CallState.connecting, callerNumber: '+19095737372');
|
|
expect(info.state, CallState.connecting);
|
|
expect(info.callerNumber, '+19095737372');
|
|
expect(info.isActive, true);
|
|
|
|
// SDK fires connected
|
|
info = info.copyWith(state: CallState.connected);
|
|
expect(info.state, CallState.connected);
|
|
expect(info.callerNumber, '+19095737372'); // preserved
|
|
expect(info.isActive, true);
|
|
|
|
// callEnded resets
|
|
info = const CallInfo();
|
|
expect(info.state, CallState.idle);
|
|
expect(info.isActive, false);
|
|
});
|
|
|
|
test('inbound call flow: idle -> ringing -> connected -> idle', () {
|
|
var info = const CallInfo();
|
|
|
|
info = info.copyWith(state: CallState.ringing);
|
|
expect(info.isActive, true);
|
|
|
|
// callerNumber set from active.from
|
|
info = info.copyWith(callerNumber: '+18005551234');
|
|
expect(info.callerNumber, '+18005551234');
|
|
|
|
info = info.copyWith(state: CallState.connected);
|
|
expect(info.state, CallState.connected);
|
|
|
|
info = const CallInfo();
|
|
expect(info.state, CallState.idle);
|
|
});
|
|
|
|
test('outbound callerNumber not overwritten by null copyWith', () {
|
|
var info = const CallInfo(
|
|
state: CallState.connecting,
|
|
callerNumber: '+19095737372',
|
|
);
|
|
|
|
// copyWith without callerNumber should preserve it
|
|
info = info.copyWith(state: CallState.connected);
|
|
expect(info.callerNumber, '+19095737372');
|
|
});
|
|
});
|
|
}
|