// // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import XCTest @testable import SignalServiceKit class PhoneNumberTest: XCTestCase { private var phoneNumberUtilRef: PhoneNumberUtil! override func setUp() { super.setUp() phoneNumberUtilRef = PhoneNumberUtil(swiftValues: PhoneNumberUtilSwiftValues()) } func testInitWithE164() { let shouldBeNil: [String] = ["", "+", "1", "19025550123", "+190255501238"] for input in shouldBeNil { XCTAssertNil(phoneNumberUtilRef.parseE164(input), input) } let shouldNotBeNil: [String] = ["+19025550123", "+33170393800"] for input in shouldNotBeNil { XCTAssertEqual(phoneNumberUtilRef.parseE164(input)?.toE164(), input, input) } } func testTryParsePhoneNumberTextOnly() { let testCases: [(inputValue: String, expectedValue: String?)] = [ // Phone numbers with explicit region codes ("+1 (902) 555-0123", "+19025550123"), ("1 (902) 555-0123", "+19025550123"), ("1-902-555-0123", "+19025550123"), ("1 902 555 0123", "+19025550123"), ("1.902.555.0123", "+19025550123"), ("+33 1 70 39 38 00", "+33170393800"), // Phone numbers missing a calling code. Assumes local region ("9025550123", "+19025550123"), ("902-555-0123", "+19025550123"), ("902.555.0123", "+19025550123"), ("902 555 0123", "+19025550123"), ("(902) 555-0123", "+19025550123"), // Phone numbers outside your region without a plus. // You must include a plus when dialing outside of your locale. // This might not be desired, but documents existing behavior. ("33 1 70 39 38 00", nil), // Phone numbers with a calling code but without a plus ("19025550123", "+19025550123"), // Empty input ("", nil), ] for (inputValue, expectedValue) in testCases { let actualValue = phoneNumberUtilRef.parsePhoneNumber(userSpecifiedText: inputValue)?.toE164() XCTAssertEqual(actualValue, expectedValue, inputValue) } } func testTryParsePhoneNumberWithCallingCode() { XCTAssertEqual( phoneNumberUtilRef.parsePhoneNumber(userSpecifiedText: "18085550101", callingCode: "1")?.toE164(), "+18085550101" ) XCTAssertEqual( phoneNumberUtilRef.parsePhoneNumber(userSpecifiedText: "61255504321", callingCode: "61")?.toE164(), "+61255504321" ) XCTAssertEqual( phoneNumberUtilRef.parsePhoneNumber(userSpecifiedText: "493083050", callingCode: "49")?.toE164(), "+493083050" ) } func testNationalNumber() throws { let testCases: [(inputValue: String, expectedValue: String)] = [ ("+19025550123", "9025550123"), ("+447700900123", "7700900123"), ("+33639981234", "639981234"), ] for testCase in testCases { let phoneNumber = try XCTUnwrap(phoneNumberUtilRef.parseE164(testCase.inputValue)) let actualValue = phoneNumberUtilRef.nationalNumber(for: phoneNumber) XCTAssertEqual(actualValue, testCase.expectedValue) } } func testTryParsePhoneNumbersFromUserSpecifiedText_SimpleUSA() { let expectedValue = "+13235551234" let inputValues = [ "323 555 1234", "323-555-1234", "323.555.1234", "1-323-555-1234", "+13235551234", ] for inputValue in inputValues { let phoneNumbers = phoneNumberUtilRef.parsePhoneNumbers(userSpecifiedText: inputValue, localPhoneNumber: "+13213214321") XCTAssertEqual(phoneNumbers.first?.toE164(), expectedValue, inputValue) } } func testMissingAreaCode_USA() { let localNumber = "+13233214321" let testCases: [(inputValue: String, expectedValue: String, isExpected: Bool)] = [ // Add area code to numbers that look like "local" numbers ("555-1234", "+13235551234", true), ("5551234", "+13235551234", true), ("555 1234", "+13235551234", true), // Discard numbers which libPhoneNumber considers "impossible", even if they have a leading "+" ("+5551234", "+5551234", false), // Don't infer area code when number already has one ("570 555 1234", "+15705551234", true), // Don't touch numbers that are already in e164 ("+33170393800", "+33170393800", true), ] for testCase in testCases { let phoneNumbers = phoneNumberUtilRef.parsePhoneNumbers( userSpecifiedText: testCase.inputValue, localPhoneNumber: localNumber ) XCTAssertEqual( phoneNumbers.contains(where: { $0.toE164() == testCase.expectedValue }), testCase.isExpected, testCase.inputValue ) } } func testMissingAreaCode_Brazil() { let localNumber = "+5521912345678" let testCases: [(inputValue: String, expectedValue: String, isExpected: Bool)] = [ // Add area code to land-line numbers that look like "local" numbers ("87654321", "+552187654321", true), ("8765-4321", "+552187654321", true), ("8765 4321", "+552187654321", true), // Add area code to mobile numbers that look like "local" numbers ("987654321", "+5521987654321", true), ("9 8765-4321", "+5521987654321", true), ("9 8765 4321", "+5521987654321", true), // Don't touch land-line numbers that already have an area code ("22 8765 4321", "+552287654321", true), // Don't touch mobile numbers that already have an area code ("22 9 8765 4321", "+5522987654321", true), // Don't touch numbers that are already in e164 ("+33170393800", "+33170393800", true), ] for testCase in testCases { let phoneNumbers = phoneNumberUtilRef.parsePhoneNumbers( userSpecifiedText: testCase.inputValue, localPhoneNumber: localNumber ) XCTAssertEqual( phoneNumbers.contains(where: { $0.toE164() == testCase.expectedValue }), testCase.isExpected, testCase.inputValue ) } } }