parse.test.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. import metadata from '../metadata.min.json' assert { type: 'json' }
  2. import _parseNumber from './parse.js'
  3. import Metadata from './metadata.js'
  4. function parseNumber(...parameters) {
  5. if (parameters.length < 2) {
  6. // `options` parameter.
  7. parameters.push(undefined)
  8. }
  9. // Convert default country argument to an `options` object.
  10. if (typeof parameters[1] === 'string') {
  11. parameters[1] = {
  12. ...parameters[2],
  13. defaultCountry: parameters[1]
  14. }
  15. }
  16. if (parameters[2]) {
  17. parameters[2] = metadata
  18. } else {
  19. parameters.push(metadata)
  20. }
  21. return _parseNumber.apply(this, parameters)
  22. }
  23. const USE_NON_GEOGRAPHIC_COUNTRY_CODE = false
  24. describe('parse', () => {
  25. it('should not parse invalid phone numbers', () => {
  26. // Too short.
  27. parseNumber('+7 (800) 55-35-35').should.deep.equal({})
  28. // Too long.
  29. parseNumber('+7 (800) 55-35-35-55').should.deep.equal({})
  30. parseNumber('+7 (800) 55-35-35', 'US').should.deep.equal({})
  31. parseNumber('(800) 55 35 35', { defaultCountry: 'RU' }).should.deep.equal({})
  32. parseNumber('+1 187 215 5230', 'US').should.deep.equal({})
  33. parseNumber('911231231', 'BE').should.deep.equal({})
  34. })
  35. it('should parse valid phone numbers', () => {
  36. // Instant loans
  37. // https://www.youtube.com/watch?v=6e1pMrYH5jI
  38. //
  39. // Restrict to RU
  40. parseNumber('Phone: 8 (800) 555 35 35.', 'RU').should.deep.equal({ country: 'RU', phone: '8005553535' })
  41. // International format
  42. parseNumber('Phone: +7 (800) 555-35-35.').should.deep.equal({ country: 'RU', phone: '8005553535' })
  43. // // Restrict to US, but not a US country phone code supplied
  44. // parseNumber('+7 (800) 555-35-35', 'US').should.deep.equal({})
  45. // Restrict to RU
  46. parseNumber('(800) 555 35 35', 'RU').should.deep.equal({ country: 'RU', phone: '8005553535' })
  47. // Default to RU
  48. parseNumber('8 (800) 555 35 35', { defaultCountry: 'RU' }).should.deep.equal({ country: 'RU', phone: '8005553535' })
  49. // Gangster partyline
  50. parseNumber('+1-213-373-4253').should.deep.equal({ country: 'US', phone: '2133734253' })
  51. // Switzerland (just in case)
  52. parseNumber('044 668 18 00', 'CH').should.deep.equal({ country: 'CH', phone: '446681800' })
  53. // China, Beijing
  54. parseNumber('010-852644821', 'CN').should.deep.equal({ country: 'CN', phone: '10852644821' })
  55. // France
  56. parseNumber('+33169454850').should.deep.equal({ country: 'FR', phone: '169454850' })
  57. // UK (Jersey)
  58. parseNumber('+44 7700 300000').should.deep.equal({ country: 'JE', phone: '7700300000' })
  59. // KZ
  60. parseNumber('+7 702 211 1111').should.deep.equal({ country: 'KZ', phone: '7022111111' })
  61. // Brazil
  62. parseNumber('11987654321', 'BR').should.deep.equal({ country: 'BR', phone: '11987654321' })
  63. // Long country phone code.
  64. parseNumber('+212659777777').should.deep.equal({ country: 'MA', phone: '659777777' })
  65. // No country could be derived.
  66. // parseNumber('+212569887076').should.deep.equal({ countryPhoneCode: '212', phone: '569887076' })
  67. // GB. Moible numbers starting 07624* are Isle of Man.
  68. parseNumber('07624369230', 'GB').should.deep.equal({ country: 'IM', phone: '7624369230' })
  69. })
  70. it('should parse possible numbers', () => {
  71. // Invalid phone number for a given country.
  72. parseNumber('1112223344', 'RU', { extended: true }).should.deep.equal({
  73. country : 'RU',
  74. countryCallingCode : '7',
  75. phone : '1112223344',
  76. carrierCode : undefined,
  77. ext : undefined,
  78. valid : false,
  79. possible : true
  80. })
  81. // International phone number.
  82. // Several countries with the same country phone code.
  83. parseNumber('+71112223344').should.deep.equal({})
  84. parseNumber('+71112223344', { extended: true }).should.deep.equal({
  85. country : undefined,
  86. countryCallingCode : '7',
  87. phone : '1112223344',
  88. carrierCode : undefined,
  89. ext : undefined,
  90. valid : false,
  91. possible : true
  92. })
  93. // International phone number.
  94. // Single country with the given country phone code.
  95. parseNumber('+33011222333', { extended: true }).should.deep.equal({
  96. country : 'FR',
  97. countryCallingCode : '33',
  98. phone : '011222333',
  99. carrierCode : undefined,
  100. ext : undefined,
  101. valid : false,
  102. possible : true
  103. })
  104. // Too short.
  105. // Won't strip national prefix `8` because otherwise the number would be too short.
  106. parseNumber('+7 (800) 55-35-35', { extended: true }).should.deep.equal({
  107. country : 'RU',
  108. countryCallingCode : '7',
  109. phone : '800553535',
  110. carrierCode : undefined,
  111. ext : undefined,
  112. valid : false,
  113. possible : false
  114. })
  115. // Too long.
  116. parseNumber('+1 213 37342530', { extended: true }).should.deep.equal({
  117. country : undefined,
  118. countryCallingCode : '1',
  119. phone : '21337342530',
  120. carrierCode : undefined,
  121. ext : undefined,
  122. valid : false,
  123. possible : false
  124. })
  125. // No national number to be parsed.
  126. parseNumber('+996', { extended: true }).should.deep.equal({
  127. // countryCallingCode : '996'
  128. })
  129. // Valid number.
  130. parseNumber('+78005553535', { extended: true }).should.deep.equal({
  131. country : 'RU',
  132. countryCallingCode : '7',
  133. phone : '8005553535',
  134. carrierCode : undefined,
  135. ext : undefined,
  136. valid : true,
  137. possible : true
  138. })
  139. // https://github.com/catamphetamine/libphonenumber-js/issues/211
  140. parseNumber('+966', { extended: true }).should.deep.equal({})
  141. parseNumber('+9664', { extended: true }).should.deep.equal({})
  142. parseNumber('+96645', { extended: true }).should.deep.equal({
  143. carrierCode : undefined,
  144. phone : '45',
  145. ext : undefined,
  146. country : 'SA',
  147. countryCallingCode : '966',
  148. possible : false,
  149. valid : false
  150. })
  151. })
  152. it('should parse non-European digits', () => {
  153. parseNumber('+١٢١٢٢٣٢٣٢٣٢').should.deep.equal({ country: 'US', phone: '2122323232' })
  154. })
  155. it('should work in edge cases', () => {
  156. let thrower
  157. // No input
  158. parseNumber('').should.deep.equal({})
  159. // No country phone code
  160. parseNumber('+').should.deep.equal({})
  161. // No country at all (non international number and no explicit country code)
  162. parseNumber('123').should.deep.equal({})
  163. // No country metadata for this `require` country code
  164. thrower = () => parseNumber('123', 'ZZ')
  165. thrower.should.throw('Unknown country')
  166. // No country metadata for this `default` country code
  167. thrower = () => parseNumber('123', { defaultCountry: 'ZZ' })
  168. thrower.should.throw('Unknown country')
  169. // Invalid country phone code
  170. parseNumber('+210').should.deep.equal({})
  171. // Invalid country phone code (extended parsing mode)
  172. parseNumber('+210', { extended: true }).should.deep.equal({})
  173. // Too short of a number.
  174. parseNumber('1', 'US', { extended: true }).should.deep.equal({})
  175. // Too long of a number.
  176. parseNumber('1111111111111111111', 'RU', { extended: true }).should.deep.equal({})
  177. // Not a number.
  178. parseNumber('abcdefg', 'US', { extended: true }).should.deep.equal({})
  179. // Country phone code beginning with a '0'
  180. parseNumber('+0123').should.deep.equal({})
  181. // Barbados NANPA phone number
  182. parseNumber('+12460000000').should.deep.equal({ country: 'BB', phone: '2460000000' })
  183. // // A case when country (restricted to) is not equal
  184. // // to the one parsed out of an international number.
  185. // parseNumber('+1-213-373-4253', 'RU').should.deep.equal({})
  186. // National (significant) number too short
  187. parseNumber('2', 'US').should.deep.equal({})
  188. // National (significant) number too long
  189. parseNumber('222222222222222222', 'US').should.deep.equal({})
  190. // No `national_prefix_for_parsing`
  191. parseNumber('41111', 'AC').should.deep.equal({ country: 'AC', phone: '41111'})
  192. // https://github.com/catamphetamine/libphonenumber-js/issues/235
  193. // `matchesEntirely()` bug fix.
  194. parseNumber('+4915784846111‬').should.deep.equal({ country: 'DE', phone: '15784846111' })
  195. // No metadata
  196. thrower = () => _parseNumber('')
  197. thrower.should.throw('`metadata` argument not passed')
  198. // // Numerical `value`
  199. // thrower = () => parseNumber(2141111111, 'US')
  200. // thrower.should.throw('A text for parsing must be a string.')
  201. // Input string too long.
  202. parseNumber('8005553535 ', 'RU').should.deep.equal({})
  203. })
  204. it('should parse phone number extensions', () => {
  205. // "ext"
  206. parseNumber('2134567890 ext 123', 'US').should.deep.equal({
  207. country : 'US',
  208. phone : '2134567890',
  209. ext : '123'
  210. })
  211. // "ext."
  212. parseNumber('+12134567890 ext. 12345', 'US').should.deep.equal({
  213. country : 'US',
  214. phone : '2134567890',
  215. ext : '12345'
  216. })
  217. // "доб."
  218. parseNumber('+78005553535 доб. 1234', 'RU').should.deep.equal({
  219. country : 'RU',
  220. phone : '8005553535',
  221. ext : '1234'
  222. })
  223. // "#"
  224. parseNumber('+12134567890#1234').should.deep.equal({
  225. country : 'US',
  226. phone : '2134567890',
  227. ext : '1234'
  228. })
  229. // "x"
  230. parseNumber('+78005553535 x1234').should.deep.equal({
  231. country : 'RU',
  232. phone : '8005553535',
  233. ext : '1234'
  234. })
  235. // Not a valid extension
  236. parseNumber('2134567890 ext. abc', 'US').should.deep.equal({
  237. country : 'US',
  238. phone : '2134567890'
  239. })
  240. })
  241. it('should parse RFC 3966 phone numbers', () => {
  242. parseNumber('tel:+78005553535;ext=123').should.deep.equal({
  243. country : 'RU',
  244. phone : '8005553535',
  245. ext : '123'
  246. })
  247. // Should parse "visual separators".
  248. parseNumber('tel:+7(800)555-35.35;ext=123').should.deep.equal({
  249. country : 'RU',
  250. phone : '8005553535',
  251. ext : '123'
  252. })
  253. // Invalid number.
  254. parseNumber('tel:+7x8005553535;ext=123').should.deep.equal({})
  255. })
  256. it('should parse invalid international numbers even if they are invalid', () => {
  257. parseNumber('+7(8)8005553535', 'RU').should.deep.equal({
  258. country : 'RU',
  259. phone : '8005553535'
  260. })
  261. })
  262. it('should parse carrier codes', () => {
  263. parseNumber('0 15 21 5555-5555', 'BR', { extended: true }).should.deep.equal({
  264. country : 'BR',
  265. countryCallingCode : '55',
  266. phone : '2155555555',
  267. carrierCode : '15',
  268. ext : undefined,
  269. valid : true,
  270. possible : true
  271. })
  272. })
  273. it('should parse IDD prefixes', () => {
  274. parseNumber('011 61 2 3456 7890', 'US').should.deep.equal({
  275. phone : '234567890',
  276. country : 'AU'
  277. })
  278. parseNumber('011 61 2 3456 7890', 'FR').should.deep.equal({})
  279. parseNumber('00 61 2 3456 7890', 'US').should.deep.equal({})
  280. parseNumber('810 61 2 3456 7890', 'RU').should.deep.equal({
  281. phone : '234567890',
  282. country : 'AU'
  283. })
  284. })
  285. it('should work with v2 API', () => {
  286. parseNumber('+99989160151539')
  287. })
  288. it('should work with Argentina numbers', () => {
  289. // The same mobile number is written differently
  290. // in different formats in Argentina:
  291. // `9` gets prepended in international format.
  292. parseNumber('+54 9 3435 55 1212').should.deep.equal({
  293. country: 'AR',
  294. phone: '93435551212'
  295. })
  296. parseNumber('0343 15-555-1212', 'AR').should.deep.equal({
  297. country: 'AR',
  298. phone: '93435551212'
  299. })
  300. })
  301. it('should work with Mexico numbers', () => {
  302. // Fixed line.
  303. parseNumber('+52 449 978 0001').should.deep.equal({
  304. country: 'MX',
  305. phone: '4499780001'
  306. })
  307. parseNumber('01 (449)978-0001', 'MX').should.deep.equal({
  308. country: 'MX',
  309. phone: '4499780001'
  310. })
  311. parseNumber('(449)978-0001', 'MX').should.deep.equal({
  312. country: 'MX',
  313. phone: '4499780001'
  314. })
  315. // Mobile.
  316. // `1` is prepended before area code to mobile numbers in international format.
  317. parseNumber('+52 1 33 1234-5678', 'MX').should.deep.equal({
  318. country: 'MX',
  319. phone: '3312345678'
  320. })
  321. parseNumber('+52 33 1234-5678', 'MX').should.deep.equal({
  322. country: 'MX',
  323. phone: '3312345678'
  324. })
  325. parseNumber('044 (33) 1234-5678', 'MX').should.deep.equal({
  326. country: 'MX',
  327. phone: '3312345678'
  328. })
  329. parseNumber('045 33 1234-5678', 'MX').should.deep.equal({
  330. country: 'MX',
  331. phone: '3312345678'
  332. })
  333. })
  334. it('should parse non-geographic numbering plan phone numbers', () => {
  335. parseNumber('+870773111632').should.deep.equal(
  336. USE_NON_GEOGRAPHIC_COUNTRY_CODE ?
  337. {
  338. country: '001',
  339. phone: '773111632'
  340. } :
  341. {}
  342. )
  343. })
  344. it('should parse non-geographic numbering plan phone numbers (default country code)', () => {
  345. parseNumber('773111632', { defaultCallingCode: '870' }).should.deep.equal(
  346. USE_NON_GEOGRAPHIC_COUNTRY_CODE ?
  347. {
  348. country: '001',
  349. phone: '773111632'
  350. } :
  351. {}
  352. )
  353. })
  354. it('should parse non-geographic numbering plan phone numbers (extended)', () => {
  355. parseNumber('+870773111632', { extended: true }).should.deep.equal({
  356. country: USE_NON_GEOGRAPHIC_COUNTRY_CODE ? '001' : undefined,
  357. countryCallingCode: '870',
  358. phone: '773111632',
  359. carrierCode: undefined,
  360. ext: undefined,
  361. possible: true,
  362. valid: true
  363. })
  364. })
  365. it('should parse non-geographic numbering plan phone numbers (default country code) (extended)', () => {
  366. parseNumber('773111632', { defaultCallingCode: '870', extended: true }).should.deep.equal({
  367. country: USE_NON_GEOGRAPHIC_COUNTRY_CODE ? '001' : undefined,
  368. countryCallingCode: '870',
  369. phone: '773111632',
  370. carrierCode: undefined,
  371. ext: undefined,
  372. possible: true,
  373. valid: true
  374. })
  375. })
  376. it('shouldn\'t crash when invalid `defaultCallingCode` is passed', () => {
  377. expect(() => parseNumber('773111632', { defaultCallingCode: '999' }))
  378. .to.throw('Unknown calling code')
  379. })
  380. it('shouldn\'t set `country` when there\'s no `defaultCountry` and `defaultCallingCode` is not of a "non-geographic entity"', () => {
  381. parseNumber('88005553535', { defaultCallingCode: '7' }).should.deep.equal({
  382. country: 'RU',
  383. phone: '8005553535'
  384. })
  385. })
  386. it('should correctly parse numbers starting with the same digit as the national prefix', () => {
  387. // https://github.com/catamphetamine/libphonenumber-js/issues/373
  388. // `BY`'s `national_prefix` is `8`.
  389. parseNumber('+37582004910060').should.deep.equal({
  390. country: 'BY',
  391. phone: '82004910060'
  392. });
  393. })
  394. it('should autocorrect numbers without a leading +', () => {
  395. // https://github.com/catamphetamine/libphonenumber-js/issues/376
  396. parseNumber('375447521111', 'BY').should.deep.equal({
  397. country: 'BY',
  398. phone: '447521111'
  399. });
  400. // https://github.com/catamphetamine/libphonenumber-js/issues/316
  401. parseNumber('33612902554', 'FR').should.deep.equal({
  402. country: 'FR',
  403. phone: '612902554'
  404. });
  405. // https://github.com/catamphetamine/libphonenumber-js/issues/375
  406. parseNumber('61438331999', 'AU').should.deep.equal({
  407. country: 'AU',
  408. phone: '438331999'
  409. });
  410. // A case when `49` is a country calling code of a number without a leading `+`.
  411. parseNumber('4930123456', 'DE').should.deep.equal({
  412. country: 'DE',
  413. phone: '30123456'
  414. });
  415. // A case when `49` is a valid area code.
  416. parseNumber('4951234567890', 'DE').should.deep.equal({
  417. country: 'DE',
  418. phone: '4951234567890'
  419. });
  420. })
  421. it('should parse extensions (long extensions with explicitl abels)', () => {
  422. // Test lower and upper limits of extension lengths for each type of label.
  423. // Firstly, when in RFC format: PhoneNumberUtil.extLimitAfterExplicitLabel
  424. parseNumber('33316005 ext 0', 'NZ').ext.should.equal('0')
  425. parseNumber('33316005 ext 01234567890123456789', 'NZ').ext.should.equal('01234567890123456789')
  426. // Extension too long.
  427. expect(parseNumber('33316005 ext 012345678901234567890', 'NZ').ext).to.be.undefined
  428. // Explicit extension label.
  429. parseNumber('03 3316005ext:1', 'NZ').ext.should.equal('1')
  430. parseNumber('03 3316005 xtn:12345678901234567890', 'NZ').ext.should.equal('12345678901234567890')
  431. parseNumber('03 3316005 extension\t12345678901234567890', 'NZ').ext.should.equal('12345678901234567890')
  432. parseNumber('03 3316005 xtensio:12345678901234567890', 'NZ').ext.should.equal('12345678901234567890')
  433. parseNumber('03 3316005 xtensión, 12345678901234567890#', 'NZ').ext.should.equal('12345678901234567890')
  434. parseNumber('03 3316005extension.12345678901234567890', 'NZ').ext.should.equal('12345678901234567890')
  435. parseNumber('03 3316005 доб:12345678901234567890', 'NZ').ext.should.equal('12345678901234567890')
  436. // Extension too long.
  437. expect(parseNumber('03 3316005 extension 123456789012345678901', 'NZ').ext).to.be.undefined
  438. })
  439. it('should parse extensions (long extensions with auto dialling labels)', () => {
  440. parseNumber('+12679000000,,123456789012345#').ext.should.equal('123456789012345')
  441. parseNumber('+12679000000;123456789012345#').ext.should.equal('123456789012345')
  442. parseNumber('+442034000000,,123456789#').ext.should.equal('123456789')
  443. // Extension too long.
  444. expect(parseNumber('+12679000000,,1234567890123456#').ext).to.be.undefined
  445. })
  446. it('should parse extensions (short extensions with ambiguous characters)', () => {
  447. parseNumber('03 3316005 x 123456789', 'NZ').ext.should.equal('123456789')
  448. parseNumber('03 3316005 x. 123456789', 'NZ').ext.should.equal('123456789')
  449. parseNumber('03 3316005 #123456789#', 'NZ').ext.should.equal('123456789')
  450. parseNumber('03 3316005 ~ 123456789', 'NZ').ext.should.equal('123456789')
  451. // Extension too long.
  452. expect(parseNumber('03 3316005 ~ 1234567890', 'NZ').ext).to.be.undefined
  453. })
  454. it('should parse extensions (short extensions when not sure of label)', () => {
  455. parseNumber('+1123-456-7890 666666#', { v2: true }).ext.should.equal('666666')
  456. parseNumber('+11234567890-6#', { v2: true }).ext.should.equal('6')
  457. // Extension too long.
  458. expect(() => parseNumber('+1123-456-7890 7777777#', { v2: true })).to.throw('NOT_A_NUMBER')
  459. })
  460. it('should choose `defaultCountry` (non-"main" one) when multiple countries match the number', () => {
  461. // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/103
  462. const phoneNumber = parseNumber('8004001000', { defaultCountry: 'CA', v2: true })
  463. phoneNumber.country.should.equal('CA')
  464. const phoneNumber2 = parseNumber('4389999999', { defaultCountry: 'US', v2: true })
  465. phoneNumber.country.should.equal('CA')
  466. })
  467. })