import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:ikar/backend/api.dart'; import 'package:ikar/backend/modules/contacts.dart'; import 'package:ikar/core/config/countries.dart'; import 'package:ikar/core/protocol/opcode_map.dart'; import 'package:ikar/core/protocol/packet.dart'; import 'package:ikar/frontend/screens/auth/phone_input_formatter.dart'; class _ContactApi extends Api { _ContactApi(this.error); final PacketError error; Map? sent; @override Future sendRequest( int opcode, Map payload, { void Function()? beforeSend, bool silent = false, }) async { beforeSend?.call(); expect(opcode, Opcode.contactAddByPhone); sent = payload; throw error; } } String formattedDigits(String raw, {String country = 'RU'}) { final input = FilteringTextInputFormatter.digitsOnly.formatEditUpdate( TextEditingValue.empty, TextEditingValue(text: raw), ); return PhoneInputFormatter(countriesByCode[country]!) .formatEditUpdate(TextEditingValue.empty, input) .text .replaceAll(RegExp(r'\D'), ''); } void main() { TestWidgetsFlutterBinding.ensureInitialized(); for (final phone in [ '+7 (900) 000-00-01', '79000000001', '8 900 000 00 01', '9000000001', ]) { test('contact paste preserves subscriber digits: $phone', () async { final digits = formattedDigits(phone); expect(digits, '9000000001'); final api = _ContactApi( const PacketError('missing', errorKey: 'user.not.found'), ); addTearDown(api.dispose); final result = await ContactsModule.addContactByPhone( api, phone: '+7$digits', firstName: 'Test', ); expect(api.sent?['phone'], '+79000000001'); expect(result.status, AddContactStatus.notFound); }); } test('international paste removes selected calling code', () { expect(formattedDigits('+44 7700 900123', country: 'GB'), '7700900123'); }); for (final entry in { '8 (900) 000-00-01': '+79000000001', '9000000001': '+9000000001', '+44 7700 900123': '+447700900123', }.entries) { test('backend normalizes contact phone ${entry.key}', () async { final api = _ContactApi( const PacketError('missing', errorKey: 'user.not.found'), ); addTearDown(api.dispose); final result = await ContactsModule.addContactByPhone( api, phone: entry.key, firstName: 'Test', ); expect(api.sent?['phone'], entry.value); expect(result.status, AddContactStatus.notFound); }); } for (final phone in ['12345', '1234567890123456']) { test( 'invalid contact phone is rejected before requesting $phone', () async { final api = _ContactApi( const PacketError('missing', errorKey: 'user.not.found'), ); addTearDown(api.dispose); final result = await ContactsModule.addContactByPhone( api, phone: phone, firstName: 'Test', ); expect(api.sent, isNull); expect(result.status, AddContactStatus.error); }, ); } test('replacing a selected formatted number preserves its final digit', () { final formatter = PhoneInputFormatter(countriesByCode['RU']!); final formatted = formatter.formatEditUpdate( TextEditingValue.empty, const TextEditingValue(text: '9000000001'), ); final old = formatted.copyWith( selection: TextSelection( baseOffset: 0, extentOffset: formatted.text.length, ), ); final filtered = FilteringTextInputFormatter.digitsOnly.formatEditUpdate( old, const TextEditingValue(text: '+7 (900) 000-00-02'), ); expect( formatter .formatEditUpdate(old, filtered) .text .replaceAll(RegExp(r'\D'), ''), '9000000002', ); }); test('oversized invalid paste is rejected without truncation', () { expect(formattedDigits('123456789012345'), isEmpty); }); test('typing an extra digit does not reinterpret the completed number', () { final formatter = PhoneInputFormatter(countriesByCode['RU']!); final old = formatter.formatEditUpdate( TextEditingValue.empty, const TextEditingValue(text: '7900000001'), ); expect( formatter.formatEditUpdate(old, old.copyWith(text: '${old.text}2')), old, ); }); test( 'unrelated not found error is not reported as absent ИКАР user', () async { final api = _ContactApi( const PacketError('endpoint not found', errorKey: 'method.not.found'), ); addTearDown(api.dispose); final result = await ContactsModule.addContactByPhone( api, phone: '+79000000001', firstName: 'Test', ); expect(result.status, AddContactStatus.error); }, ); }