• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lancedikson / bowser / #676

31 Jan 2026 05:08PM UTC coverage: 89.113%. Remained the same
#676

push

github

naorpeled
Merge branch 'master' of github.com:bowser-js/bowser

369 of 490 branches covered (75.31%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 2 files covered. (100.0%)

3 existing lines in 2 files now uncovered.

736 of 750 relevant lines covered (98.13%)

354.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.27
/src/parser-browsers.js
1
/**
2
 * Browsers' descriptors
3
 *
4
 * The idea of descriptors is simple. You should know about them two simple things:
5
 * 1. Every descriptor has a method or property called `test` and a `describe` method.
6
 * 2. Order of descriptors is important.
7
 *
8
 * More details:
9
 * 1. Method or property `test` serves as a way to detect whether the UA string
10
 * matches some certain browser or not. The `describe` method helps to make a result
11
 * object with params that show some browser-specific things: name, version, etc.
12
 * 2. Order of descriptors is important because a Parser goes through them one by one
13
 * in course. For example, if you insert Chrome's descriptor as the first one,
14
 * more then a half of browsers will be described as Chrome, because they will pass
15
 * the Chrome descriptor's test.
16
 *
17
 * Descriptor's `test` could be a property with an array of RegExps, where every RegExp
18
 * will be applied to a UA string to test it whether it matches or not.
19
 * If a descriptor has two or more regexps in the `test` array it tests them one by one
20
 * with a logical sum operation. Parser stops if it has found any RegExp that matches the UA.
21
 *
22
 * Or `test` could be a method. In that case it gets a Parser instance and should
23
 * return true/false to get the Parser know if this browser descriptor matches the UA or not.
24
 */
25

26
import Utils from './utils.js';
27

28
const commonVersionIdentifier = /version\/(\d+(\.?_?\d+)+)/i;
6✔
29

30
const browsersList = [
6✔
31
  /* GPTBot */
32
  {
33
    test: [/gptbot/i],
34
    describe(ua) {
35
      const browser = {
2✔
36
        name: 'GPTBot',
37
      };
38
      const version = Utils.getFirstMatch(/gptbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
39

40
      if (version) {
2!
41
        browser.version = version;
2✔
42
      }
43

44
      return browser;
2✔
45
    },
46
  },
47

48
  /* ChatGPT-User */
49
  {
50
    test: [/chatgpt-user/i],
51
    describe(ua) {
52
      const browser = {
2✔
53
        name: 'ChatGPT-User',
54
      };
55
      const version = Utils.getFirstMatch(/chatgpt-user\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
56

57
      if (version) {
2!
58
        browser.version = version;
2✔
59
      }
60

61
      return browser;
2✔
62
    },
63
  },
64

65
  /* OAI-SearchBot */
66
  {
67
    test: [/oai-searchbot/i],
68
    describe(ua) {
69
      const browser = {
2✔
70
        name: 'OAI-SearchBot',
71
      };
72
      const version = Utils.getFirstMatch(/oai-searchbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
73

74
      if (version) {
2!
75
        browser.version = version;
2✔
76
      }
77

78
      return browser;
2✔
79
    },
80
  },
81

82
  /* ClaudeBot */
83
  {
84
    test: [/claudebot/i, /claude-web/i, /claude-user/i, /claude-searchbot/i],
85
    describe(ua) {
86
      const browser = {
2✔
87
        name: 'ClaudeBot',
88
      };
89
      const version = Utils.getFirstMatch(/(?:claudebot|claude-web|claude-user|claude-searchbot)\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
90

91
      if (version) {
2!
92
        browser.version = version;
2✔
93
      }
94

95
      return browser;
2✔
96
    },
97
  },
98

99
  /* Omgilibot */
100
  {
101
    test: [/omgilibot/i, /webzio-extended/i],
102
    describe(ua) {
103
      const browser = {
2✔
104
        name: 'Omgilibot',
105
      };
106
      const version = Utils.getFirstMatch(/(?:omgilibot|webzio-extended)\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
107

108
      if (version) {
2!
109
        browser.version = version;
2✔
110
      }
111

112
      return browser;
2✔
113
    },
114
  },
115

116
  /* Diffbot */
117
  {
118
    test: [/diffbot/i],
119
    describe(ua) {
120
      const browser = {
2✔
121
        name: 'Diffbot',
122
      };
123
      const version = Utils.getFirstMatch(/diffbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
124

125
      if (version) {
2!
126
        browser.version = version;
2✔
127
      }
128

129
      return browser;
2✔
130
    },
131
  },
132

133
  /* PerplexityBot */
134
  {
135
    test: [/perplexitybot/i],
136
    describe(ua) {
137
      const browser = {
2✔
138
        name: 'PerplexityBot',
139
      };
140
      const version = Utils.getFirstMatch(/perplexitybot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
141

142
      if (version) {
2!
143
        browser.version = version;
2✔
144
      }
145

146
      return browser;
2✔
147
    },
148
  },
149

150
  /* Perplexity-User */
151
  {
152
    test: [/perplexity-user/i],
153
    describe(ua) {
154
      const browser = {
2✔
155
        name: 'Perplexity-User',
156
      };
157
      const version = Utils.getFirstMatch(/perplexity-user\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
158

159
      if (version) {
2!
160
        browser.version = version;
2✔
161
      }
162

163
      return browser;
2✔
164
    },
165
  },
166

167
  /* YouBot */
168
  {
169
    test: [/youbot/i],
170
    describe(ua) {
171
      const browser = {
2✔
172
        name: 'YouBot',
173
      };
174
      const version = Utils.getFirstMatch(/youbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
175

176
      if (version) {
2!
177
        browser.version = version;
2✔
178
      }
179

180
      return browser;
2✔
181
    },
182
  },
183

184
  /* Meta-WebIndexer */
185
  {
186
    test: [/meta-webindexer/i],
187
    describe(ua) {
188
      const browser = {
4✔
189
        name: 'Meta-WebIndexer',
190
      };
191
      const version = Utils.getFirstMatch(/meta-webindexer\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
4!
192

193
      if (version) {
4!
194
        browser.version = version;
4✔
195
      }
196

197
      return browser;
4✔
198
    },
199
  },
200

201
  /* Meta-ExternalAds */
202
  {
203
    test: [/meta-externalads/i],
204
    describe(ua) {
205
      const browser = {
4✔
206
        name: 'Meta-ExternalAds',
207
      };
208
      const version = Utils.getFirstMatch(/meta-externalads\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
4!
209

210
      if (version) {
4!
211
        browser.version = version;
4✔
212
      }
213

214
      return browser;
4✔
215
    },
216
  },
217

218
  /* Meta-ExternalAgent */
219
  {
220
    test: [/meta-externalagent/i],
221
    describe(ua) {
222
      const browser = {
4✔
223
        name: 'Meta-ExternalAgent',
224
      };
225
      const version = Utils.getFirstMatch(/meta-externalagent\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
4!
226

227
      if (version) {
4!
228
        browser.version = version;
4✔
229
      }
230

231
      return browser;
4✔
232
    },
233
  },
234

235
  /* Meta-ExternalFetcher */
236
  {
237
    test: [/meta-externalfetcher/i],
238
    describe(ua) {
239
      const browser = {
4✔
240
        name: 'Meta-ExternalFetcher',
241
      };
242
      const version = Utils.getFirstMatch(/meta-externalfetcher\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
4!
243

244
      if (version) {
4!
245
        browser.version = version;
4✔
246
      }
247

248
      return browser;
4✔
249
    },
250
  },
251

252
  /* Googlebot */
253
  {
254
    test: [/googlebot/i],
255
    describe(ua) {
256
      const browser = {
6✔
257
        name: 'Googlebot',
258
      };
259
      const version = Utils.getFirstMatch(/googlebot\/(\d+(\.\d+))/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
6!
260

261
      if (version) {
6!
262
        browser.version = version;
6✔
263
      }
264

265
      return browser;
6✔
266
    },
267
  },
268

269
  /* AmazonBot */
270
  {
271
    test: [/amazonbot/i],
272
    describe(ua) {
273
      const browser = {
2✔
274
        name: 'AmazonBot',
275
      };
276
      const version = Utils.getFirstMatch(/amazonbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
277

278
      if (version) {
2!
279
        browser.version = version;
2✔
280
      }
281

282
      return browser;
2✔
283
    },
284
  },
285

286
  /* BingCrawler */
287
  {
288
    test: [/bingbot/i],
289
    describe(ua) {
290
      const browser = {
6✔
291
        name: 'BingCrawler',
292
      };
293
      const version = Utils.getFirstMatch(/bingbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
6!
294

295
      if (version) {
6!
296
        browser.version = version;
6✔
297
      }
298

299
      return browser;
6✔
300
    },
301
  },
302

303
  /* BaiduSpider */
304
  {
305
    test: [/baiduspider/i],
306
    describe(ua) {
307
      const browser = {
2✔
308
        name: 'BaiduSpider',
309
      };
310
      const version = Utils.getFirstMatch(/baiduspider\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2✔
311

312
      if (version) {
2!
313
        browser.version = version;
×
314
      }
315

316
      return browser;
2✔
317
    },
318
  },
319

320
  /* DuckDuckBot */
321
  {
322
    test: [/duckduckbot/i],
323
    describe(ua) {
324
      const browser = {
2✔
325
        name: 'DuckDuckBot',
326
      };
327
      const version = Utils.getFirstMatch(/duckduckbot\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
328

329
      if (version) {
2!
330
        browser.version = version;
2✔
331
      }
332

333
      return browser;
2✔
334
    },
335
  },
336

337
  /* InternetArchiveCrawler */
338
  {
339
    test: [/ia_archiver/i],
340
    describe(ua) {
341
      const browser = {
2✔
342
        name: 'InternetArchiveCrawler',
343
      };
344
      const version = Utils.getFirstMatch(/ia_archiver\/(\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2✔
345

346
      if (version) {
2!
347
        browser.version = version;
×
348
      }
349

350
      return browser;
2✔
351
    },
352
  },
353

354
  /* FacebookExternalHit */
355
  {
356
    test: [/facebookexternalhit/i, /facebookcatalog/i],
357
    describe() {
358
      return {
6✔
359
        name: 'FacebookExternalHit',
360
      };
361
    },
362
  },
363

364
  /* SlackBot */
365
  {
366
    test: [/slackbot/i, /slack-imgProxy/i],
367
    describe(ua) {
368
      const browser = {
6✔
369
        name: 'SlackBot',
370
      };
371
      const version = Utils.getFirstMatch(/(?:slackbot|slack-imgproxy)(?:-[-\w]+)?[\s/](\d+(\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
6!
372

373
      if (version) {
6!
374
        browser.version = version;
6✔
375
      }
376

377
      return browser;
6✔
378
    },
379
  },
380

381
  /* YahooSlurp */
382
  {
383
    test: [/yahoo!?[\s/]*slurp/i],
384
    describe() {
385
      return {
2✔
386
        name: 'YahooSlurp',
387
      };
388
    },
389
  },
390

391
  /* YandexBot */
392
  {
393
    test: [/yandexbot/i, /yandexmobilebot/i],
394
    describe() {
395
      return {
4✔
396
        name: 'YandexBot',
397
      };
398
    },
399
  },
400

401
  /* PingdomBot */
402
  {
403
    test: [/pingdom/i],
404
    describe() {
405
      return {
2✔
406
        name: 'PingdomBot',
407
      };
408
    },
409
  },
410

411
  /* Opera < 13.0 */
412
  {
413
    test: [/opera/i],
414
    describe(ua) {
415
      const browser = {
26✔
416
        name: 'Opera',
417
      };
418
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:opera)[\s/](\d+(\.?_?\d+)+)/i, ua);
26✔
419

420
      if (version) {
26!
421
        browser.version = version;
26✔
422
      }
423

424
      return browser;
26✔
425
    },
426
  },
427

428
  /* Opera > 13.0 */
429
  {
430
    test: [/opr\/|opios/i],
431
    describe(ua) {
432
      const browser = {
16✔
433
        name: 'Opera',
434
      };
435
      const version = Utils.getFirstMatch(/(?:opr|opios)[\s/](\S+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
16!
436

437
      if (version) {
16!
438
        browser.version = version;
16✔
439
      }
440

441
      return browser;
16✔
442
    },
443
  },
444
  {
445
    test: [/SamsungBrowser/i],
446
    describe(ua) {
447
      const browser = {
2✔
448
        name: 'Samsung Internet for Android',
449
      };
450
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:SamsungBrowser)[\s/](\d+(\.?_?\d+)+)/i, ua);
2✔
451

452
      if (version) {
2!
453
        browser.version = version;
2✔
454
      }
455

456
      return browser;
2✔
457
    },
458
  },
459
  {
460
    test: [/Whale/i],
461
    describe(ua) {
462
      const browser = {
2✔
463
        name: 'NAVER Whale Browser',
464
      };
465
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:whale)[\s/](\d+(?:\.\d+)+)/i, ua);
2✔
466

467
      if (version) {
2!
468
        browser.version = version;
2✔
469
      }
470

471
      return browser;
2✔
472
    },
473
  },
474
  {
475
    test: [/PaleMoon/i],
476
    describe(ua) {
477
      const browser = {
4✔
478
        name: 'Pale Moon',
479
      };
480
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:PaleMoon)[\s/](\d+(?:\.\d+)+)/i, ua);
4✔
481

482
      if (version) {
4!
483
        browser.version = version;
4✔
484
      }
485

486
      return browser;
4✔
487
    },
488
  },
489
  {
490
    test: [/MZBrowser/i],
491
    describe(ua) {
492
      const browser = {
2✔
493
        name: 'MZ Browser',
494
      };
495
      const version = Utils.getFirstMatch(/(?:MZBrowser)[\s/](\d+(?:\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
496

497
      if (version) {
2!
498
        browser.version = version;
2✔
499
      }
500

501
      return browser;
2✔
502
    },
503
  },
504
  {
505
    test: [/focus/i],
506
    describe(ua) {
507
      const browser = {
4✔
508
        name: 'Focus',
509
      };
510
      const version = Utils.getFirstMatch(/(?:focus)[\s/](\d+(?:\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
4!
511

512
      if (version) {
4!
513
        browser.version = version;
4✔
514
      }
515

516
      return browser;
4✔
517
    },
518
  },
519
  {
520
    test: [/swing/i],
521
    describe(ua) {
522
      const browser = {
2✔
523
        name: 'Swing',
524
      };
525
      const version = Utils.getFirstMatch(/(?:swing)[\s/](\d+(?:\.\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
526

527
      if (version) {
2!
528
        browser.version = version;
2✔
529
      }
530

531
      return browser;
2✔
532
    },
533
  },
534
  {
535
    test: [/coast/i],
536
    describe(ua) {
537
      const browser = {
2✔
538
        name: 'Opera Coast',
539
      };
540
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:coast)[\s/](\d+(\.?_?\d+)+)/i, ua);
2✔
541

542
      if (version) {
2!
543
        browser.version = version;
2✔
544
      }
545

546
      return browser;
2✔
547
    },
548
  },
549
  {
550
    test: [/opt\/\d+(?:.?_?\d+)+/i],
551
    describe(ua) {
552
      const browser = {
2✔
553
        name: 'Opera Touch',
554
      };
555
      const version = Utils.getFirstMatch(/(?:opt)[\s/](\d+(\.?_?\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
2!
556

557
      if (version) {
2!
558
        browser.version = version;
2✔
559
      }
560

561
      return browser;
2✔
562
    },
563
  },
564
  {
565
    test: [/yabrowser/i],
566
    describe(ua) {
567
      const browser = {
6✔
568
        name: 'Yandex Browser',
569
      };
570
      const version = Utils.getFirstMatch(/(?:yabrowser)[\s/](\d+(\.?_?\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
6!
571

572
      if (version) {
6!
573
        browser.version = version;
6✔
574
      }
575

576
      return browser;
6✔
577
    },
578
  },
579
  {
580
    test: [/ucbrowser/i],
581
    describe(ua) {
582
      const browser = {
6✔
583
        name: 'UC Browser',
584
      };
585
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:ucbrowser)[\s/](\d+(\.?_?\d+)+)/i, ua);
6✔
586

587
      if (version) {
6!
588
        browser.version = version;
6✔
589
      }
590

591
      return browser;
6✔
592
    },
593
  },
594
  {
595
    test: [/Maxthon|mxios/i],
596
    describe(ua) {
597
      const browser = {
4✔
598
        name: 'Maxthon',
599
      };
600
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:Maxthon|mxios)[\s/](\d+(\.?_?\d+)+)/i, ua);
4✔
601

602
      if (version) {
4!
603
        browser.version = version;
4✔
604
      }
605

606
      return browser;
4✔
607
    },
608
  },
609
  {
610
    test: [/epiphany/i],
611
    describe(ua) {
612
      const browser = {
2✔
613
        name: 'Epiphany',
614
      };
615
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:epiphany)[\s/](\d+(\.?_?\d+)+)/i, ua);
2!
616

617
      if (version) {
2!
618
        browser.version = version;
2✔
619
      }
620

621
      return browser;
2✔
622
    },
623
  },
624
  {
625
    test: [/puffin/i],
626
    describe(ua) {
627
      const browser = {
2✔
628
        name: 'Puffin',
629
      };
630
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:puffin)[\s/](\d+(\.?_?\d+)+)/i, ua);
2✔
631

632
      if (version) {
2!
633
        browser.version = version;
2✔
634
      }
635

636
      return browser;
2✔
637
    },
638
  },
639
  {
640
    test: [/sleipnir/i],
641
    describe(ua) {
642
      const browser = {
2✔
643
        name: 'Sleipnir',
644
      };
645
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:sleipnir)[\s/](\d+(\.?_?\d+)+)/i, ua);
2✔
646

647
      if (version) {
2!
648
        browser.version = version;
2✔
649
      }
650

651
      return browser;
2✔
652
    },
653
  },
654
  {
655
    test: [/k-meleon/i],
656
    describe(ua) {
657
      const browser = {
2✔
658
        name: 'K-Meleon',
659
      };
660
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/(?:k-meleon)[\s/](\d+(\.?_?\d+)+)/i, ua);
2✔
661

662
      if (version) {
2!
663
        browser.version = version;
2✔
664
      }
665

666
      return browser;
2✔
667
    },
668
  },
669
  {
670
    test: [/micromessenger/i],
671
    describe(ua) {
672
      const browser = {
8✔
673
        name: 'WeChat',
674
      };
675
      const version = Utils.getFirstMatch(/(?:micromessenger)[\s/](\d+(\.?_?\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
8!
676

677
      if (version) {
8!
678
        browser.version = version;
8✔
679
      }
680

681
      return browser;
8✔
682
    },
683
  },
684
  {
685
    test: [/qqbrowser/i],
686
    describe(ua) {
687
      const browser = {
12✔
688
        name: (/qqbrowserlite/i).test(ua) ? 'QQ Browser Lite' : 'QQ Browser',
12✔
689
      };
690
      const version = Utils.getFirstMatch(/(?:qqbrowserlite|qqbrowser)[/](\d+(\.?_?\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
12!
691

692
      if (version) {
12!
693
        browser.version = version;
12✔
694
      }
695

696
      return browser;
12✔
697
    },
698
  },
699
  {
700
    test: [/msie|trident/i],
701
    describe(ua) {
702
      const browser = {
42✔
703
        name: 'Internet Explorer',
704
      };
705
      const version = Utils.getFirstMatch(/(?:msie |rv:)(\d+(\.?_?\d+)+)/i, ua);
42✔
706

707
      if (version) {
42!
708
        browser.version = version;
42✔
709
      }
710

711
      return browser;
42✔
712
    },
713
  },
714
  {
715
    test: [/\sedg\//i],
716
    describe(ua) {
717
      const browser = {
4✔
718
        name: 'Microsoft Edge',
719
      };
720

721
      const version = Utils.getFirstMatch(/\sedg\/(\d+(\.?_?\d+)+)/i, ua);
4✔
722

723
      if (version) {
4!
724
        browser.version = version;
4✔
725
      }
726

727
      return browser;
4✔
728
    },
729
  },
730
  {
731
    test: [/edg([ea]|ios)/i],
732
    describe(ua) {
733
      const browser = {
10✔
734
        name: 'Microsoft Edge',
735
      };
736

737
      const version = Utils.getSecondMatch(/edg([ea]|ios)\/(\d+(\.?_?\d+)+)/i, ua);
10✔
738

739
      if (version) {
10!
740
        browser.version = version;
10✔
741
      }
742

743
      return browser;
10✔
744
    },
745
  },
746
  {
747
    test: [/vivaldi/i],
748
    describe(ua) {
749
      const browser = {
4✔
750
        name: 'Vivaldi',
751
      };
752
      const version = Utils.getFirstMatch(/vivaldi\/(\d+(\.?_?\d+)+)/i, ua);
4✔
753

754
      if (version) {
4!
755
        browser.version = version;
4✔
756
      }
757

758
      return browser;
4✔
759
    },
760
  },
761
  {
762
    test: [/seamonkey/i],
763
    describe(ua) {
764
      const browser = {
6✔
765
        name: 'SeaMonkey',
766
      };
767
      const version = Utils.getFirstMatch(/seamonkey\/(\d+(\.?_?\d+)+)/i, ua);
6✔
768

769
      if (version) {
6!
770
        browser.version = version;
6✔
771
      }
772

773
      return browser;
6✔
774
    },
775
  },
776
  {
777
    test: [/sailfish/i],
778
    describe(ua) {
779
      const browser = {
4✔
780
        name: 'Sailfish',
781
      };
782

783
      const version = Utils.getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i, ua);
4✔
784

785
      if (version) {
4!
786
        browser.version = version;
4✔
787
      }
788

789
      return browser;
4✔
790
    },
791
  },
792
  {
793
    test: [/silk/i],
794
    describe(ua) {
795
      const browser = {
8✔
796
        name: 'Amazon Silk',
797
      };
798
      const version = Utils.getFirstMatch(/silk\/(\d+(\.?_?\d+)+)/i, ua);
8✔
799

800
      if (version) {
8!
801
        browser.version = version;
8✔
802
      }
803

804
      return browser;
8✔
805
    },
806
  },
807
  {
808
    test: [/phantom/i],
809
    describe(ua) {
810
      const browser = {
2✔
811
        name: 'PhantomJS',
812
      };
813
      const version = Utils.getFirstMatch(/phantomjs\/(\d+(\.?_?\d+)+)/i, ua);
2✔
814

815
      if (version) {
2!
816
        browser.version = version;
2✔
817
      }
818

819
      return browser;
2✔
820
    },
821
  },
822
  {
823
    test: [/slimerjs/i],
824
    describe(ua) {
825
      const browser = {
2✔
826
        name: 'SlimerJS',
827
      };
828
      const version = Utils.getFirstMatch(/slimerjs\/(\d+(\.?_?\d+)+)/i, ua);
2✔
829

830
      if (version) {
2!
831
        browser.version = version;
2✔
832
      }
833

834
      return browser;
2✔
835
    },
836
  },
837
  {
838
    test: [/blackberry|\bbb\d+/i, /rim\stablet/i],
839
    describe(ua) {
840
      const browser = {
20✔
841
        name: 'BlackBerry',
842
      };
843
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/blackberry[\d]+\/(\d+(\.?_?\d+)+)/i, ua);
20✔
844

845
      if (version) {
20!
846
        browser.version = version;
20✔
847
      }
848

849
      return browser;
20✔
850
    },
851
  },
852
  {
853
    test: [/(web|hpw)[o0]s/i],
854
    describe(ua) {
855
      const browser = {
14✔
856
        name: 'WebOS Browser',
857
      };
858
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua) || Utils.getFirstMatch(/w(?:eb)?[o0]sbrowser\/(\d+(\.?_?\d+)+)/i, ua);
14✔
859

860
      if (version) {
14✔
861
        browser.version = version;
12✔
862
      }
863

864
      return browser;
14✔
865
    },
866
  },
867
  {
868
    test: [/bada/i],
869
    describe(ua) {
870
      const browser = {
6✔
871
        name: 'Bada',
872
      };
873
      const version = Utils.getFirstMatch(/dolfin\/(\d+(\.?_?\d+)+)/i, ua);
6✔
874

875
      if (version) {
6!
876
        browser.version = version;
6✔
877
      }
878

879
      return browser;
6✔
880
    },
881
  },
882
  {
883
    test: [/tizen/i],
884
    describe(ua) {
885
      const browser = {
10✔
886
        name: 'Tizen',
887
      };
888
      const version = Utils.getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.?_?\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
10✔
889

890
      if (version) {
10!
891
        browser.version = version;
10✔
892
      }
893

894
      return browser;
10✔
895
    },
896
  },
897
  {
898
    test: [/qupzilla/i],
899
    describe(ua) {
900
      const browser = {
4✔
901
        name: 'QupZilla',
902
      };
903
      const version = Utils.getFirstMatch(/(?:qupzilla)[\s/](\d+(\.?_?\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
4!
904

905
      if (version) {
4!
906
        browser.version = version;
4✔
907
      }
908

909
      return browser;
4✔
910
    },
911
  },
912
  {
913
    test: [/librewolf/i],
914
    describe(ua) {
915
      const browser = {
2✔
916
        name: 'LibreWolf',
917
      };
918
      const version = Utils.getFirstMatch(/(?:librewolf)[\s/](\d+(\.?_?\d+)+)/i, ua);
2✔
919

920
      if (version) {
2!
921
        browser.version = version;
2✔
922
      }
923

924
      return browser;
2✔
925
    },
926
  },
927
  {
928
    test: [/firefox|iceweasel|fxios/i],
929
    describe(ua) {
930
      const browser = {
44✔
931
        name: 'Firefox',
932
      };
933
      const version = Utils.getFirstMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i, ua);
44✔
934

935
      if (version) {
44!
936
        browser.version = version;
44✔
937
      }
938

939
      return browser;
44✔
940
    },
941
  },
942
  {
943
    test: [/electron/i],
944
    describe(ua) {
945
      const browser = {
8✔
946
        name: 'Electron',
947
      };
948
      const version = Utils.getFirstMatch(/(?:electron)\/(\d+(\.?_?\d+)+)/i, ua);
8✔
949

950
      if (version) {
8!
951
        browser.version = version;
8✔
952
      }
953

954
      return browser;
8✔
955
    },
956
  },
957
  {
958
    test: [/sogoumobilebrowser/i, /metasr/i, /se 2\.[x]/i],
959
    describe(ua) {
960
      const browser = {
6✔
961
        name: 'Sogou Browser',
962
      };
963
      const sogouMobileVersion = Utils.getFirstMatch(/(?:sogoumobilebrowser)[\s/](\d+(\.?_?\d+)+)/i, ua);
6✔
964
      const chromiumVersion = Utils.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua);
6✔
965
      const seVersion = Utils.getFirstMatch(/se ([\d.]+)x/i, ua);
6✔
966
      const version = sogouMobileVersion || chromiumVersion || seVersion;
6!
967

968
      if (version) {
6!
969
        browser.version = version;
6✔
970
      }
971

972
      return browser;
6✔
973
    },
974
  },
975
  {
976
    test: [/MiuiBrowser/i],
977
    describe(ua) {
978
      const browser = {
4✔
979
        name: 'Miui',
980
      };
981
      const version = Utils.getFirstMatch(/(?:MiuiBrowser)[\s/](\d+(\.?_?\d+)+)/i, ua);
4✔
982

983
      if (version) {
4✔
984
        browser.version = version;
2✔
985
      }
986

987
      return browser;
4✔
988
    },
989
  },
990
  {
991
    test: [/chromium/i],
992
    describe(ua) {
993
      const browser = {
4✔
994
        name: 'Chromium',
995
      };
996
      const version = Utils.getFirstMatch(/(?:chromium)[\s/](\d+(\.?_?\d+)+)/i, ua) || Utils.getFirstMatch(commonVersionIdentifier, ua);
4!
997

998
      if (version) {
4!
999
        browser.version = version;
4✔
1000
      }
1001

1002
      return browser;
4✔
1003
    },
1004
  },
1005
  {
1006
    test: [/chrome|crios|crmo/i],
1007
    describe(ua) {
1008
      const browser = {
52✔
1009
        name: 'Chrome',
1010
      };
1011
      const version = Utils.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua);
52✔
1012

1013
      if (version) {
52!
1014
        browser.version = version;
52✔
1015
      }
1016

1017
      return browser;
52✔
1018
    },
1019
  },
1020
  {
1021
    test: [/GSA/i],
1022
    describe(ua) {
1023
      const browser = {
2✔
1024
        name: 'Google Search',
1025
      };
1026
      const version = Utils.getFirstMatch(/(?:GSA)\/(\d+(\.?_?\d+)+)/i, ua);
2✔
1027

1028
      if (version) {
2!
1029
        browser.version = version;
2✔
1030
      }
1031

1032
      return browser;
2✔
1033
    },
1034
  },
1035

1036
  /* Android Browser */
1037
  {
1038
    test(parser) {
1039
      const notLikeAndroid = !parser.test(/like android/i);
86✔
1040
      const butAndroid = parser.test(/android/i);
86✔
1041
      return notLikeAndroid && butAndroid;
86✔
1042
    },
1043
    describe(ua) {
1044
      const browser = {
14✔
1045
        name: 'Android Browser',
1046
      };
1047
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua);
14✔
1048

1049
      if (version) {
14!
1050
        browser.version = version;
14✔
1051
      }
1052

1053
      return browser;
14✔
1054
    },
1055
  },
1056

1057
  /* PlayStation 4 */
1058
  {
1059
    test: [/playstation 4/i],
1060
    describe(ua) {
1061
      const browser = {
2✔
1062
        name: 'PlayStation 4',
1063
      };
1064
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua);
2✔
1065

1066
      if (version) {
2!
UNCOV
1067
        browser.version = version;
×
1068
      }
1069

1070
      return browser;
2✔
1071
    },
1072
  },
1073

1074
  /* Safari */
1075
  {
1076
    test: [/safari|applewebkit/i],
1077
    describe(ua) {
1078
      const browser = {
64✔
1079
        name: 'Safari',
1080
      };
1081
      const version = Utils.getFirstMatch(commonVersionIdentifier, ua);
64✔
1082

1083
      if (version) {
64✔
1084
        browser.version = version;
58✔
1085
      }
1086

1087
      return browser;
64✔
1088
    },
1089
  },
1090

1091
  /* Something else */
1092
  {
1093
    test: [/.*/i],
1094
    describe(ua) {
1095
      /* Here we try to make sure that there are explicit details about the device
1096
       * in order to decide what regexp exactly we want to apply
1097
       * (as there is a specific decision based on that conclusion)
1098
       */
1099
      const regexpWithoutDeviceSpec = /^(.*)\/(.*) /;
6✔
1100
      const regexpWithDeviceSpec = /^(.*)\/(.*)[ \t]\((.*)/;
6✔
1101
      const hasDeviceSpec = ua.search('\\(') !== -1;
6✔
1102
      const regexp = hasDeviceSpec ? regexpWithDeviceSpec : regexpWithoutDeviceSpec;
6✔
1103
      return {
6✔
1104
        name: Utils.getFirstMatch(regexp, ua),
1105
        version: Utils.getSecondMatch(regexp, ua),
1106
      };
1107
    },
1108
  },
1109
];
1110

1111
export default browsersList;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc