├── .circleci └── config.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── internal │ ├── bot_exceptions.yml │ └── known_bots.yml └── test │ ├── bots.yml │ ├── devices.yml │ ├── matchers.yml │ └── platforms.yml ├── bot.go ├── bot_test.go ├── bots ├── bots_test.go ├── empty.go ├── empty_test.go ├── keyword.go ├── keyword_test.go ├── known.go └── known_test.go ├── browser.go ├── browser_test.go ├── device.go ├── device_test.go ├── devices ├── android.go ├── android_test.go ├── blackberry_playbook.go ├── blackberry_playbook_test.go ├── devices_test.go ├── ipad.go ├── ipad_test.go ├── iphone.go ├── iphone_test.go ├── ipod_touch.go ├── ipod_touch_test.go ├── kindle.go ├── kindle_fire.go ├── kindle_fire_test.go ├── kindle_test.go ├── parser.go ├── playstation_3.go ├── playstation_3_test.go ├── playstation_4.go ├── playstation_4_test.go ├── playstation_5.go ├── playstation_5_test.go ├── ps_vita.go ├── ps_vita_test.go ├── psp.go ├── psp_test.go ├── samsung.go ├── samsung_test.go ├── surface.go ├── surface_test.go ├── switch.go ├── switch_test.go ├── tv.go ├── tv_test.go ├── unknown.go ├── unknown_test.go ├── wii.go ├── wii_test.go ├── wii_u.go ├── wii_u_test.go ├── xbox_360.go ├── xbox_360_test.go ├── xbox_one.go └── xbox_one_test.go ├── errors.go ├── go.mod ├── go.sum ├── logo.png ├── matchers ├── alipay.go ├── alipay_test.go ├── black_berry.go ├── black_berry_test.go ├── brave.go ├── brave_test.go ├── chrome.go ├── chrome_test.go ├── duck_duck_go.go ├── duck_duck_go_test.go ├── edge.go ├── edge_test.go ├── electron.go ├── electron_test.go ├── firefox.go ├── firefox_test.go ├── gsa.go ├── gsa_test.go ├── huawei_browser.go ├── huawei_browser_test.go ├── instagram.go ├── instagram_test.go ├── internet_explorer.go ├── internet_explorer_test.go ├── konqueror.go ├── konqueror_test.go ├── matchers_test.go ├── maxthon.go ├── maxthon_test.go ├── micro_messenger.go ├── micro_messenger_test.go ├── miui_browser.go ├── miui_browser_test.go ├── nokia.go ├── nokia_test.go ├── opera.go ├── opera_test.go ├── otter.go ├── otter_test.go ├── pale_moon.go ├── pale_moon_test.go ├── parser.go ├── parser_test.go ├── puffin.go ├── puffin_test.go ├── qq.go ├── qq_test.go ├── safari.go ├── safari_test.go ├── samsung_browser.go ├── samsung_browser_test.go ├── snapchat.go ├── snapchat_test.go ├── sogou_browser.go ├── sogou_browser_test.go ├── sputnik.go ├── sputnik_test.go ├── uc_browser.go ├── uc_browser_test.go ├── unknown.go ├── unknown_test.go ├── vivaldi.go ├── vivaldi_test.go ├── vivo_browser.go ├── vivo_browser_test.go ├── weibo.go ├── weibo_test.go ├── yaani_browser.go ├── yaani_browser_test.go ├── yandex.go └── yandex_test.go ├── platform.go ├── platform_test.go ├── platforms ├── adobe_air.go ├── adobe_air_test.go ├── android.go ├── android_test.go ├── black_berry.go ├── black_berry_test.go ├── chrome_os.go ├── chrome_os_test.go ├── firefox_os.go ├── firefox_os_test.go ├── ios.go ├── ios_test.go ├── kai.go ├── kai_test.go ├── linux.go ├── linux_test.go ├── mac.go ├── mac_test.go ├── parser.go ├── parser_test.go ├── platforms_test.go ├── unknown.go ├── unknown_test.go ├── watch_os.go ├── watch_os_test.go ├── windows.go ├── windows_mobile.go ├── windows_mobile_test.go ├── windows_phone.go ├── windows_phone_test.go └── windows_test.go └── utils ├── version.go └── version_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | codecov: codecov/codecov@3.2.5 4 | jobs: 5 | build: 6 | working_directory: ~/browser 7 | docker: 8 | - image: cimg/go:1.19.13 9 | steps: 10 | - checkout 11 | - restore_cache: 12 | keys: 13 | - go-mod-v4-{{ checksum "go.sum" }} 14 | - run: 15 | name: Install Dependencies 16 | command: go mod download 17 | - save_cache: 18 | key: go-mod-v4-{{ checksum "go.sum" }} 19 | paths: 20 | - "/go/pkg/mod" 21 | - run: 22 | name: "Create a temp directory for artifacts" 23 | command: | 24 | mkdir -p /tmp/artifacts 25 | - run: 26 | name: Run tests 27 | command: | 28 | go test ./... -coverprofile=c.out 29 | mv c.out /tmp/artifacts 30 | - codecov/upload: 31 | file: /tmp/artifacts/c.out 32 | 33 | workflow: 34 | version: 2.1 35 | build-test: 36 | jobs: 37 | - build 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | 23 | vendor 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Requirements 4 | 5 | - [Go](https://golang.org/dl/) 1.19 or higher 6 | 7 | ## Getting Started 8 | 9 | 1. Fork the repository and clone it locally 10 | 2. Run `go mod download` to download dependencies 11 | 3. Run `go test ./...` to run all tests 12 | 13 | ## Testing 14 | 15 | The tests are located in the `matchers`, `devices`, `platforms` and `browser` packages. The tests are using the `assets/test` directory to load the user agents data. 16 | 17 | Package follows BDD style test cases. The tests are using the [GoConvey](https://github.com/smartystreets/goconvey) library to write BDD style tests. 18 | 19 | ## Adding a new browser support 20 | 21 | Every browser implements the `BrowserMatcher` interface. 22 | 23 | 1. Add the file to the `matchers` package. 24 | 2. Add the new browser the list of matchers in `browser.go`. Make sure the order of the matchers is correct. The order of the matchers is important because the first matcher that returns `true` will be used. 25 | 3. Add specific tests for the new browser in the `matchers` package. Make sure to test all the possible user agents for the browser. You can use [this](https://developers.whatismybrowser.com/useragents/explore/) website to find user agents for the browser. 26 | 4. Add the user agents data to [matchers.yml](assets/test/matchers.yml) file. 27 | 5. Add all necerssary functions to `Browser` struct in `browser.go` file. 28 | 29 | ## Adding a new device support 30 | 31 | Every device implements the `DeviceMatcher` interface. 32 | 33 | 1. Add the file to the `devices` package. 34 | 2. Add the new device to list of `DeviceMatcher` in `device.go`. Make sure the order of the matchers is correct. The order of the matchers is important because the first matcher that returns `true` will be used. 35 | 3. Add specific tests for the new device in the `devices` package. Make sure to test all the possible user agents for the device. You can use [this](https://developers.whatismybrowser.com/useragents/explore/) website to find user agents for the device. 36 | 4. Add the user agents data to [devices.yml](assets/test/devices.yml) file. 37 | 5. Add all necerssary functions to `Device` struct in `device.go` file. 38 | 39 | ## Adding a new platform support 40 | 41 | Every platform implements the `PlatformMatcher` interface. 42 | 43 | 1. Add the file to the `platforms` package. 44 | 2. Add the new platform to list of `PlatformMatcher` in `platform.go`. Make sure the order of the matchers is correct. The order of the matchers is important because the first matcher that returns `true` will be used. 45 | 3. Add specific tests for the new platform in the `platforms` package. Make sure to test all the possible user agents for the platform. You can use [this](https://developers.whatismybrowser.com/useragents/explore/) website to find user agents for the platform. 46 | 4. Add the user agents data to [platforms.yml](assets/test/platforms.yml) file. 47 | 5. Add all necerssary functions to `Platform` struct in `platform.go` file. 48 | 49 | ## Adding a new known bot support 50 | 51 | 1. Add the new bot to [known_bots.yml](assets/internal/known_bots.yml). 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Dinesh Gowda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/internal/bot_exceptions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - ruby build 3 | - pinterest/android 4 | - pinterest/ios 5 | - yandexsearchbrowser 6 | -------------------------------------------------------------------------------- /assets/test/bots.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apache-httpclient: Apache-HttpClient/4.5.5 (Java/9.0.1) 3 | apis-google: APIs-Google (https://developers.google.com/webmasters/APIs-Google.html) 4 | barkrowler: Barkrowler/0.9 (+http://www.exensa.com/crawl) X-SiteSpeedApp-1 5 | bingbot: Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) 6 | crawl: NetSeer/Nutch-0.9 (NetSeer Crawler; http://www.netseer.com; crawler@netseer.com) 7 | datapack: DataparkSearch/4.35 ( http://www.dataparksearch.org/) 8 | fetch: admantx-ussy02/2.4 (+http://www.admantx.com/service-fetcher.html) 9 | googlebot: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) 10 | monitoring: FreeWebMonitoring SiteChecker/0.2 (+https://www.freewebmonitoring.com/bot.html) 11 | spider: Mozilla/5.0(compatible; Sosospider/2.0; +http://help.soso.com/webspider.htm) 12 | -------------------------------------------------------------------------------- /bot.go: -------------------------------------------------------------------------------- 1 | package browser 2 | 3 | import ( 4 | "embed" 5 | "log" 6 | "reflect" 7 | "strings" 8 | 9 | "github.com/dineshgowda24/browser/bots" 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // Embed the assets/internal directory 14 | // 15 | //go:embed assets/internal 16 | var fs embed.FS 17 | 18 | // BotMatcher is the interface for bot matchers 19 | type BotMatcher interface { 20 | Matcher 21 | } 22 | 23 | var genericBotName = "Generic Bot" 24 | 25 | // Bot is a struct that contains information about the user agent's bot 26 | type Bot struct { 27 | userAgent string // user agent string 28 | exceptions []string // list of user agents that are exceptions 29 | matcher BotMatcher // bot matcher detected 30 | registered bool // indicates if the bot matcher has registered 31 | } 32 | 33 | // NewBot returns a new Bot 34 | // It will return an error if the bot exceptions file cannot be read 35 | // It will return an error if the bot exceptions file cannot be unmarshalled 36 | func NewBot(userAgent string) (*Bot, error) { 37 | be, err := fs.ReadFile("assets/internal/bot_exceptions.yml") 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | e := make([]string, 0) 43 | if err := yaml.Unmarshal(be, &e); err != nil { 44 | return nil, err 45 | } 46 | 47 | bot := &Bot{ 48 | userAgent: userAgent, 49 | exceptions: e, 50 | } 51 | bot.register() 52 | 53 | return bot, nil 54 | } 55 | 56 | // register registers the bot matcher 57 | func (b *Bot) register() { 58 | if b.exception() { 59 | return 60 | } 61 | 62 | matchers := []Matcher{ 63 | bots.NewEmpty(b.userAgent), 64 | bots.NewKnown(b.userAgent, getKnownBots()), 65 | bots.NewKeyword(b.userAgent), 66 | } 67 | 68 | for _, m := range matchers { 69 | if m.Match() { 70 | b.matcher = m 71 | break 72 | } 73 | } 74 | 75 | b.registered = true 76 | } 77 | 78 | // getKnownBots returns the known bots 79 | // It will panic if the known bots file cannot be read 80 | // TODO: return an error 81 | func getKnownBots() map[string]string { 82 | kb, err := fs.ReadFile("assets/internal/known_bots.yml") 83 | if err != nil { 84 | log.Panicln(err) 85 | } 86 | 87 | b := make(map[string]string) 88 | if err := yaml.Unmarshal(kb, &b); err != nil { 89 | log.Panicln(err) 90 | } 91 | 92 | return b 93 | } 94 | 95 | // getMatcher returns the bot matcher detected from the user agent string 96 | // It will register the bot matcher if it has not been registered 97 | func (b *Bot) getMatcher() Matcher { 98 | if !b.registered { 99 | b.register() 100 | } 101 | 102 | return b.matcher 103 | } 104 | 105 | // exception returns true if the user agent is an exception 106 | func (b *Bot) exception() bool { 107 | for _, e := range b.exceptions { 108 | if strings.Contains(b.userAgent, e) { 109 | return true 110 | } 111 | } 112 | 113 | return false 114 | } 115 | 116 | // IsBot returns true if the user agent is a bot 117 | func (b *Bot) IsBot() bool { 118 | return b.getMatcher() != nil 119 | } 120 | 121 | // Name returns the name of the bot 122 | func (b *Bot) Name() string { 123 | if b.matcher == nil { 124 | return "" 125 | } 126 | 127 | return b.matcher.Name() 128 | } 129 | 130 | // Why returns the reason why the user agent is a bot 131 | // It will return an empty string if the user agent is not a bot 132 | // It will return the type of the bot matcher detected 133 | func (b *Bot) Why() string { 134 | if b.matcher == nil { 135 | return "" 136 | } 137 | 138 | return reflect.TypeOf(b.matcher).String() 139 | } 140 | -------------------------------------------------------------------------------- /bot_test.go: -------------------------------------------------------------------------------- 1 | package browser 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "strings" 8 | "testing" 9 | 10 | . "github.com/smartystreets/goconvey/convey" 11 | "gopkg.in/yaml.v2" 12 | ) 13 | 14 | // testBots is a map of user agent strings for each bot from the bots.yml file 15 | var testBots map[string]string 16 | 17 | // initTestBots reads the bots.yml file and 18 | // unmarshals it into the testPlatforms map. 19 | func initTestBots() { 20 | wd, err := os.Getwd() 21 | if err != nil { 22 | log.Fatalf("failed to get working directory: %v", err) 23 | } 24 | 25 | wd = strings.Split(wd, "/browser")[0] 26 | yamlFile, err := os.ReadFile(fmt.Sprintf("%s/browser/assets/test/bots.yml", wd)) 27 | if err != nil { 28 | log.Fatalf("failed to read file: %v", err) 29 | } 30 | 31 | var data map[string]string 32 | if err := yaml.Unmarshal(yamlFile, &data); err != nil { 33 | log.Fatalf("failed to unmarshal: %v", err) 34 | } 35 | 36 | testBots = data 37 | } 38 | 39 | func TestNewBot(t *testing.T) { 40 | Convey("Subject: #NewBot", t, func() { 41 | Convey("It returns a new Bot", func() { 42 | b, err := NewBot("") 43 | 44 | So(err, ShouldBeNil) 45 | So(b, ShouldHaveSameTypeAs, &Bot{}) 46 | }) 47 | }) 48 | } 49 | 50 | func TestBotIsBot(t *testing.T) { 51 | Convey("Subject: #IsBot", t, func() { 52 | Convey("When the user agent is a bot", func() { 53 | Convey("When the user agent is empty", func() { 54 | Convey("It returns true", func() { 55 | b, _ := NewBot("") 56 | 57 | So(b.IsBot(), ShouldBeTrue) 58 | }) 59 | }) 60 | 61 | Convey("When the user agent has bot keywords", func() { 62 | Convey("It returns true", func() { 63 | b, _ := NewBot(testBots["monitoring"]) 64 | 65 | So(b.IsBot(), ShouldBeTrue) 66 | }) 67 | }) 68 | 69 | Convey("When the user agent is a known bot", func() { 70 | Convey("It returns true", func() { 71 | b, _ := NewBot(testBots["apis-google"]) 72 | 73 | So(b.IsBot(), ShouldBeTrue) 74 | }) 75 | }) 76 | }) 77 | }) 78 | } 79 | 80 | func TestBotName(t *testing.T) { 81 | Convey("Subject: #Name", t, func() { 82 | Convey("When the user agent is a bot", func() { 83 | Convey("When the user agent is empty", func() { 84 | Convey("It returns Generic Bot", func() { 85 | b, _ := NewBot("") 86 | 87 | So(b.Name(), ShouldEqual, "Generic Bot") 88 | }) 89 | }) 90 | 91 | Convey("When the user agent is a known bot", func() { 92 | Convey("It returns the name of the bot", func() { 93 | b, _ := NewBot(testBots["apis-google"]) 94 | 95 | So(b.Name(), ShouldEqual, "APIs-Google") 96 | }) 97 | }) 98 | }) 99 | }) 100 | } 101 | 102 | func TestBotWhy(t *testing.T) { 103 | Convey("Subject: #Why", t, func() { 104 | Convey("When the user agent is a bot", func() { 105 | Convey("When the user agent is empty", func() { 106 | Convey("It returns an empty string", func() { 107 | b, _ := NewBot("") 108 | 109 | So(b.Why(), ShouldEqual, "*bots.Empty") 110 | }) 111 | }) 112 | 113 | Convey("When the user agent is a known bot", func() { 114 | Convey("It returns the type of bot", func() { 115 | b, _ := NewBot(testBots["apis-google"]) 116 | 117 | So(b.Why(), ShouldEqual, "*bots.Known") 118 | }) 119 | }) 120 | }) 121 | }) 122 | } 123 | -------------------------------------------------------------------------------- /bots/bots_test.go: -------------------------------------------------------------------------------- 1 | package bots 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "strings" 8 | "testing" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // testBots is a map of user agent strings for each bot from the bots.yml file 14 | var testBots map[string]string 15 | 16 | // initTestBots reads the bots.yml file and 17 | // unmarshals it into the testPlatforms map. 18 | func initTestBots() { 19 | wd, err := os.Getwd() 20 | if err != nil { 21 | log.Fatalf("failed to get working directory: %v", err) 22 | } 23 | 24 | wd = strings.Split(wd, "/browser")[0] 25 | yamlFile, err := os.ReadFile(fmt.Sprintf("%s/browser/assets/test/bots.yml", wd)) 26 | if err != nil { 27 | log.Fatalf("failed to read file: %v", err) 28 | } 29 | 30 | var data map[string]string 31 | if err := yaml.Unmarshal(yamlFile, &data); err != nil { 32 | log.Fatalf("failed to unmarshal: %v", err) 33 | } 34 | 35 | testBots = data 36 | } 37 | 38 | // TestMain is the entry point for running tests in this package. 39 | func TestMain(m *testing.M) { 40 | initTestBots() 41 | exitCode := m.Run() 42 | os.Exit(exitCode) 43 | } 44 | -------------------------------------------------------------------------------- /bots/empty.go: -------------------------------------------------------------------------------- 1 | package bots 2 | 3 | var genericBot = "Generic Bot" 4 | 5 | // Empty is a bot that matches when the user agent is empty. 6 | // An empty user agent is treated as a bot. 7 | type Empty struct { 8 | userAgent string 9 | } 10 | 11 | func NewEmpty(userAgent string) *Empty { 12 | return &Empty{ 13 | userAgent: userAgent, 14 | } 15 | } 16 | 17 | // Name returns genericBot. 18 | func (e *Empty) Name() string { 19 | return genericBot 20 | } 21 | 22 | // Match returns true if the user agent is empty. 23 | func (e *Empty) Match() bool { 24 | return e.userAgent == "" 25 | } 26 | -------------------------------------------------------------------------------- /bots/empty_test.go: -------------------------------------------------------------------------------- 1 | package bots 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewEmpty(t *testing.T) { 10 | Convey("Subject: #NewEmpty", t, func() { 11 | Convey("It should return an empty bot", func() { 12 | So(NewEmpty(testBots["googlebot"]), ShouldHaveSameTypeAs, &Empty{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestEmptyName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Generic Bot", func() { 20 | So(NewEmpty(testBots["googlebot"]).Name(), ShouldEqual, "Generic Bot") 21 | }) 22 | }) 23 | } 24 | 25 | func TestEmptyMatch(t *testing.T) { 26 | Convey("Subject: #Match", t, func() { 27 | Convey("When the user agent is empty", func() { 28 | Convey("It should return true", func() { 29 | So(NewEmpty("").Match(), ShouldBeTrue) 30 | }) 31 | }) 32 | 33 | Convey("When the user agent is not empty", func() { 34 | Convey("It should return false", func() { 35 | So(NewEmpty(testBots["googlebot"]).Match(), ShouldBeFalse) 36 | }) 37 | }) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /bots/keyword.go: -------------------------------------------------------------------------------- 1 | package bots 2 | 3 | import "regexp" 4 | 5 | // Keyword is a bot that matches when the user agent contains a keyword. 6 | type Keyword struct { 7 | userAgent string 8 | } 9 | 10 | var ( 11 | keywordMatchRegex = `(?i)(?:crawl|fetch|search|monitoring|spider|bot)` 12 | ) 13 | 14 | // NewKeyword returns a new Keyword bot. 15 | func NewKeyword(userAgent string) *Keyword { 16 | return &Keyword{ 17 | userAgent: userAgent, 18 | } 19 | } 20 | 21 | // Name returns genericBot. 22 | func (k *Keyword) Name() string { 23 | return genericBot 24 | } 25 | 26 | // Match returns true if the user agent contains a keyword. 27 | func (k *Keyword) Match() bool { 28 | return regexp.MustCompile(keywordMatchRegex).MatchString(k.userAgent) 29 | } 30 | -------------------------------------------------------------------------------- /bots/keyword_test.go: -------------------------------------------------------------------------------- 1 | package bots 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewKeyword(t *testing.T) { 10 | Convey("Subject: #NewKeyword", t, func() { 11 | Convey("It returns a Keyword bot", func() { 12 | k := NewKeyword("Googlebot") 13 | So(k, ShouldHaveSameTypeAs, &Keyword{}) 14 | }) 15 | }) 16 | } 17 | 18 | func TestKeywordName(t *testing.T) { 19 | Convey("Subject: #Name", t, func() { 20 | Convey("It returns the keyword that matched", func() { 21 | bots := []string{"crawl", "spider", "fetch", "datapack", "monitoring", "googlebot"} 22 | 23 | for _, v := range bots { 24 | k := NewKeyword(testBots[v]) 25 | So(k.Name(), ShouldEqual, "Generic Bot") 26 | } 27 | }) 28 | }) 29 | } 30 | 31 | func TestKeywordMatch(t *testing.T) { 32 | Convey("Subject: #Match", t, func() { 33 | Convey("When the user agent contains a keyword", func() { 34 | Convey("It returns true", func() { 35 | bots := []string{"crawl", "spider", "fetch", "datapack", "monitoring", "googlebot"} 36 | 37 | for _, b := range bots { 38 | So(NewKeyword(testBots[b]).Match(), ShouldBeTrue) 39 | } 40 | }) 41 | }) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /bots/known.go: -------------------------------------------------------------------------------- 1 | package bots 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | // Known is a bot that matches when the user agent contains a keyword. 8 | // It is used for bots that are known to be bots 9 | type Known struct { 10 | userAgent string 11 | bots map[string]string 12 | matchedBot string 13 | } 14 | 15 | // NewKnown returns a new Known bot. 16 | func NewKnown(userAgent string, bots map[string]string) *Known { 17 | return &Known{ 18 | userAgent: userAgent, 19 | bots: bots, 20 | } 21 | } 22 | 23 | // Name returns the keyword that matched. 24 | func (k *Known) Name() string { 25 | if !k.matched() { 26 | k.Match() 27 | } 28 | 29 | return k.matchedBot 30 | } 31 | 32 | // Match returns true if the user agent contains a keyword. 33 | func (k *Known) Match() bool { 34 | if k.matched() { 35 | return true 36 | } 37 | 38 | for b, n := range k.bots { 39 | rg := regexp.MustCompile(`(?i)` + b) 40 | if rg.MatchString(k.userAgent) { 41 | k.matchedBot = n 42 | return true 43 | } 44 | } 45 | return false 46 | } 47 | 48 | func (k *Known) matched() bool { 49 | return k.matchedBot != "" 50 | } 51 | -------------------------------------------------------------------------------- /bots/known_test.go: -------------------------------------------------------------------------------- 1 | package bots 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewKnown(t *testing.T) { 10 | Convey("Subject: #NewKnown", t, func() { 11 | Convey("It returns a Known bot", func() { 12 | k := NewKnown("Googlebot", nil) 13 | So(k, ShouldHaveSameTypeAs, &Known{}) 14 | }) 15 | }) 16 | } 17 | 18 | func TestKnownName(t *testing.T) { 19 | Convey("Subject: #Name", t, func() { 20 | Convey("It returns the name of matched bot", func() { 21 | bots := map[string]string{ 22 | "apis-google": "APIs-Google", 23 | "apache-httpclient": "Java http library", 24 | "barkrowler": "Barkrowler", 25 | "bingbot": "Microsoft Bing", 26 | } 27 | 28 | for key, val := range bots { 29 | k := NewKnown(testBots[key], bots) 30 | 31 | So(k.Name(), ShouldEqual, val) 32 | } 33 | }) 34 | }) 35 | } 36 | 37 | func TestKnownMatch(t *testing.T) { 38 | Convey("Subject: #Match", t, func() { 39 | known := map[string]string{ 40 | "apis-google": "APIs-Google", 41 | "apache-httpclient": "Java http library", 42 | "barkrowler": "Barkrowler", 43 | "bingbot": "Microsoft Bing", 44 | } 45 | Convey("When user agent contains a keyword", func() { 46 | Convey("It returns true", func() { 47 | for key, _ := range known { 48 | k := NewKnown(testBots[key], known) 49 | 50 | So(k.Match(), ShouldBeTrue) 51 | } 52 | }) 53 | }) 54 | 55 | Convey("When user agent does not contain a keyword", func() { 56 | Convey("It returns false", func() { 57 | u := "Mozilla/5.0 (Linux; Android 10.0.0; Nexus 7 Build/OPM1.171019.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3359.158 Safari/537.36" 58 | So(NewKnown(u, known).Match(), ShouldBeFalse) 59 | }) 60 | }) 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /devices/android.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | type Android struct { 8 | p Parser 9 | } 10 | 11 | var ( 12 | androidNameRegex = `(?i)\(Linux.*?; Android.*?; ([-_a-z0-9 ]+)(?:;)? Build[^)]+\)` 13 | androidMatchRegex = []string{`(?i)Android`} 14 | ) 15 | 16 | func NewAndroid(p Parser) *Android { 17 | return &Android{ 18 | p: p, 19 | } 20 | } 21 | 22 | func (a Android) Name() string { 23 | re := regexp.MustCompile(androidNameRegex) 24 | matches := re.FindStringSubmatch(a.p.String()) 25 | if len(matches) > 1 { 26 | return matches[1] 27 | } 28 | 29 | return "Unknown" 30 | } 31 | 32 | func (a Android) Match() bool { 33 | return a.p.Match(androidMatchRegex) 34 | } 35 | -------------------------------------------------------------------------------- /devices/blackberry_playbook.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type BlackberryPlaybook struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | bbPlaybookName = "BlackBerry Playbook" 9 | bbPlaybookMatchRegex = []string{`PlayBook.*?RIM Tablet`} 10 | ) 11 | 12 | func NewBlackberryPlaybook(p Parser) *BlackberryPlaybook { 13 | return &BlackberryPlaybook{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (b *BlackberryPlaybook) Name() string { 19 | return bbPlaybookName 20 | } 21 | 22 | func (b *BlackberryPlaybook) Match() bool { 23 | return b.p.Match(bbPlaybookMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/blackberry_playbook_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewBlackberryPlaybook(t *testing.T) { 10 | Convey("Subject: #NewBlackberryPlaybook", t, func() { 11 | Convey("It should return a new BlackberryPlaybook instance", func() { 12 | So(NewBlackberryPlaybook(NewUAParser("")), ShouldHaveSameTypeAs, &BlackberryPlaybook{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestBlackberryPlaybookName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return BlackBerry PlayBook", func() { 20 | s := NewBlackberryPlaybook(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "BlackBerry Playbook") 22 | }) 23 | }) 24 | } 25 | 26 | func TestBlackberryPlaybookMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewBlackberryPlaybook(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeTrue) 31 | So(NewBlackberryPlaybook(NewUAParser(testDevices["bb-playbook-2"])).Match(), ShouldBeTrue) 32 | }) 33 | }) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /devices/devices_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "strings" 8 | "testing" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // testDevices is a map of user agent strings for each device from the devices.yml file 14 | var testDevices map[string]string 15 | 16 | // initTestDevices reads the devices.yml file and 17 | // unmarshals it into the testDevices map. 18 | func initTestDevices() { 19 | wd, err := os.Getwd() 20 | if err != nil { 21 | log.Fatalf("failed to get working directory: %v", err) 22 | } 23 | 24 | wd = strings.Split(wd, "/browser")[0] 25 | yamlFile, err := os.ReadFile(fmt.Sprintf("%s/browser/assets/test/devices.yml", wd)) 26 | if err != nil { 27 | log.Fatalf("failed to read file: %v", err) 28 | } 29 | 30 | var data map[string]string 31 | if err := yaml.Unmarshal(yamlFile, &data); err != nil { 32 | log.Fatalf("failed to unmarshal: %v", err) 33 | } 34 | 35 | testDevices = data 36 | } 37 | 38 | // TestMain is the entry point for running tests in this package. 39 | func TestMain(m *testing.M) { 40 | initTestDevices() 41 | exitCode := m.Run() 42 | os.Exit(exitCode) 43 | } 44 | -------------------------------------------------------------------------------- /devices/ipad.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type Ipad struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | iPadName = "iPad" 9 | iPadMatchRegex = []string{`iPad`} 10 | ) 11 | 12 | func NewIpad(p Parser) *Ipad { 13 | return &Ipad{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (i *Ipad) Name() string { 19 | return iPadName 20 | } 21 | 22 | func (i *Ipad) Match() bool { 23 | return i.p.Match(iPadMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/ipad_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewIpad(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | So(NewIpad(NewUAParser(testDevices["ipad-1"])), ShouldHaveSameTypeAs, &Ipad{}) 12 | }) 13 | } 14 | 15 | func TestIpadName(t *testing.T) { 16 | Convey("Subject: #Name", t, func() { 17 | Convey("It should return iPad", func() { 18 | s := NewIpad(NewUAParser("")) 19 | So(s.Name(), ShouldEqual, "iPad") 20 | }) 21 | }) 22 | } 23 | 24 | func TestIpadMatch(t *testing.T) { 25 | Convey("Subject: #Match", t, func() { 26 | Convey("When the user agent matches", func() { 27 | Convey("It should return true", func() { 28 | So(NewIpad(NewUAParser(testDevices["ipad-1"])).Match(), ShouldBeTrue) 29 | So(NewIpad(NewUAParser(testDevices["ipad-2"])).Match(), ShouldBeTrue) 30 | So(NewIpad(NewUAParser(testDevices["ipad-3"])).Match(), ShouldBeTrue) 31 | So(NewIpad(NewUAParser(testDevices["ipad-4"])).Match(), ShouldBeTrue) 32 | So(NewIpad(NewUAParser(testDevices["ipad-5"])).Match(), ShouldBeTrue) 33 | }) 34 | }) 35 | 36 | Convey("When the user agent doesn't match", func() { 37 | Convey("It should return false", func() { 38 | So(NewIpad(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 39 | }) 40 | }) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /devices/iphone.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type Iphone struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | iPhoneName = "iPhone" 9 | iPhoneMatchRegex = []string{`iPhone`} 10 | ) 11 | 12 | func NewIphone(p Parser) *Iphone { 13 | return &Iphone{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (i *Iphone) Name() string { 19 | return iPhoneName 20 | } 21 | 22 | func (i *Iphone) Match() bool { 23 | return i.p.Match(iPhoneMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/iphone_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewIphone(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | So(NewIphone(NewUAParser(testDevices["iphone-1"])), ShouldHaveSameTypeAs, &Iphone{}) 12 | }) 13 | } 14 | 15 | func TestIphoneName(t *testing.T) { 16 | Convey("Subject: #Name", t, func() { 17 | Convey("It should return iPhone", func() { 18 | s := NewIphone(NewUAParser("")) 19 | So(s.Name(), ShouldEqual, "iPhone") 20 | }) 21 | }) 22 | } 23 | 24 | func TestIphoneMatch(t *testing.T) { 25 | Convey("Subject: #Match", t, func() { 26 | Convey("When the user agent matches", func() { 27 | Convey("It should return true", func() { 28 | So(NewIphone(NewUAParser(testDevices["iphone-1"])).Match(), ShouldBeTrue) 29 | So(NewIphone(NewUAParser(testDevices["iphone-2"])).Match(), ShouldBeTrue) 30 | So(NewIphone(NewUAParser(testDevices["iphone-3"])).Match(), ShouldBeTrue) 31 | So(NewIphone(NewUAParser(testDevices["iphone-4"])).Match(), ShouldBeTrue) 32 | So(NewIphone(NewUAParser(testDevices["iphone-5"])).Match(), ShouldBeTrue) 33 | So(NewIphone(NewUAParser(testDevices["iphone-6"])).Match(), ShouldBeTrue) 34 | }) 35 | }) 36 | 37 | Convey("When the user agent doesn't match", func() { 38 | Convey("It should return false", func() { 39 | So(NewIphone(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 40 | }) 41 | }) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /devices/ipod_touch.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type IpodTouch struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | ipodTouchName = "iPod Touch" 9 | ipodTouchMatchRegex = []string{`(?i)iPod`} 10 | ) 11 | 12 | func NewIpodTouch(p Parser) *IpodTouch { 13 | return &IpodTouch{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (i *IpodTouch) Name() string { 19 | return ipodTouchName 20 | } 21 | 22 | func (i *IpodTouch) Match() bool { 23 | return i.p.Match(ipodTouchMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/ipod_touch_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewIpodTouch(t *testing.T) { 10 | Convey("Subject: #NewIpodTouch", t, func() { 11 | Convey("It should return a new IpodTouch instance", func() { 12 | So(NewIpodTouch(NewUAParser("")), ShouldHaveSameTypeAs, &IpodTouch{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestIpodTouchName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return iPod Touch", func() { 20 | s := NewIpodTouch(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "iPod Touch") 22 | }) 23 | }) 24 | } 25 | 26 | func TestIpodTouchMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewIpodTouch(NewUAParser(testDevices["ipod-touch-1"])).Match(), ShouldBeTrue) 31 | So(NewIpodTouch(NewUAParser(testDevices["ipod-touch-2"])).Match(), ShouldBeTrue) 32 | }) 33 | }) 34 | 35 | Convey("When the user agent does not match", func() { 36 | Convey("It should return false", func() { 37 | So(NewIpodTouch(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 38 | }) 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /devices/kindle.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type Kindle struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | kindleName = "Kindle" 9 | kindleMatchRegex = []string{`Kindle`} 10 | ) 11 | 12 | func NewKindle(p Parser) *Kindle { 13 | return &Kindle{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (k *Kindle) Name() string { 19 | return kindleName 20 | } 21 | 22 | func (k *Kindle) Match() bool { 23 | return k.p.Match(kindleMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/kindle_fire.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type KindleFire struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | kindleFireName = "Kindle Fire" 9 | kindleFireMatchRegex = []string{`Kindle Fire|KFTT`} 10 | ) 11 | 12 | func NewKindleFire(p Parser) *KindleFire { 13 | return &KindleFire{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (k *KindleFire) Name() string { 19 | return kindleFireName 20 | } 21 | 22 | func (k *KindleFire) Match() bool { 23 | return k.p.Match(kindleFireMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/kindle_fire_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewKindleFire(t *testing.T) { 10 | Convey("Subject: #NewKindleFire", t, func() { 11 | Convey("It should return a new KindleFire instance", func() { 12 | So(NewKindleFire(NewUAParser("")), ShouldHaveSameTypeAs, &KindleFire{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestKindleFireName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Kindle Fire", func() { 20 | s := NewKindleFire(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "Kindle Fire") 22 | }) 23 | }) 24 | } 25 | 26 | func TestKindleFireMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewKindleFire(NewUAParser(testDevices["kindle-fire-1"])).Match(), ShouldBeTrue) 31 | So(NewKindleFire(NewUAParser(testDevices["kindle-fire-2"])).Match(), ShouldBeTrue) 32 | }) 33 | }) 34 | 35 | Convey("When the user agent does not match", func() { 36 | Convey("It should return false", func() { 37 | So(NewKindleFire(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 38 | }) 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /devices/kindle_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewKindle(t *testing.T) { 10 | Convey("Subject: #NewKindle", t, func() { 11 | Convey("It should return a new Kindle instance", func() { 12 | So(NewKindle(NewUAParser("")), ShouldHaveSameTypeAs, &Kindle{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestKindleName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Kindle", func() { 20 | s := NewKindle(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "Kindle") 22 | }) 23 | }) 24 | } 25 | 26 | func TestKindleMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewKindle(NewUAParser(testDevices["kindle-1"])).Match(), ShouldBeTrue) 31 | So(NewKindle(NewUAParser(testDevices["kindle-2"])).Match(), ShouldBeTrue) 32 | }) 33 | }) 34 | 35 | Convey("When the user agent does not match", func() { 36 | Convey("It should return false", func() { 37 | So(NewKindle(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 38 | }) 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /devices/parser.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import "regexp" 4 | 5 | type Parser interface { 6 | Match([]string) bool 7 | String() string 8 | } 9 | 10 | // compile time check if UAParser implements Parser interface 11 | var _ Parser = (*UAParser)(nil) 12 | 13 | type UAParser struct { 14 | userAgent string 15 | } 16 | 17 | func NewUAParser(userAgent string) *UAParser { 18 | return &UAParser{ 19 | userAgent: userAgent, 20 | } 21 | } 22 | 23 | func (u UAParser) String() string { 24 | return u.userAgent 25 | } 26 | 27 | // match returns true if the user agent matches the pattern. 28 | // The pattern is a list of regular expressions. 29 | func (u UAParser) Match(pattern []string) bool { 30 | for _, p := range pattern { 31 | re := regexp.MustCompile(p) 32 | if re.MatchString(u.userAgent) { 33 | return true 34 | } 35 | } 36 | 37 | return false 38 | } 39 | -------------------------------------------------------------------------------- /devices/playstation_3.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type PlayStation3 struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | playStation3Name = "PlayStation 3" 9 | playStation3MatchRegex = []string{`(?i)PLAYSTATION 3`} 10 | ) 11 | 12 | func NewPlayStation3(p Parser) *PlayStation3 { 13 | return &PlayStation3{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (p *PlayStation3) Name() string { 19 | return playStation3Name 20 | } 21 | 22 | func (p *PlayStation3) Match() bool { 23 | return p.p.Match(playStation3MatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/playstation_3_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewPlayStation3(t *testing.T) { 10 | Convey("Subject: #NewPlayStation3", t, func() { 11 | Convey("It should return a new PlayStation3 instance", func() { 12 | So(NewPlayStation3(NewUAParser("")), ShouldHaveSameTypeAs, &PlayStation3{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestPlayStation3Name(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return PlayStation 3", func() { 20 | s := NewPlayStation3(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "PlayStation 3") 22 | }) 23 | }) 24 | } 25 | 26 | func TestPlayStation3Match(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewPlayStation3(NewUAParser(testDevices["ps3"])).Match(), ShouldBeTrue) 31 | }) 32 | }) 33 | 34 | Convey("When the user agent does not match", func() { 35 | Convey("It should return false", func() { 36 | So(NewPlayStation3(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 37 | }) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /devices/playstation_4.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type PlayStation4 struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | playStation4Name = "PlayStation 4" 9 | playStation4MatchRegex = []string{`(?i)PLAYSTATION 4`} 10 | ) 11 | 12 | func NewPlayStation4(p Parser) *PlayStation4 { 13 | return &PlayStation4{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (p *PlayStation4) Name() string { 19 | return playStation4Name 20 | } 21 | 22 | func (p *PlayStation4) Match() bool { 23 | return p.p.Match(playStation4MatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/playstation_4_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewPlayStation4(t *testing.T) { 10 | Convey("Subject: #NewPlayStation4", t, func() { 11 | Convey("It should return a new PlayStation4 instance", func() { 12 | So(NewPlayStation4(NewUAParser("")), ShouldHaveSameTypeAs, &PlayStation4{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestPlayStation4Name(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return PlayStation 4", func() { 20 | s := NewPlayStation4(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "PlayStation 4") 22 | }) 23 | }) 24 | } 25 | 26 | func TestPlayStation4Match(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewPlayStation4(NewUAParser(testDevices["ps4"])).Match(), ShouldBeTrue) 31 | }) 32 | }) 33 | 34 | Convey("When the user agent does not match", func() { 35 | Convey("It should return false", func() { 36 | So(NewPlayStation4(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 37 | }) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /devices/playstation_5.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type PlayStation5 struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | playStation5Name = "PlayStation 5" 9 | playStation5MatchRegex = []string{`(?i)PlayStation 5`} 10 | ) 11 | 12 | func NewPlayStation5(p Parser) *PlayStation5 { 13 | return &PlayStation5{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (p *PlayStation5) Name() string { 19 | return playStation5Name 20 | } 21 | 22 | func (p *PlayStation5) Match() bool { 23 | return p.p.Match(playStation5MatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/playstation_5_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewPlayStation5(t *testing.T) { 10 | Convey("Subject: #NewPlayStation5", t, func() { 11 | Convey("It should return a new PlayStation5 instance", func() { 12 | So(NewPlayStation5(NewUAParser("")), ShouldHaveSameTypeAs, &PlayStation5{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestPlayStation5Name(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return PlayStation 5", func() { 20 | s := NewPlayStation5(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "PlayStation 5") 22 | }) 23 | }) 24 | } 25 | 26 | func TestPlayStation5Match(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewPlayStation5(NewUAParser(testDevices["ps5"])).Match(), ShouldBeTrue) 31 | }) 32 | }) 33 | 34 | Convey("When the user agent does not match", func() { 35 | Convey("It should return false", func() { 36 | So(NewPlayStation5(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 37 | }) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /devices/ps_vita.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type PSVita struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | psVitaName = "PlayStation Vita" 9 | psVitaMatchRegex = []string{`(?i)PlayStation Vita`} 10 | ) 11 | 12 | func NewPSVita(p Parser) *PSVita { 13 | return &PSVita{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (p *PSVita) Name() string { 19 | return psVitaName 20 | } 21 | 22 | func (p *PSVita) Match() bool { 23 | return p.p.Match(psVitaMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/ps_vita_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewPSViat(t *testing.T) { 10 | Convey("Subject: #NewPSVita", t, func() { 11 | Convey("It should return a new PSVita instance", func() { 12 | So(NewPSVita(NewUAParser("")), ShouldHaveSameTypeAs, &PSVita{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestPSVitaName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return PlayStation Vita", func() { 20 | s := NewPSVita(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "PlayStation Vita") 22 | }) 23 | }) 24 | } 25 | 26 | func TestPSVitaMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewPSVita(NewUAParser(testDevices["ps-vita-1"])).Match(), ShouldBeTrue) 31 | So(NewPSVita(NewUAParser(testDevices["ps-vita-2"])).Match(), ShouldBeTrue) 32 | So(NewPSVita(NewUAParser(testDevices["ps-vita-3"])).Match(), ShouldBeTrue) 33 | }) 34 | }) 35 | 36 | Convey("When the user agent does not match", func() { 37 | Convey("It should return false", func() { 38 | So(NewPSVita(NewUAParser(testDevices["ps5"])).Match(), ShouldBeFalse) 39 | }) 40 | }) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /devices/psp.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type PSP struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | pspName = "PlayStation Portable" 9 | pspMatchRegex = []string{`(?i)PlayStation Portable`} 10 | ) 11 | 12 | func NewPSP(p Parser) *PSP { 13 | return &PSP{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (p *PSP) Name() string { 19 | return pspName 20 | } 21 | 22 | func (p *PSP) Match() bool { 23 | return p.p.Match(pspMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/psp_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewPSP(t *testing.T) { 10 | Convey("Subject: #NewPSP", t, func() { 11 | Convey("It should return a new PSP instance", func() { 12 | So(NewPSP(NewUAParser("")), ShouldHaveSameTypeAs, &PSP{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestPSPName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return PlayStation Portable", func() { 20 | s := NewPSP(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "PlayStation Portable") 22 | }) 23 | }) 24 | } 25 | 26 | func TestPSPMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewPSP(NewUAParser(testDevices["psp-1"])).Match(), ShouldBeTrue) 31 | So(NewPSP(NewUAParser(testDevices["psp-2"])).Match(), ShouldBeTrue) 32 | So(NewPSP(NewUAParser(testDevices["psp-3"])).Match(), ShouldBeTrue) 33 | }) 34 | }) 35 | 36 | Convey("When the user agent does not match", func() { 37 | Convey("It should return false", func() { 38 | So(NewPSP(NewUAParser(testDevices["ps5"])).Match(), ShouldBeFalse) 39 | }) 40 | }) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /devices/samsung.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | type Samsung struct { 8 | p Parser 9 | matches []string 10 | } 11 | 12 | // NewSamsung creates a new Samsung platform. 13 | func NewSamsung(p Parser) *Samsung { 14 | return &Samsung{ 15 | p: p, 16 | matches: make([]string, 0), 17 | } 18 | } 19 | 20 | var ( 21 | samsungName = "Samsung" 22 | samsungMatchRegex = `(?i)(?:Linux.*?; Android.*?; (?:SAMSUNG )?(SM-[A-Z0-9]+).*?)` 23 | ) 24 | 25 | func (s *Samsung) Name() string { 26 | s.registerMatches() 27 | if len(s.matches) > 1 { 28 | return samsungName + " " + s.matches[1] 29 | } 30 | 31 | return samsungName 32 | } 33 | 34 | func (s *Samsung) Match() bool { 35 | s.registerMatches() 36 | return len(s.matches) > 0 37 | } 38 | 39 | func (s *Samsung) registerMatches() { 40 | if len(s.matches) > 0 { 41 | return 42 | } 43 | 44 | re := regexp.MustCompile(samsungMatchRegex) 45 | matches := re.FindStringSubmatch(s.p.String()) 46 | if len(matches) > 0 { 47 | s.matches = matches 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /devices/samsung_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewSamsung(t *testing.T) { 10 | Convey("Subject: #NewSamsung", t, func() { 11 | Convey("It returns a Samsung platform", func() { 12 | s := NewSamsung(NewUAParser(testDevices["samsung-1"])) 13 | So(s, ShouldHaveSameTypeAs, &Samsung{}) 14 | }) 15 | }) 16 | } 17 | 18 | func TestSamsungName(t *testing.T) { 19 | Convey("Subject: #Name", t, func() { 20 | Convey("It returns the correct name", func() { 21 | So(NewSamsung(NewUAParser(testDevices["samsung-1"])).Name(), ShouldEqual, "Samsung SM-N900") 22 | So(NewSamsung(NewUAParser(testDevices["samsung-2"])).Name(), ShouldEqual, "Samsung SM-J700F") 23 | So(NewSamsung(NewUAParser(testDevices["samsung-3"])).Name(), ShouldEqual, "Samsung SM-G928K") 24 | }) 25 | }) 26 | } 27 | 28 | func TestSamsungMatch(t *testing.T) { 29 | Convey("Subject: #Match", t, func() { 30 | Convey("When the user agent matches", func() { 31 | Convey("It returns true", func() { 32 | So(NewSamsung(NewUAParser(testDevices["samsung-1"])).Match(), ShouldBeTrue) 33 | So(NewSamsung(NewUAParser(testDevices["samsung-2"])).Match(), ShouldBeTrue) 34 | So(NewSamsung(NewUAParser(testDevices["samsung-3"])).Match(), ShouldBeTrue) 35 | }) 36 | }) 37 | 38 | Convey("When the user agent does not match", func() { 39 | Convey("It returns false", func() { 40 | s := NewSamsung(NewUAParser(testDevices["iphone-7"])) 41 | So(s.Match(), ShouldBeFalse) 42 | }) 43 | }) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /devices/surface.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import "strings" 4 | 5 | type Surface struct { 6 | p Parser 7 | } 8 | 9 | var ( 10 | surfaceName = "Surface" 11 | surfaceWindowsRTRegex = []string{`Windows NT\s*(6\.[2-3]); ARM;`} // Windows RT 8.0 and 8.1 12 | ) 13 | 14 | func NewSurface(p Parser) *Surface { 15 | return &Surface{ 16 | p: p, 17 | } 18 | } 19 | 20 | func (s *Surface) Name() string { 21 | return surfaceName 22 | } 23 | 24 | func (s *Surface) Match() bool { 25 | // Matches Touch and Windows RT 26 | return strings.Contains(s.p.String(), "Touch") && s.p.Match(surfaceWindowsRTRegex) 27 | } 28 | -------------------------------------------------------------------------------- /devices/surface_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewSurface(t *testing.T) { 10 | Convey("Subject: #NewSurface", t, func() { 11 | Convey("It should return a new Surface instance", func() { 12 | So(NewSurface(NewUAParser("")), ShouldHaveSameTypeAs, &Surface{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestSurfaceName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Surface", func() { 20 | s := NewSurface(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "Surface") 22 | }) 23 | }) 24 | } 25 | 26 | func TestSurfaceMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewSurface(NewUAParser(testDevices["surface-1"])).Match(), ShouldBeTrue) 31 | So(NewSurface(NewUAParser(testDevices["surface-2"])).Match(), ShouldBeTrue) 32 | }) 33 | }) 34 | 35 | Convey("When the user agent does not match", func() { 36 | Convey("It should return false", func() { 37 | So(NewSurface(NewUAParser(testDevices["ps5"])).Match(), ShouldBeFalse) 38 | }) 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /devices/switch.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type Switch struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | switchName = "Nintendo Switch" 9 | switchMatchRegex = []string{`(?i)Nintendo Switch`} 10 | ) 11 | 12 | func NewSwitch(p Parser) *Switch { 13 | return &Switch{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (s *Switch) Name() string { 19 | return switchName 20 | } 21 | 22 | func (s *Switch) Match() bool { 23 | return s.p.Match(switchMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/switch_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewSwitch(t *testing.T) { 10 | Convey("Subject: #NewSwitch", t, func() { 11 | Convey("It should return a new Switch instance", func() { 12 | So(NewSwitch(NewUAParser("")), ShouldHaveSameTypeAs, &Switch{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestSwitchName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Nintendo Switch", func() { 20 | s := NewSwitch(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "Nintendo Switch") 22 | }) 23 | }) 24 | } 25 | 26 | func TestSwitchMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewSwitch(NewUAParser(testDevices["switch-1"])).Match(), ShouldBeTrue) 31 | So(NewSwitch(NewUAParser(testDevices["switch-2"])).Match(), ShouldBeTrue) 32 | So(NewSwitch(NewUAParser(testDevices["switch-3"])).Match(), ShouldBeTrue) 33 | }) 34 | }) 35 | 36 | Convey("When the user agent does not match", func() { 37 | Convey("It should return false", func() { 38 | So(NewSwitch(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 39 | }) 40 | }) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /devices/tv.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type TV struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | tvName = "TV" 9 | tvMatchRegex = []string{`(?i)(\btv|Android.*?ADT-1|Nexus Player)`} 10 | ) 11 | 12 | func NewTV(p Parser) *TV { 13 | return &TV{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (t *TV) Name() string { 19 | return tvName 20 | } 21 | 22 | func (t *TV) Match() bool { 23 | return t.p.Match(tvMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/tv_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewTV(t *testing.T) { 10 | Convey("Subject: #NewTv", t, func() { 11 | Convey("It should return a new Tv instance", func() { 12 | So(NewTV(NewUAParser("")), ShouldHaveSameTypeAs, &TV{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestTVName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return TV", func() { 20 | s := NewTV(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "TV") 22 | }) 23 | }) 24 | } 25 | 26 | func TestTVMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("When the user agent matches", func() { 29 | Convey("It should return true", func() { 30 | So(NewTV(NewUAParser(testDevices["tv-1"])).Match(), ShouldBeTrue) 31 | So(NewTV(NewUAParser(testDevices["tv-2"])).Match(), ShouldBeTrue) 32 | }) 33 | }) 34 | 35 | Convey("When the user agent does not match", func() { 36 | Convey("It should return false", func() { 37 | So(NewSwitch(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 38 | }) 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /devices/unknown.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | // Unknown represents an unknown device. 4 | // These are devices that we don't know about yet. These are matched last. 5 | type Unknown struct { 6 | p Parser 7 | } 8 | 9 | var unknownName = "Unknown" 10 | 11 | func NewUnknown(p Parser) *Unknown { 12 | return &Unknown{ 13 | p: p, 14 | } 15 | } 16 | 17 | func (u Unknown) Name() string { 18 | return unknownName 19 | } 20 | 21 | func (u Unknown) Match() bool { 22 | return true 23 | } 24 | -------------------------------------------------------------------------------- /devices/unknown_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewUnknown(t *testing.T) { 10 | Convey("Subject: #NewUnknown", t, func() { 11 | Convey("It should return a new Unknown instance", func() { 12 | So(NewUnknown(NewUAParser("")), ShouldHaveSameTypeAs, &Unknown{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestUnknownName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Unknown", func() { 20 | s := NewUnknown(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "Unknown") 22 | }) 23 | }) 24 | } 25 | 26 | func TestUnknownMatch(t *testing.T) { 27 | Convey("Subject: #Match", t, func() { 28 | Convey("It should always return true", func() { 29 | So(NewUnknown(NewUAParser("")).Match(), ShouldBeTrue) 30 | }) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /devices/wii.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type Wii struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | wiiName = "Nintendo Wii" 9 | wiiMatchRegex = []string{`(?i)Nintendo Wii`} 10 | ) 11 | 12 | func NewWii(p Parser) *Wii { 13 | return &Wii{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (w *Wii) Name() string { 19 | return wiiName 20 | } 21 | 22 | func (w *Wii) Match() bool { 23 | return w.p.Match(wiiMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/wii_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewWii(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | So(NewWii(NewUAParser(testDevices["wii-1"])), ShouldHaveSameTypeAs, &Wii{}) 12 | }) 13 | } 14 | 15 | func TestWiiName(t *testing.T) { 16 | Convey("Subject: #Name", t, func() { 17 | Convey("It should return Wii", func() { 18 | s := NewWii(NewUAParser("")) 19 | So(s.Name(), ShouldEqual, "Nintendo Wii") 20 | }) 21 | }) 22 | } 23 | 24 | func TestWiiMatch(t *testing.T) { 25 | Convey("Subject: #Match", t, func() { 26 | Convey("When the user agent matches", func() { 27 | Convey("It should return true", func() { 28 | So(NewWii(NewUAParser(testDevices["wii-1"])).Match(), ShouldBeTrue) 29 | So(NewWii(NewUAParser(testDevices["wii-2"])).Match(), ShouldBeTrue) 30 | So(NewWii(NewUAParser(testDevices["wii-3"])).Match(), ShouldBeTrue) 31 | }) 32 | }) 33 | 34 | Convey("When the user agent does not match", func() { 35 | Convey("It should return false", func() { 36 | So(NewWii(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 37 | }) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /devices/wii_u.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type WiiU struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | wiiuName = "Nintendo WiiU" 9 | wiiUMatchRegex = []string{`(?i)Nintendo WiiU`} 10 | ) 11 | 12 | func NewWiiU(p Parser) *WiiU { 13 | return &WiiU{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (w *WiiU) Name() string { 19 | return wiiuName 20 | } 21 | 22 | func (w *WiiU) Match() bool { 23 | return w.p.Match(wiiUMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/wii_u_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewWiiU(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | So(NewWiiU(NewUAParser(testDevices["wiiu-1"])), ShouldHaveSameTypeAs, &WiiU{}) 12 | }) 13 | } 14 | 15 | func TestWiiUName(t *testing.T) { 16 | Convey("Subject: #Name", t, func() { 17 | Convey("It should return WiiU", func() { 18 | s := NewWiiU(NewUAParser("")) 19 | So(s.Name(), ShouldEqual, "Nintendo WiiU") 20 | }) 21 | }) 22 | } 23 | 24 | func TestWiiUMatch(t *testing.T) { 25 | Convey("Subject: #Match", t, func() { 26 | Convey("When the user agent matches", func() { 27 | Convey("It should return true", func() { 28 | So(NewWiiU(NewUAParser(testDevices["wiiu-1"])).Match(), ShouldBeTrue) 29 | So(NewWiiU(NewUAParser(testDevices["wiiu-2"])).Match(), ShouldBeTrue) 30 | So(NewWiiU(NewUAParser(testDevices["wiiu-3"])).Match(), ShouldBeTrue) 31 | }) 32 | }) 33 | 34 | Convey("When the user agent does not match", func() { 35 | Convey("It should return false", func() { 36 | So(NewWiiU(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 37 | }) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /devices/xbox_360.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type Xbox360 struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | xbox360Name = "Xbox 360" 9 | xbox360MatchRegex = []string{`(?i)Xbox`} 10 | ) 11 | 12 | func NewXbox360(p Parser) *Xbox360 { 13 | return &Xbox360{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (x *Xbox360) Name() string { 19 | return xbox360Name 20 | } 21 | 22 | func (x *Xbox360) Match() bool { 23 | return x.p.Match(xbox360MatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/xbox_360_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewXbox360(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | So(NewXbox360(NewUAParser(testDevices["xbox360-1"])), ShouldHaveSameTypeAs, &Xbox360{}) 12 | }) 13 | } 14 | 15 | func TestXbox360Name(t *testing.T) { 16 | Convey("Subject: #Name", t, func() { 17 | Convey("It should return Xbox 360", func() { 18 | s := NewXbox360(NewUAParser("")) 19 | So(s.Name(), ShouldEqual, "Xbox 360") 20 | }) 21 | }) 22 | } 23 | 24 | func TestXbox360Match(t *testing.T) { 25 | Convey("Subject: #Match", t, func() { 26 | Convey("When the user agent matches", func() { 27 | Convey("It should return true", func() { 28 | So(NewXbox360(NewUAParser(testDevices["xbox360-1"])).Match(), ShouldBeTrue) 29 | So(NewXbox360(NewUAParser(testDevices["xbox360-2"])).Match(), ShouldBeTrue) 30 | So(NewXbox360(NewUAParser(testDevices["xbox360-3"])).Match(), ShouldBeTrue) 31 | }) 32 | }) 33 | 34 | Convey("When the user agent does not match", func() { 35 | Convey("It should return false", func() { 36 | So(NewXbox360(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 37 | }) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /devices/xbox_one.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | type XboxOne struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | xboxOneName = "Xbox One" 9 | xboxOneMatchRegex = []string{`(?i)Xbox One`} 10 | ) 11 | 12 | func NewXboxOne(p Parser) *XboxOne { 13 | return &XboxOne{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (x *XboxOne) Name() string { 19 | return xboxOneName 20 | } 21 | 22 | func (x *XboxOne) Match() bool { 23 | return x.p.Match(xboxOneMatchRegex) 24 | } 25 | -------------------------------------------------------------------------------- /devices/xbox_one_test.go: -------------------------------------------------------------------------------- 1 | package devices 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewXboxOne(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | So(NewXboxOne(NewUAParser(testDevices["xbox-one-1"])), ShouldHaveSameTypeAs, &XboxOne{}) 12 | }) 13 | } 14 | 15 | func TestXboxOneName(t *testing.T) { 16 | Convey("Subject: #Name", t, func() { 17 | Convey("It should return Xbox One", func() { 18 | s := NewXboxOne(NewUAParser("")) 19 | So(s.Name(), ShouldEqual, "Xbox One") 20 | }) 21 | }) 22 | } 23 | 24 | func TestXboxOneMatch(t *testing.T) { 25 | Convey("Subject: #Match", t, func() { 26 | Convey("When the user agent matches", func() { 27 | Convey("It should return true", func() { 28 | So(NewXboxOne(NewUAParser(testDevices["xbox-one-1"])).Match(), ShouldBeTrue) 29 | So(NewXboxOne(NewUAParser(testDevices["xbox-one-2"])).Match(), ShouldBeTrue) 30 | }) 31 | }) 32 | 33 | Convey("When the user agent does not match", func() { 34 | Convey("It should return false", func() { 35 | So(NewXboxOne(NewUAParser(testDevices["bb-playbook-1"])).Match(), ShouldBeFalse) 36 | }) 37 | }) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package browser 2 | 3 | import "fmt" 4 | 5 | var ( 6 | ErrNoUserAgent = fmt.Errorf("no user agent provided") 7 | ErrUserAgentSizeExceeded = fmt.Errorf("user agent size exceeds limit of %d", userAgentSizeLimit) 8 | ) 9 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dineshgowda24/browser 2 | 3 | go 1.19 4 | 5 | require github.com/blang/semver/v4 v4.0.0 6 | 7 | require ( 8 | github.com/smartystreets/goconvey v1.8.1 9 | gopkg.in/yaml.v2 v2.4.0 10 | gopkg.in/yaml.v3 v3.0.1 11 | ) 12 | 13 | require ( 14 | github.com/gopherjs/gopherjs v1.17.2 // indirect 15 | github.com/jtolds/gls v4.20.0+incompatible // indirect 16 | github.com/kr/text v0.2.0 // indirect 17 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 18 | github.com/smarty/assertions v1.15.0 // indirect 19 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= 2 | github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 3 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 4 | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 5 | github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 6 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 7 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 8 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 9 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 10 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 11 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 12 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 13 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 14 | github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= 15 | github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= 16 | github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= 17 | github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 19 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 20 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 21 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 22 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 23 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 24 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dineshgowda24/browser/468fc9a1805d79411ec8973caa89dbb00424343c/logo.png -------------------------------------------------------------------------------- /matchers/alipay.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Alipay struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | aliPayName = "Alipay" 9 | aliPayVersionRegexp = []string{`AlipayClient(?:HK)?/([\d.]+)`} 10 | aliPayMatchRegex = []string{`(?i)Alipay`} 11 | ) 12 | 13 | func NewAlipay(p Parser) *Alipay { 14 | return &Alipay{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (a *Alipay) Name() string { 20 | return aliPayName 21 | } 22 | 23 | func (a *Alipay) Version() string { 24 | return a.p.Version(aliPayVersionRegexp, 1) 25 | } 26 | 27 | func (a *Alipay) Match() bool { 28 | return a.p.Match(aliPayMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/alipay_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewAlipay(t *testing.T) { 10 | Convey("Subject: #NewAlipay", t, func() { 11 | Convey("It should return a new Alipay instance", func() { 12 | alipay := NewAlipay(NewUAParser("")) 13 | So(alipay, ShouldHaveSameTypeAs, &Alipay{}) 14 | }) 15 | }) 16 | } 17 | 18 | func TestAlipayName(t *testing.T) { 19 | Convey("Subject: #Name", t, func() { 20 | Convey("It should return the correct name", func() { 21 | alipay := NewAlipay(NewUAParser("")) 22 | So(alipay.Name(), ShouldEqual, "Alipay") 23 | }) 24 | }) 25 | } 26 | 27 | func TestAlipayVersion(t *testing.T) { 28 | Convey("Subject: #Version", t, func() { 29 | Convey("When the version is captured", func() { 30 | Convey("It should return the version", func() { 31 | ua := testUserAgents["alipay"] 32 | 33 | So(NewAlipay(NewUAParser(ua.IOS)).Version(), ShouldEqual, "9.9.0.000001") 34 | So(NewAlipay(NewUAParser(ua.Android)).Version(), ShouldEqual, "1.7.0.123008") 35 | }) 36 | }) 37 | 38 | Convey("When the version is not captured", func() { 39 | Convey("It should return default version", func() { 40 | ua := testUserAgents["alipay"] 41 | 42 | So(NewAlipay(NewUAParser(ua.Windows)).Version(), ShouldEqual, "0.0") 43 | So(NewAlipay(NewUAParser(ua.Mac)).Version(), ShouldEqual, "0.0") 44 | }) 45 | }) 46 | }) 47 | } 48 | 49 | func TestAlipayMatch(t *testing.T) { 50 | Convey("Subject: #Match", t, func() { 51 | Convey("When user agent matches Alipay", func() { 52 | Convey("It should return true", func() { 53 | ua := testUserAgents["alipay"] 54 | So(NewAlipay(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 55 | So(NewAlipay(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 56 | So(NewAlipay(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 57 | So(NewAlipay(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 58 | }) 59 | }) 60 | 61 | Convey("When user agent does not match Alipay", func() { 62 | Convey("It should return false", func() { 63 | ua := "Mozilla/5.0 (Linux; Android 10; Redmi K30 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Mobile Safari/537.36" 64 | alipay := NewAlipay(NewUAParser(ua)) 65 | So(alipay.Match(), ShouldBeFalse) 66 | }) 67 | }) 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /matchers/black_berry.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type BlackBerry struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | blackBerryName = "BlackBerry" 9 | blackBerryVersionRegexp = []string{`BlackBerry[\da-z]+/([\d.]+)`, `Version/([\d.]+)`} 10 | blackBerryMatchRegex = []string{`BlackBerry|(?i)BB10`} 11 | ) 12 | 13 | func NewBlackBerry(p Parser) *BlackBerry { 14 | return &BlackBerry{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (b *BlackBerry) Name() string { 20 | return blackBerryName 21 | } 22 | 23 | func (b *BlackBerry) Version() string { 24 | return b.p.Version(blackBerryVersionRegexp, 1) 25 | } 26 | 27 | func (b *BlackBerry) Match() bool { 28 | return b.p.Match(blackBerryMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/black_berry_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewBlackberry(t *testing.T) { 10 | Convey("Subject: #NewBlackberry", t, func() { 11 | Convey("It should return a new Blackberry instance", func() { 12 | So(NewBlackBerry(NewUAParser("")), ShouldHaveSameTypeAs, &BlackBerry{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestBlackberryName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the name", func() { 20 | So(NewBlackBerry(NewUAParser("")).Name(), ShouldEqual, "BlackBerry") 21 | }) 22 | }) 23 | } 24 | 25 | func TestBlackberryVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | ua := testUserAgents["blackberry"] 28 | Convey("When the version is captured", func() { 29 | Convey("It should return the version", func() { 30 | So(NewBlackBerry(NewUAParser(ua.Linux)).Version(), ShouldEqual, "10.1.0.2342") 31 | So(NewBlackBerry(NewUAParser(ua.Windows)).Version(), ShouldEqual, "7.1.0.346") 32 | }) 33 | }) 34 | 35 | Convey("When the version is not captured", func() { 36 | Convey("It should return default version", func() { 37 | So(NewBlackBerry(NewUAParser(ua.Android)).Version(), ShouldEqual, "0.0") 38 | So(NewBlackBerry(NewUAParser(ua.IOS)).Version(), ShouldEqual, "0.0") 39 | }) 40 | }) 41 | }) 42 | } 43 | 44 | func TestBlackberryMatch(t *testing.T) { 45 | Convey("Subject: #Match", t, func() { 46 | Convey("When user agent matches", func() { 47 | Convey("It should return true", func() { 48 | ua := testUserAgents["blackberry"] 49 | 50 | So(NewBlackBerry(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 51 | So(NewBlackBerry(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 52 | So(NewBlackBerry(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 53 | So(NewBlackBerry(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 54 | }) 55 | }) 56 | 57 | Convey("When user agent does not match", func() { 58 | Convey("It should return false", func() { 59 | bb := NewBlackBerry( 60 | NewUAParser("Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Mobile Safari/537.36"), 61 | ) 62 | So(bb.Match(), ShouldBeFalse) 63 | }) 64 | }) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /matchers/brave.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | var ( 4 | braveName = "Brave" 5 | braveVersionRegexp = []string{`brave/([\d.]+)`} 6 | braveMatchRegex = []string{`(?i)Brave`} 7 | ) 8 | 9 | type Brave struct { 10 | p Parser 11 | } 12 | 13 | func NewBrave(p Parser) *Brave { 14 | return &Brave{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (b *Brave) Name() string { 20 | return braveName 21 | } 22 | 23 | func (b *Brave) Version() string { 24 | return b.p.Version(braveVersionRegexp, 1) 25 | } 26 | 27 | func (b *Brave) Match() bool { 28 | return b.p.Match(braveMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/brave_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewBrave(t *testing.T) { 10 | Convey("Subject: #NewBrave", t, func() { 11 | Convey("It should return a new Brave instance", func() { 12 | So(NewBrave(NewUAParser("")), ShouldHaveSameTypeAs, &Brave{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestBraveName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the name", func() { 20 | So(NewBrave(NewUAParser("")).Name(), ShouldEqual, "Brave") 21 | }) 22 | }) 23 | } 24 | 25 | func TestBraveVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | ua := testUserAgents["brave"] 28 | Convey("When the version is captured", func() { 29 | Convey("It should return the version", func() { 30 | So(NewBrave(NewUAParser(ua.Windows)).Version(), ShouldEqual, "0.7.7") 31 | So(NewBrave(NewUAParser(ua.Linux)).Version(), ShouldEqual, "0.7.7") 32 | So(NewBrave(NewUAParser(ua.Mac)).Version(), ShouldEqual, "0.7.7") 33 | }) 34 | }) 35 | 36 | Convey("When the version is not captured", func() { 37 | ua := testUserAgents["chrome"] 38 | Convey("It should return default version", func() { 39 | So(NewBrave(NewUAParser(ua.Android)).Version(), ShouldEqual, "0.0") 40 | So(NewBrave(NewUAParser(ua.IOS)).Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestBraveMatch(t *testing.T) { 47 | Convey("Subject: #Match", t, func() { 48 | Convey("When the user agent matches", func() { 49 | ua := testUserAgents["brave"] 50 | Convey("It should return true", func() { 51 | So(NewBrave(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 52 | So(NewBrave(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 53 | So(NewBrave(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 54 | }) 55 | }) 56 | 57 | Convey("When the user agent does not match", func() { 58 | ua := testUserAgents["chrome"] 59 | Convey("It should return false", func() { 60 | So(NewBrave(NewUAParser(ua.Android)).Match(), ShouldBeFalse) 61 | So(NewBrave(NewUAParser(ua.IOS)).Match(), ShouldBeFalse) 62 | }) 63 | }) 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /matchers/chrome.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Chrome struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | chromeName = "Chrome" 9 | chromeVersionRegexp = []string{`Chrome/([\d.]+)`, `CriOS/([\d.]+)`, `Safari/([\d.]+)`, `AppleWebKit/([\d.]+)`} 10 | chromeMatchRegex = []string{`Chrome|CriOS`} 11 | ) 12 | 13 | func NewChrome(p Parser) *Chrome { 14 | return &Chrome{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (c *Chrome) Name() string { 20 | return chromeName 21 | } 22 | 23 | func (c *Chrome) Version() string { 24 | return c.p.Version(chromeVersionRegexp, 1) 25 | } 26 | 27 | func (c *Chrome) Match() bool { 28 | return c.p.Match(chromeMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/chrome_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewChrome(t *testing.T) { 10 | Convey("Subject: #NewChrome", t, func() { 11 | Convey("It should return a new Chrome instance", func() { 12 | So(NewChrome(NewUAParser("")), ShouldHaveSameTypeAs, &Chrome{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestChromeName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the name", func() { 20 | So(NewChrome(NewUAParser("")).Name(), ShouldEqual, "Chrome") 21 | }) 22 | }) 23 | } 24 | 25 | func TestChromeVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is captured", func() { 28 | Convey("It should return the version", func() { 29 | ua := testUserAgents["chrome"] 30 | So(NewChrome(NewUAParser(ua.Windows)).Version(), ShouldEqual, "44.0.2403.155") 31 | So(NewChrome(NewUAParser(ua.Linux)).Version(), ShouldEqual, "42.0.2311.135") 32 | So(NewChrome(NewUAParser(ua.Mac)).Version(), ShouldEqual, "44.0.2403.155") 33 | So(NewChrome(NewUAParser(ua.IOS)).Version(), ShouldEqual, "44.0.2403.67") 34 | So(NewChrome(NewUAParser(ua.Android)).Version(), ShouldEqual, "30.0.0.0") 35 | }) 36 | }) 37 | 38 | Convey("When the version is not captured", func() { 39 | Convey("It should return default version", func() { 40 | So(NewChrome(NewUAParser("")).Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestChromeMatch(t *testing.T) { 47 | Convey("Subject: #Match", t, func() { 48 | Convey("When user agent matches", func() { 49 | Convey("It should return true", func() { 50 | ua := testUserAgents["chrome"] 51 | So(NewChrome(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 52 | So(NewChrome(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 53 | So(NewChrome(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 54 | So(NewChrome(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 55 | So(NewChrome(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 56 | }) 57 | }) 58 | 59 | Convey("When user agent does not match", func() { 60 | Convey("It should return false", func() { 61 | ua := testUserAgents["opera-mini"] 62 | So(NewChrome(NewUAParser(ua.Mac)).Match(), ShouldBeFalse) 63 | }) 64 | }) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /matchers/duck_duck_go.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type DuckDuckGo struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | duckDuckGoName = "DuckDuckGo" 9 | duckDuckGoVersionRegexp = []string{`DuckDuck(?:Go|GoKite)?/([\d.]+)`} 10 | duckDuckGoMatchRegex = []string{`DuckDuck(Go|GoKite)?`} 11 | ) 12 | 13 | func NewDuckDuckGo(p Parser) *DuckDuckGo { 14 | return &DuckDuckGo{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (ddg *DuckDuckGo) Name() string { 20 | return duckDuckGoName 21 | } 22 | 23 | func (ddg *DuckDuckGo) Version() string { 24 | return ddg.p.Version(duckDuckGoVersionRegexp, 1) 25 | } 26 | 27 | func (ddg *DuckDuckGo) Match() bool { 28 | return ddg.p.Match(duckDuckGoMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/duck_duck_go_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewDuckDuckGo(t *testing.T) { 10 | Convey("Subject: #NewDuckDuckGo", t, func() { 11 | Convey("When the given user agent is empty", func() { 12 | Convey("It should return a new DuckDuckGo instance", func() { 13 | So(NewDuckDuckGo(NewUAParser("")), ShouldHaveSameTypeAs, &DuckDuckGo{}) 14 | }) 15 | }) 16 | }) 17 | } 18 | 19 | func TestDuckDuckGoName(t *testing.T) { 20 | Convey("Subject: #Name", t, func() { 21 | Convey("It should return the correct name", func() { 22 | ddg := NewDuckDuckGo(NewUAParser("")) 23 | So(ddg.Name(), ShouldEqual, duckDuckGoName) 24 | }) 25 | }) 26 | } 27 | 28 | func TestDuckDuckGoVersion(t *testing.T) { 29 | Convey("Subject: #Version", t, func() { 30 | Convey("When the version is matched", func() { 31 | Convey("It should return the correct version", func() { 32 | ua := testUserAgents["duck_duck_go"] 33 | 34 | So(NewDuckDuckGo(NewUAParser(ua.IOS)).Version(), ShouldEqual, "2") 35 | So(NewDuckDuckGo(NewUAParser(ua.Android)).Version(), ShouldEqual, "5") 36 | So(NewDuckDuckGo(NewUAParser(ua.Windows)).Version(), ShouldEqual, "1.0") 37 | So(NewDuckDuckGo(NewUAParser(ua.Mac)).Version(), ShouldEqual, "1.0.1") 38 | So(NewDuckDuckGo(NewUAParser(ua.Linux)).Version(), ShouldEqual, "5") 39 | }) 40 | }) 41 | 42 | Convey("When the version is not matched", func() { 43 | Convey("It should return default version", func() { 44 | ddg := NewDuckDuckGo( 45 | NewUAParser("Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/605.1.15"), 46 | ) 47 | So(ddg.Version(), ShouldEqual, "0.0") 48 | }) 49 | }) 50 | }) 51 | } 52 | 53 | func TestDuckDuckGoMatch(t *testing.T) { 54 | Convey("Subject: #Match", t, func() { 55 | Convey("When user agent matches", func() { 56 | Convey("It should return true", func() { 57 | ua := testUserAgents["duck_duck_go"] 58 | 59 | So(NewDuckDuckGo(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 60 | So(NewDuckDuckGo(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 61 | So(NewDuckDuckGo(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 62 | So(NewDuckDuckGo(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 63 | So(NewDuckDuckGo(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 64 | }) 65 | }) 66 | 67 | Convey("When user agent does not match", func() { 68 | Convey("It should return false", func() { 69 | ddg := NewDuckDuckGo( 70 | NewUAParser("Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/605.1.15"), 71 | ) 72 | So(ddg.Match(), ShouldBeFalse) 73 | }) 74 | }) 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /matchers/edge.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Edge struct { 4 | p Parser 5 | ie *InternetExplorer 6 | } 7 | 8 | var ( 9 | edgeName = "Microsoft Edge" 10 | edgeMatchRegex = []string{`((?:Edge|Edg|EdgiOS|EdgA)/[\d.]+|Trident/8)`} 11 | edgeVersionRegex = []string{`(?:Edge|Edg|EdgiOS|EdgA)/([\d.]+)`} 12 | ) 13 | 14 | func NewEdge(p Parser) *Edge { 15 | return &Edge{ 16 | p: p, 17 | ie: NewInternetExplorer(p), 18 | } 19 | } 20 | 21 | func (e *Edge) Name() string { 22 | return edgeName 23 | } 24 | 25 | func (e *Edge) Version() string { 26 | v := e.p.Version(edgeVersionRegex, 1) 27 | if v != "0.0" { 28 | return v 29 | } 30 | 31 | return e.ie.Version() 32 | } 33 | 34 | func (e *Edge) Match() bool { 35 | return e.p.Match(edgeMatchRegex) 36 | } 37 | -------------------------------------------------------------------------------- /matchers/edge_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewEdge(t *testing.T) { 10 | Convey("Subject: #NewEdge", t, func() { 11 | Convey("It should return an Edge instance", func() { 12 | So(NewEdge(NewUAParser("")), ShouldHaveSameTypeAs, &Edge{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestEdgeName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the correct browser name", func() { 20 | So(NewEdge(NewUAParser("")).Name(), ShouldEqual, "Microsoft Edge") 21 | }) 22 | }) 23 | } 24 | 25 | func TestEdgeVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("It should return the correct browser version", func() { 28 | ua := testUserAgents["edge"] 29 | 30 | So(NewEdge(NewUAParser(ua.Windows)).Version(), ShouldEqual, "12.10240") 31 | So(NewEdge(NewUAParser(ua.Android)).Version(), ShouldEqual, "14.14393") 32 | }) 33 | }) 34 | } 35 | 36 | func TestEdgeMatch(t *testing.T) { 37 | Convey("Subject: #Match", t, func() { 38 | Convey("When the user agent matches", func() { 39 | Convey("It should return true", func() { 40 | ua := testUserAgents["edge"] 41 | 42 | So(NewEdge(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 43 | So(NewEdge(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 44 | }) 45 | }) 46 | 47 | Convey("When the user agent doesn't match", func() { 48 | Convey("It should return false", func() { 49 | So(NewEdge(NewUAParser(testUserAgents["chrome"].Android)).Match(), ShouldBeFalse) 50 | }) 51 | }) 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /matchers/electron.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Electron struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | electronName = "Electron" 9 | electronVersionRegexp = []string{`Electron/([\d.]+)`} 10 | electronMatchRegex = []string{`Electron`} 11 | ) 12 | 13 | func NewElectron(p Parser) *Electron { 14 | return &Electron{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (e *Electron) Name() string { 20 | return electronName 21 | } 22 | 23 | func (e *Electron) Version() string { 24 | return e.p.Version(electronVersionRegexp, 1) 25 | } 26 | 27 | func (e *Electron) Match() bool { 28 | return e.p.Match(electronMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/electron_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewElectron(t *testing.T) { 10 | Convey("Subject: #NewElectron", t, func() { 11 | Convey("It should return a new Electron instance", func() { 12 | So(NewElectron(NewUAParser("")), ShouldHaveSameTypeAs, &Electron{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestElectronName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the name", func() { 20 | e := NewElectron(NewUAParser("")) 21 | So(e.Name(), ShouldEqual, electronName) 22 | }) 23 | }) 24 | } 25 | 26 | func TestElectronVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ua := testUserAgents["electron"] 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the version", func() { 31 | So(NewElectron(NewUAParser(ua.Linux)).Version(), ShouldEqual, "0.30.7") 32 | So(NewElectron(NewUAParser(ua.Windows)).Version(), ShouldEqual, "0.36.4") 33 | So(NewElectron(NewUAParser(ua.Android)).Version(), ShouldEqual, "7.1.2") 34 | So(NewElectron(NewUAParser(ua.Mac)).Version(), ShouldEqual, "0.36.9") 35 | }) 36 | }) 37 | 38 | Convey("When the version is not matched", func() { 39 | Convey("It should return default version", func() { 40 | So(NewElectron(NewUAParser(ua.IOS)).Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestElectronMatch(t *testing.T) { 47 | Convey("Subject: #Match", t, func() { 48 | Convey("When user agent matches Electron", func() { 49 | Convey("It should return true", func() { 50 | ua := testUserAgents["electron"] 51 | 52 | So(NewElectron(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 53 | So(NewElectron(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 54 | So(NewElectron(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 55 | So(NewElectron(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 56 | So(NewElectron(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 57 | }) 58 | }) 59 | 60 | Convey("When user agent does not match Electron", func() { 61 | Convey("It should return false", func() { 62 | e := NewElectron( 63 | NewUAParser("Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko)"), 64 | ) 65 | So(e.Match(), ShouldBeFalse) 66 | }) 67 | }) 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /matchers/firefox.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Firefox struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | firefoxName = "Firefox" 9 | firefoxVersionRegexp = []string{`Firefox/([\d.]+)`, `FxiOS/([\d.]+)`} 10 | firefoxMatchRegex = []string{`Firefox`, `FxiOS`} 11 | ) 12 | 13 | func NewFirefox(p Parser) *Firefox { 14 | return &Firefox{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (f *Firefox) Name() string { 20 | return firefoxName 21 | } 22 | 23 | func (f *Firefox) Version() string { 24 | return f.p.Version(firefoxVersionRegexp, 1) 25 | } 26 | 27 | func (f *Firefox) Match() bool { 28 | return f.p.Match(firefoxMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/firefox_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewFirefox(t *testing.T) { 10 | Convey("Subject: #NewFirefox", t, func() { 11 | Convey("It should return a new Firefox instance", func() { 12 | So(NewFirefox(NewUAParser("")), ShouldHaveSameTypeAs, &Firefox{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestFirefoxName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the name", func() { 20 | So(NewFirefox(NewUAParser("")).Name(), ShouldEqual, "Firefox") 21 | }) 22 | }) 23 | } 24 | 25 | func TestFirefoxVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is captured", func() { 28 | ua := testUserAgents["firefox"] 29 | Convey("It should return the version", func() { 30 | So(NewFirefox(NewUAParser(ua.Android)).Version(), ShouldEqual, "59.0.2") 31 | So(NewFirefox(NewUAParser(ua.IOS)).Version(), ShouldEqual, "7.0.4") 32 | So(NewFirefox(NewUAParser(ua.Linux)).Version(), ShouldEqual, "3.6.8") 33 | So(NewFirefox(NewUAParser(ua.Mac)).Version(), ShouldEqual, "64.0") 34 | So(NewFirefox(NewUAParser(ua.Windows)).Version(), ShouldEqual, "3.6.8") 35 | }) 36 | }) 37 | 38 | Convey("When the version is not captured", func() { 39 | ua := testUserAgents["chrome"] 40 | Convey("It should return default version", func() { 41 | So(NewFirefox(NewUAParser(ua.Android)).Version(), ShouldEqual, "0.0") 42 | }) 43 | }) 44 | }) 45 | } 46 | 47 | func TestFirefoxMatch(t *testing.T) { 48 | Convey("Subject: #Match", t, func() { 49 | Convey("When the user agent matches", func() { 50 | ua := testUserAgents["firefox"] 51 | Convey("It should return true", func() { 52 | So(NewFirefox(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 53 | So(NewFirefox(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 54 | So(NewFirefox(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 55 | So(NewFirefox(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 56 | So(NewFirefox(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 57 | }) 58 | }) 59 | 60 | Convey("When the user agent does not match", func() { 61 | ua := testUserAgents["chrome"] 62 | Convey("It should return false", func() { 63 | So(NewFirefox(NewUAParser(ua.Android)).Match(), ShouldBeFalse) 64 | }) 65 | }) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /matchers/gsa.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type GoogleSearchApp struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | googleSearchAppName = "Google Search App" 9 | // TODO: review this regex, prev `GSA/([\d.]+)` 10 | googleSearchAppVersionRegexp = []string{`GSA/([\d]+(?:\.[\d]+)*)`} 11 | googleSearchAppMatchRegex = []string{`(?i)GSA`} 12 | ) 13 | 14 | func NewGoogleSearchApp(p Parser) *GoogleSearchApp { 15 | return &GoogleSearchApp{ 16 | p: p, 17 | } 18 | } 19 | 20 | func (g *GoogleSearchApp) Name() string { 21 | return googleSearchAppName 22 | } 23 | 24 | func (g *GoogleSearchApp) Version() string { 25 | return g.p.Version(googleSearchAppVersionRegexp, 1) 26 | } 27 | 28 | func (g *GoogleSearchApp) Match() bool { 29 | return g.p.Match(googleSearchAppMatchRegex) 30 | } 31 | -------------------------------------------------------------------------------- /matchers/gsa_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewGSA(t *testing.T) { 10 | Convey("Subject: #NewGSA", t, func() { 11 | Convey("It should return a new GSA instance", func() { 12 | So(NewGoogleSearchApp(NewUAParser("")), ShouldHaveSameTypeAs, &GoogleSearchApp{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestGSAName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Google Search App", func() { 20 | gsa := NewGoogleSearchApp(NewUAParser("")) 21 | So(gsa.Name(), ShouldEqual, googleSearchAppName) 22 | }) 23 | }) 24 | } 25 | 26 | func TestGSAVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ua := testUserAgents["gsa"] 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the version", func() { 31 | So(NewGoogleSearchApp(NewUAParser(ua.Linux)).Version(), ShouldEqual, "11.2.9.21") 32 | So(NewGoogleSearchApp(NewUAParser(ua.Mac)).Version(), ShouldEqual, "7.0.55539") 33 | So(NewGoogleSearchApp(NewUAParser(ua.Android)).Version(), ShouldEqual, "5.10.32.19") 34 | So(NewGoogleSearchApp(NewUAParser(ua.IOS)).Version(), ShouldEqual, "7.0.55539") 35 | }) 36 | }) 37 | 38 | Convey("When the version is not matched", func() { 39 | Convey("It should return default version", func() { 40 | So(NewGoogleSearchApp(NewUAParser(ua.Windows)).Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestGSAMatch(t *testing.T) { 47 | Convey("Subject: #Match", t, func() { 48 | Convey("When user agent matches GSA", func() { 49 | Convey("It should return true", func() { 50 | ua := testUserAgents["gsa"] 51 | 52 | So(NewGoogleSearchApp(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 53 | So(NewGoogleSearchApp(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 54 | So(NewGoogleSearchApp(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 55 | So(NewGoogleSearchApp(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 56 | So(NewGoogleSearchApp(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 57 | }) 58 | }) 59 | 60 | Convey("When user agent does not match GSA", func() { 61 | Convey("It should return false", func() { 62 | userAgent := "Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Mobile Safari/537.36" 63 | 64 | So(NewGoogleSearchApp(NewUAParser(userAgent)).Match(), ShouldBeFalse) 65 | }) 66 | }) 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /matchers/huawei_browser.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type HuaweiBrowser struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | huaweiBrowserName = "Huawei Browser" 9 | huaweiBrowserVersionRegexp = []string{`(?i)(?:HuaweiBrowser|HBPC)/([\d.]+)`} 10 | huaweiBrowserMatchRegexp = []string{`(?i)(HuaweiBrowser|HBPC)`} 11 | ) 12 | 13 | func NewHuaweiBrowser(p Parser) *HuaweiBrowser { 14 | return &HuaweiBrowser{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (h *HuaweiBrowser) Name() string { 20 | return huaweiBrowserName 21 | } 22 | 23 | func (h *HuaweiBrowser) Version() string { 24 | return h.p.Version(huaweiBrowserVersionRegexp, 1) 25 | } 26 | 27 | func (h *HuaweiBrowser) Match() bool { 28 | return h.p.Match(huaweiBrowserMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/huawei_browser_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewHuaweiBrowser(t *testing.T) { 10 | Convey("Subject: #NewHuaweiBrowser", t, func() { 11 | Convey("It should return a new HuaweiBrowser instance", func() { 12 | So(NewHuaweiBrowser(NewUAParser("")), ShouldHaveSameTypeAs, &HuaweiBrowser{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestHuaweiBrowserName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the correct name", func() { 20 | hb := NewHuaweiBrowser(NewUAParser("")) 21 | So(hb.Name(), ShouldEqual, huaweiBrowserName) 22 | }) 23 | }) 24 | } 25 | 26 | func TestHuaweiBrowserVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the correct version", func() { 30 | ua := testUserAgents["huawei-browser"] 31 | 32 | So(NewHuaweiBrowser(NewUAParser(ua.Windows)).Version(), ShouldEqual, "12.1.3.303") 33 | So(NewHuaweiBrowser(NewUAParser(ua.Android)).Version(), ShouldEqual, "14.0.2.300") 34 | }) 35 | }) 36 | 37 | Convey("When the version is not matched", func() { 38 | Convey("It should return default version", func() { 39 | ua := testUserAgents["uc-browser"] 40 | So(NewHuaweiBrowser(NewUAParser(ua.Android)).Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestHuaweiBrowserMatch(t *testing.T) { 47 | Convey("Subject: #Match", t, func() { 48 | Convey("When user agent matches HuaweiBrowser", func() { 49 | Convey("It should return true", func() { 50 | ua := testUserAgents["huawei-browser"] 51 | So(NewHuaweiBrowser(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 52 | So(NewHuaweiBrowser(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 53 | }) 54 | }) 55 | 56 | Convey("When user agent does not match HuaweiBrowser", func() { 57 | Convey("It should return false", func() { 58 | ua := testUserAgents["uc-browser"] 59 | So(NewHuaweiBrowser(NewUAParser(ua.Android)).Match(), ShouldBeFalse) 60 | }) 61 | }) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /matchers/instagram.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Instagram struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | instagramName = "Instagram" 9 | instagramVersionRegexp = []string{`Instagram[ /]([\d.]+)`} 10 | instagramMatchRegex = []string{`(?i)Instagram`} 11 | ) 12 | 13 | func NewInstagram(p Parser) *Instagram { 14 | return &Instagram{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (i *Instagram) Name() string { 20 | return instagramName 21 | } 22 | 23 | func (i *Instagram) Version() string { 24 | return i.p.Version(instagramVersionRegexp, 1) 25 | } 26 | 27 | func (i *Instagram) Match() bool { 28 | return i.p.Match(instagramMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/instagram_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewInstagram(t *testing.T) { 10 | Convey("Subject: #NewInstagram", t, func() { 11 | Convey("It should return a new Instagram instance", func() { 12 | So(NewInstagram(NewUAParser("")), ShouldHaveSameTypeAs, &Instagram{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestInstagramName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the correct name", func() { 20 | ig := NewInstagram(NewUAParser("")) 21 | So(ig.Name(), ShouldEqual, instagramName) 22 | }) 23 | }) 24 | } 25 | 26 | func TestInstagramVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ua := testUserAgents["instagram"] 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the correct version", func() { 31 | So(NewInstagram(NewUAParser(ua.IOS)).Version(), ShouldEqual, "10.18.0") 32 | So(NewInstagram(NewUAParser(ua.Android)).Version(), ShouldEqual, "9.7.0") 33 | So(NewInstagram(NewUAParser(ua.Windows)).Version(), ShouldEqual, "10.8.1") 34 | }) 35 | }) 36 | 37 | Convey("When the version is not matched", func() { 38 | Convey("It should return default version", func() { 39 | So(NewInstagram(NewUAParser(ua.Mac)).Version(), ShouldEqual, "0.0") 40 | }) 41 | }) 42 | }) 43 | } 44 | 45 | func TestInstagramMatch(t *testing.T) { 46 | Convey("Subject: #Match", t, func() { 47 | Convey("When user agent matches", func() { 48 | ua := testUserAgents["instagram"] 49 | 50 | So(NewInstagram(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 51 | So(NewInstagram(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 52 | So(NewInstagram(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 53 | So(NewInstagram(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 54 | }) 55 | 56 | Convey("When user agent does not match", func() { 57 | Convey("It should return false", func() { 58 | userAgent := "Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko)" 59 | So(NewInstagram(NewUAParser(userAgent)).Match(), ShouldBeFalse) 60 | }) 61 | }) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /matchers/internet_explorer.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | type InternetExplorer struct { 9 | p Parser 10 | } 11 | 12 | var ( 13 | ieName = "Internet Explorer" 14 | ieMatchRegex = []string{`Trident/.*?; rv:(.*?)`} 15 | // https://en.wikipedia.org/wiki/Trident_(layout_engine) 16 | // A map of trident versions and their respective IE versions 17 | tridentMapping = map[string]string{ 18 | "4.0": "8", // IE8 19 | "5.0": "9", // IE9 20 | "6.0": "10", // IE10 21 | "7.0": "11", // IE11 22 | "8.0": "12", // IE12 23 | } 24 | ) 25 | 26 | func NewInternetExplorer(p Parser) *InternetExplorer { 27 | return &InternetExplorer{ 28 | p: p, 29 | } 30 | } 31 | 32 | func (i *InternetExplorer) Name() string { 33 | return ieName 34 | } 35 | 36 | // Version returns the version of Internet Explorer 37 | // It first tries to get the version from the Trident version 38 | // If that fails, it tries to get the version from the MSIE version 39 | // If that fails, it returns 0.0 40 | func (i *InternetExplorer) Version() string { 41 | t := i.tridentVersion() 42 | if v, ok := tridentMapping[t]; ok { 43 | return v 44 | } 45 | 46 | return i.msieVersion() 47 | } 48 | 49 | // tridentVersion returns the version of Trident 50 | func (i *InternetExplorer) tridentVersion() string { 51 | re := regexp.MustCompile(`Trident/([0-9.]+)`) 52 | matches := re.FindStringSubmatch(i.p.String()) 53 | if len(matches) > 1 { 54 | return matches[1] 55 | } 56 | 57 | return "0.0" 58 | } 59 | 60 | func (i *InternetExplorer) msieVersion() string { 61 | return strings.Split(i.msieFullVersion(), ".")[0] 62 | } 63 | 64 | // msieFullVersion returns the full version of MSIE 65 | func (i *InternetExplorer) msieFullVersion() string { 66 | re := regexp.MustCompile(`MSIE ([\d.]+)|Trident/.*?; rv:([\d.]+)`) 67 | 68 | matches := re.FindStringSubmatch(i.p.String()) 69 | if len(matches) > 2 { 70 | if matches[1] != "" { 71 | return matches[1] 72 | } else if matches[2] != "" { 73 | return matches[2] 74 | } 75 | } 76 | 77 | return "0.0" 78 | } 79 | 80 | // Match returns true if the user agent matches one of the 81 | // Internet Explorer matchers 82 | // It also checks if the user agent contains MSIE and does not contain Opera 83 | func (i *InternetExplorer) Match() bool { 84 | return i.p.Match(ieMatchRegex) || (strings.Contains(i.p.String(), "MSIE") && 85 | !strings.Contains(i.p.String(), "Opera")) 86 | } 87 | -------------------------------------------------------------------------------- /matchers/internet_explorer_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewInternetExplorer(t *testing.T) { 10 | Convey("Subject: #NewInternetExplorer", t, func() { 11 | Convey("It should return a new InternetExplorer instance", func() { 12 | So(NewInternetExplorer(NewUAParser("")), ShouldHaveSameTypeAs, &InternetExplorer{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestInternetExplorerName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the correct name", func() { 20 | ie := NewInternetExplorer(NewUAParser("")) 21 | So(ie.Name(), ShouldEqual, "Internet Explorer") 22 | }) 23 | }) 24 | } 25 | 26 | func TestInternetExplorerVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ie := testUserAgents["ie"] 29 | oldIE := testUserAgents["old-ie"] 30 | 31 | Convey("When the version is matched", func() { 32 | Convey("It should return the correct version", func() { 33 | So(NewInternetExplorer(NewUAParser(ie.Windows)).Version(), ShouldEqual, "11") 34 | So(NewInternetExplorer(NewUAParser(oldIE.Mac)).Version(), ShouldEqual, "10") 35 | So(NewInternetExplorer(NewUAParser(oldIE.Windows)).Version(), ShouldEqual, "11") 36 | }) 37 | }) 38 | }) 39 | } 40 | 41 | func TestInternetExplorerMatch(t *testing.T) { 42 | Convey("Subject: #Match", t, func() { 43 | ie := testUserAgents["ie"] 44 | oldIE := testUserAgents["old-ie"] 45 | chrome := testUserAgents["chrome"] 46 | 47 | Convey("When the user agent matches", func() { 48 | Convey("It should return true", func() { 49 | So(NewInternetExplorer(NewUAParser(ie.Windows)).Match(), ShouldBeTrue) 50 | So(NewInternetExplorer(NewUAParser(oldIE.Mac)).Match(), ShouldBeTrue) 51 | So(NewInternetExplorer(NewUAParser(oldIE.Windows)).Match(), ShouldBeTrue) 52 | }) 53 | }) 54 | 55 | Convey("When the user agent does not match", func() { 56 | Convey("It should return false", func() { 57 | So(NewInternetExplorer(NewUAParser(chrome.Mac)).Match(), ShouldBeFalse) 58 | So(NewInternetExplorer(NewUAParser(chrome.Linux)).Match(), ShouldBeFalse) 59 | }) 60 | }) 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /matchers/konqueror.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Konqueror struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | konquerorName = "Konqueror" 9 | konquerorVersionRegexp = []string{`(?i)Konqueror/([\d.]+)`} 10 | konquerorMatchRegex = []string{`(?i)Konqueror`} 11 | ) 12 | 13 | func NewKonqueror(p Parser) *Konqueror { 14 | return &Konqueror{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (k *Konqueror) Name() string { 20 | return konquerorName 21 | } 22 | 23 | func (k *Konqueror) Version() string { 24 | return k.p.Version(konquerorVersionRegexp, 1) 25 | } 26 | 27 | func (k *Konqueror) Match() bool { 28 | return k.p.Match(konquerorMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/konqueror_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewKonqueror(t *testing.T) { 10 | Convey("Subject: #NewKonqueror", t, func() { 11 | Convey("It should return a new Konqueror instance", func() { 12 | So(NewKonqueror(NewUAParser("")), ShouldHaveSameTypeAs, &Konqueror{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestKonquerorName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Konqueror", func() { 20 | k := NewKonqueror(NewUAParser("")) 21 | So(k.Name(), ShouldEqual, konquerorName) 22 | }) 23 | }) 24 | } 25 | 26 | func TestKonquerorVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ua := testUserAgents["konqueror"] 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the version", func() { 31 | So(NewKonqueror(NewUAParser(ua.Linux)).Version(), ShouldEqual, "4.14.1") 32 | So(NewKonqueror(NewUAParser(ua.Mac)).Version(), ShouldEqual, "5.0") 33 | So(NewKonqueror(NewUAParser(ua.Windows)).Version(), ShouldEqual, "4.10") 34 | So(NewKonqueror(NewUAParser(ua.Android)).Version(), ShouldEqual, "10.1") 35 | So(NewKonqueror(NewUAParser(ua.IOS)).Version(), ShouldEqual, "14.1") 36 | }) 37 | }) 38 | 39 | Convey("When the version is not matched", func() { 40 | Convey("It should return default version", func() { 41 | k := NewKonqueror(NewUAParser(testUserAgents["chrome"].Linux)) 42 | So(k.Version(), ShouldEqual, "0.0") 43 | }) 44 | }) 45 | }) 46 | } 47 | 48 | func TestKonquerorMatch(t *testing.T) { 49 | Convey("Subject: #Match", t, func() { 50 | Convey("When user agent matches Konqueror", func() { 51 | Convey("It should return true", func() { 52 | ua := testUserAgents["konqueror"] 53 | So(NewKonqueror(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 54 | So(NewKonqueror(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 55 | So(NewKonqueror(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 56 | So(NewKonqueror(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 57 | So(NewKonqueror(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 58 | }) 59 | }) 60 | 61 | Convey("When user agent does not match Konqueror", func() { 62 | Convey("It should return false", func() { 63 | So(NewKonqueror(NewUAParser(testUserAgents["chrome"].Linux)).Match(), ShouldBeFalse) 64 | }) 65 | }) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /matchers/matchers_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "strings" 8 | "testing" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // ua is a struct to hold the user agent strings for each platform from the matchers.yml file 14 | // only used for testing 15 | type ua struct { 16 | Android string `yaml:"android"` 17 | IOS string `yaml:"ios"` 18 | Mac string `yaml:"mac"` 19 | Windows string `yaml:"windows"` 20 | Linux string `yaml:"linux"` 21 | } 22 | 23 | // testUserAgents is a map of user agent strings for each platform from the matchers.yml file 24 | var testUserAgents map[string]ua 25 | 26 | // initTestUserAgents reads the matchers.yml file and 27 | // unmarshals it into the testUserAgents map. 28 | func initTestUserAgents() { 29 | wd, err := os.Getwd() 30 | if err != nil { 31 | log.Fatalf("failed to get working directory: %v", err) 32 | } 33 | 34 | wd = strings.Split(wd, "/browser")[0] 35 | yamlFile, err := os.ReadFile(fmt.Sprintf("%s/browser/assets/test/matchers.yml", wd)) 36 | if err != nil { 37 | log.Fatalf("failed to read file: %v", err) 38 | } 39 | 40 | var data map[string]ua 41 | if err := yaml.Unmarshal(yamlFile, &data); err != nil { 42 | log.Fatalf("failed to unmarshal: %v", err) 43 | } 44 | 45 | testUserAgents = data 46 | } 47 | 48 | // TestMain is the entry point for running tests in this package. 49 | func TestMain(m *testing.M) { 50 | initTestUserAgents() 51 | exitCode := m.Run() 52 | os.Exit(exitCode) 53 | } 54 | -------------------------------------------------------------------------------- /matchers/maxthon.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Maxthon struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | maxthonName = "Maxthon" 9 | maxthonVersionRegexp = []string{`(?i)Maxthon/([\d.]+)`} 10 | maxthonMatchRegex = []string{`(i?)Maxthon`} 11 | ) 12 | 13 | func NewMaxthon(p Parser) *Maxthon { 14 | return &Maxthon{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (m *Maxthon) Name() string { 20 | return maxthonName 21 | } 22 | 23 | func (m *Maxthon) Version() string { 24 | return m.p.Version(maxthonVersionRegexp, 1) 25 | } 26 | 27 | func (m *Maxthon) Match() bool { 28 | return m.p.Match(maxthonMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/maxthon_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewMaxthon(t *testing.T) { 10 | Convey("Subject: #NewMaxthon", t, func() { 11 | Convey("It should return a new Maxthon instance", func() { 12 | So(NewMaxthon(NewUAParser("")), ShouldHaveSameTypeAs, &Maxthon{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestMaxthonName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Maxthon", func() { 20 | m := NewMaxthon(NewUAParser("")) 21 | So(m.Name(), ShouldEqual, "Maxthon") 22 | }) 23 | }) 24 | } 25 | 26 | func TestMaxthonVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ua := testUserAgents["maxthon"] 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the version", func() { 31 | So(NewMaxthon(NewUAParser(ua.Linux)).Version(), ShouldEqual, "1.0.5.3") 32 | So(NewMaxthon(NewUAParser(ua.Mac)).Version(), ShouldEqual, "4.5.2") 33 | So(NewMaxthon(NewUAParser(ua.Windows)).Version(), ShouldEqual, "4.4.3.4000") 34 | So(NewMaxthon(NewUAParser(ua.IOS)).Version(), ShouldEqual, "4.1.8.2000") 35 | }) 36 | }) 37 | 38 | Convey("When the version is not matched", func() { 39 | Convey("It should return default version", func() { 40 | So(NewMaxthon(NewUAParser(ua.Android)).Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestMaxthonMatch(t *testing.T) { 47 | Convey("Subject: #Match", t, func() { 48 | Convey("When user agent matches Maxthon", func() { 49 | Convey("It should return true", func() { 50 | ua := testUserAgents["maxthon"] 51 | 52 | So(NewMaxthon(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 53 | So(NewMaxthon(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 54 | So(NewMaxthon(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 55 | So(NewMaxthon(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 56 | So(NewMaxthon(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 57 | }) 58 | }) 59 | 60 | Convey("When user agent does not match Maxthon", func() { 61 | Convey("It should return false", func() { 62 | So(NewMaxthon(NewUAParser(testUserAgents["chrome"].Linux)).Match(), ShouldBeFalse) 63 | }) 64 | }) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /matchers/micro_messenger.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type MicroMessenger struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | microMessengerName = "MicroMessenger" 9 | microMessengerVersionRegexp = []string{`(?i)(?:MicroMessenger)/([\d.]+)`} 10 | microMessengerMatchRegexp = []string{`MicroMessenger`} 11 | ) 12 | 13 | func NewMicroMessenger(p Parser) *MicroMessenger { 14 | return &MicroMessenger{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (m *MicroMessenger) Name() string { 20 | return microMessengerName 21 | } 22 | 23 | func (m *MicroMessenger) Version() string { 24 | return m.p.Version(microMessengerVersionRegexp, 1) 25 | } 26 | 27 | func (m *MicroMessenger) Match() bool { 28 | return m.p.Match(microMessengerMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/micro_messenger_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewMicroMessenger(t *testing.T) { 10 | Convey("Subject: #NewMicroMessenger", t, func() { 11 | Convey("It should return a new MicroMessenger instance", func() { 12 | So(NewMicroMessenger(NewUAParser("")), ShouldHaveSameTypeAs, &MicroMessenger{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestMicroMessengerName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return MicroMessenger", func() { 20 | s := NewMicroMessenger(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "MicroMessenger") 22 | }) 23 | }) 24 | } 25 | 26 | func TestMicroMessengerVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | s := testUserAgents["micro-messenger"] 31 | 32 | So(NewMicroMessenger(NewUAParser(s.Android)).Version(), ShouldEqual, "6.2.5.51") 33 | So(NewMicroMessenger(NewUAParser(s.IOS)).Version(), ShouldEqual, "6.2.3") 34 | So(NewMicroMessenger(NewUAParser(s.Linux)).Version(), ShouldEqual, "7.0.1") 35 | So(NewMicroMessenger(NewUAParser(s.Mac)).Version(), ShouldEqual, "2.3.24") 36 | So(NewMicroMessenger(NewUAParser(s.Windows)).Version(), ShouldEqual, "6.5.2.501") 37 | }) 38 | }) 39 | }) 40 | } 41 | 42 | func TestMicroMessengerMatch(t *testing.T) { 43 | Convey("Subject: #Match", t, func() { 44 | Convey("When user agent matches MicroMessenger", func() { 45 | Convey("It should return true", func() { 46 | s := testUserAgents["micro-messenger"] 47 | 48 | So(NewMicroMessenger(NewUAParser(s.Android)).Match(), ShouldBeTrue) 49 | So(NewMicroMessenger(NewUAParser(s.IOS)).Match(), ShouldBeTrue) 50 | So(NewMicroMessenger(NewUAParser(s.Linux)).Match(), ShouldBeTrue) 51 | So(NewMicroMessenger(NewUAParser(s.Mac)).Match(), ShouldBeTrue) 52 | So(NewMicroMessenger(NewUAParser(s.Windows)).Match(), ShouldBeTrue) 53 | }) 54 | }) 55 | 56 | Convey("When user agent does not match MicroMessenger", func() { 57 | Convey("It should return false", func() { 58 | s := testUserAgents["chrome"] 59 | 60 | So(NewMicroMessenger(NewUAParser(s.Linux)).Match(), ShouldBeFalse) 61 | }) 62 | }) 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /matchers/miui_browser.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type MiuiBrowser struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | miuiBrowserName = "Miui Browser" 9 | miuiBrowserVersionRegexp = []string{`MiuiBrowser/([\d.]+)`} 10 | miuiBrowserMatchRegexp = []string{`MiuiBrowser`} 11 | ) 12 | 13 | func NewMiuiBrowser(p Parser) *MiuiBrowser { 14 | return &MiuiBrowser{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (m *MiuiBrowser) Name() string { 20 | return miuiBrowserName 21 | } 22 | 23 | func (m *MiuiBrowser) Version() string { 24 | return m.p.Version(miuiBrowserVersionRegexp, 1) 25 | } 26 | 27 | func (m *MiuiBrowser) Match() bool { 28 | return m.p.Match(miuiBrowserMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/miui_browser_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewMiuiBrowser(t *testing.T) { 10 | Convey("Subject: #NewMiuiBrowser", t, func() { 11 | Convey("It should return a new MiuiBrowser instance", func() { 12 | So(NewMiuiBrowser(NewUAParser("")), ShouldHaveSameTypeAs, &MiuiBrowser{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestMiuiBrowserName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Miui Browser", func() { 20 | So(NewMiuiBrowser(NewUAParser("")).Name(), ShouldEqual, "Miui Browser") 21 | }) 22 | }) 23 | } 24 | 25 | func TestMiuiBrowserVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | Convey("It should return the version", func() { 29 | ua := testUserAgents["miui-browser"] 30 | 31 | So(NewMiuiBrowser(NewUAParser(ua.Android)).Version(), ShouldEqual, "2.1.1") 32 | So(NewMiuiBrowser(NewUAParser(ua.IOS)).Version(), ShouldEqual, "1.0") 33 | So(NewMiuiBrowser(NewUAParser(ua.Linux)).Version(), ShouldEqual, "2.0.1") 34 | So(NewMiuiBrowser(NewUAParser(ua.Windows)).Version(), ShouldEqual, "12.5.2") 35 | }) 36 | }) 37 | 38 | Convey("When the version is not matched", func() { 39 | Convey("It should return default version", func() { 40 | mb := NewMiuiBrowser(NewUAParser(testUserAgents["chrome"].Linux)) 41 | So(mb.Version(), ShouldEqual, "0.0") 42 | }) 43 | }) 44 | }) 45 | } 46 | 47 | func TestMiuiBrowserMatch(t *testing.T) { 48 | Convey("Subject: #Match", t, func() { 49 | Convey("When user agent matches MiuiBrowser", func() { 50 | Convey("It should return true", func() { 51 | ua := testUserAgents["miui-browser"] 52 | 53 | So(NewMiuiBrowser(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 54 | So(NewMiuiBrowser(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 55 | So(NewMiuiBrowser(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 56 | So(NewMiuiBrowser(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 57 | }) 58 | }) 59 | 60 | Convey("When user agent does not match MiuiBrowser", func() { 61 | Convey("It should return false", func() { 62 | mb := NewMiuiBrowser(NewUAParser(testUserAgents["chrome"].Linux)) 63 | So(mb.Match(), ShouldBeFalse) 64 | }) 65 | }) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /matchers/nokia.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Nokia struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | nokiaName = "Nokia S40 Ovi Browser" 9 | nokiaVersionRegexp = []string{`S40OviBrowser/([\d.]+)`, `NokiaBrowser/([\d.]+)`} 10 | nokiaMatchRegexp = []string{`S40OviBrowser`, `NokiaBrowser`} 11 | ) 12 | 13 | func NewNokia(p Parser) *Nokia { 14 | return &Nokia{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (n *Nokia) Name() string { 20 | return nokiaName 21 | } 22 | 23 | func (n *Nokia) Version() string { 24 | return n.p.Version(nokiaVersionRegexp, 1) 25 | } 26 | 27 | func (n *Nokia) Match() bool { 28 | return n.p.Match(nokiaMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/nokia_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewNokia(t *testing.T) { 10 | Convey("Subject: #NewNokia", t, func() { 11 | Convey("It should return a new Nokia instance", func() { 12 | So(NewNokia(NewUAParser("")), ShouldHaveSameTypeAs, &Nokia{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestNokiaName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Nokia S40 Ovi Browser", func() { 20 | n := NewNokia(NewUAParser("")) 21 | So(n.Name(), ShouldEqual, "Nokia S40 Ovi Browser") 22 | }) 23 | }) 24 | } 25 | 26 | func TestNokiaVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ua := testUserAgents["nokia"] 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the version", func() { 31 | So(NewNokia(NewUAParser(ua.Android)).Version(), ShouldEqual, "1.2.0.12") 32 | So(NewNokia(NewUAParser(ua.Linux)).Version(), ShouldEqual, "2.3.0.0.48") 33 | }) 34 | }) 35 | }) 36 | 37 | Convey("When the version is not matched", t, func() { 38 | Convey("It should return default version", func() { 39 | n := NewNokia(NewUAParser(testUserAgents["chrome"].Linux)) 40 | So(n.Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | } 44 | 45 | func TestNokiaMatch(t *testing.T) { 46 | Convey("Subject: #Match", t, func() { 47 | Convey("When user agent matches Nokia", func() { 48 | Convey("It should return true", func() { 49 | ua := testUserAgents["nokia"] 50 | 51 | So(NewNokia(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 52 | So(NewNokia(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 53 | }) 54 | }) 55 | 56 | Convey("When user agent does not match Nokia", func() { 57 | Convey("It should return false", func() { 58 | ua := testUserAgents["chrome"] 59 | 60 | So(NewNokia(NewUAParser(ua.Linux)).Match(), ShouldBeFalse) 61 | }) 62 | }) 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /matchers/opera.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Opera struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | operaName = "Opera" 9 | // order matters for version detection 10 | operaVersionRegexp = []string{`Opera Mini/([\d.]+)`, `OP(?:R|iOS|T)/([\d.]+)`, `Opera/([\d.]+)`, `Version/([\d.]+)`} 11 | operaMatchRegexp = []string{`(Opera|OP(R|iOS|T)/)`} 12 | ) 13 | 14 | func NewOpera(p Parser) *Opera { 15 | return &Opera{ 16 | p: p, 17 | } 18 | } 19 | 20 | func (o *Opera) Name() string { 21 | return operaName 22 | } 23 | 24 | func (o *Opera) Version() string { 25 | return o.p.Version(operaVersionRegexp, 1) 26 | } 27 | 28 | func (o *Opera) Match() bool { 29 | return o.p.Match(operaMatchRegexp) 30 | } 31 | -------------------------------------------------------------------------------- /matchers/opera_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewOpera(t *testing.T) { 10 | Convey("Subject: #NewOpera", t, func() { 11 | Convey("It should return a new Opera instance", func() { 12 | So(NewOpera(NewUAParser("")), ShouldHaveSameTypeAs, &Opera{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestOperaName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Opera", func() { 20 | o := NewOpera(NewUAParser("")) 21 | So(o.Name(), ShouldEqual, "Opera") 22 | }) 23 | }) 24 | } 25 | 26 | func TestOperaVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | // Opera browsers 31 | op := testUserAgents["opera"] 32 | So(NewOpera(NewUAParser(op.Linux)).Version(), ShouldEqual, "31.0.1889.174") 33 | So(NewOpera(NewUAParser(op.Mac)).Version(), ShouldEqual, "31.0.1889.174") 34 | So(NewOpera(NewUAParser(op.Windows)).Version(), ShouldEqual, "31.0.1889.174") 35 | So(NewOpera(NewUAParser(op.IOS)).Version(), ShouldEqual, "12.0") 36 | So(NewOpera(NewUAParser(op.Android)).Version(), ShouldEqual, "28.0.1764.90386") 37 | 38 | // Opera Mini browsers 39 | opm := testUserAgents["opera-mini"] 40 | So(NewOpera(NewUAParser(opm.Linux)).Version(), ShouldEqual, "7.6.11") 41 | So(NewOpera(NewUAParser(opm.Mac)).Version(), ShouldEqual, "10.2.0.93022") 42 | So(NewOpera(NewUAParser(opm.Windows)).Version(), ShouldEqual, "8.1.0") 43 | So(NewOpera(NewUAParser(opm.IOS)).Version(), ShouldEqual, "10.2.0") 44 | So(NewOpera(NewUAParser(opm.Android)).Version(), ShouldEqual, "10.0.1884") 45 | 46 | // Opera Touch browsers 47 | opt := testUserAgents["opera-touch"] 48 | So(NewOpera(NewUAParser(opt.Linux)).Version(), ShouldEqual, "1.6.18") 49 | So(NewOpera(NewUAParser(opt.Mac)).Version(), ShouldEqual, "69") 50 | So(NewOpera(NewUAParser(opt.Windows)).Version(), ShouldEqual, "1.6.18") 51 | So(NewOpera(NewUAParser(opt.IOS)).Version(), ShouldEqual, "1.0.1.58") 52 | So(NewOpera(NewUAParser(opt.Android)).Version(), ShouldEqual, "1.0.9") 53 | }) 54 | }) 55 | 56 | Convey("When the version is not matched", func() { 57 | Convey("It should return default version", func() { 58 | o := NewOpera(NewUAParser(testUserAgents["chrome"].Linux)) 59 | So(o.Version(), ShouldEqual, "0.0") 60 | }) 61 | }) 62 | }) 63 | } 64 | 65 | func TestOperaMatch(t *testing.T) { 66 | Convey("Subject: #Match", t, func() { 67 | Convey("When user agent matches Opera", func() { 68 | Convey("It should return true", func() { 69 | // Opera browsers 70 | op := testUserAgents["opera"] 71 | So(NewOpera(NewUAParser(op.Linux)).Match(), ShouldBeTrue) 72 | So(NewOpera(NewUAParser(op.Mac)).Match(), ShouldBeTrue) 73 | So(NewOpera(NewUAParser(op.Windows)).Match(), ShouldBeTrue) 74 | So(NewOpera(NewUAParser(op.IOS)).Match(), ShouldBeTrue) 75 | So(NewOpera(NewUAParser(op.Android)).Match(), ShouldBeTrue) 76 | 77 | // Opera Mini browsers 78 | opm := testUserAgents["opera-mini"] 79 | So(NewOpera(NewUAParser(opm.Linux)).Match(), ShouldBeTrue) 80 | So(NewOpera(NewUAParser(opm.Mac)).Match(), ShouldBeTrue) 81 | So(NewOpera(NewUAParser(opm.Windows)).Match(), ShouldBeTrue) 82 | So(NewOpera(NewUAParser(opm.IOS)).Match(), ShouldBeTrue) 83 | So(NewOpera(NewUAParser(opm.Android)).Match(), ShouldBeTrue) 84 | 85 | // Opera Touch browsers 86 | opt := testUserAgents["opera-touch"] 87 | So(NewOpera(NewUAParser(opt.Linux)).Match(), ShouldBeTrue) 88 | So(NewOpera(NewUAParser(opt.Mac)).Match(), ShouldBeTrue) 89 | So(NewOpera(NewUAParser(opt.Windows)).Match(), ShouldBeTrue) 90 | So(NewOpera(NewUAParser(opt.IOS)).Match(), ShouldBeTrue) 91 | So(NewOpera(NewUAParser(opt.Android)).Match(), ShouldBeTrue) 92 | }) 93 | }) 94 | 95 | Convey("When user agent does not match Opera", func() { 96 | Convey("It should return false", func() { 97 | o := NewOpera(NewUAParser(testUserAgents["chrome"].Linux)) 98 | So(o.Match(), ShouldBeFalse) 99 | }) 100 | }) 101 | }) 102 | } 103 | -------------------------------------------------------------------------------- /matchers/otter.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Otter struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | otterName = "Otter" 9 | otterVersionRegexp = []string{`Otter/([\d.]+)`} 10 | otterMatchRegexp = []string{`Otter`} 11 | ) 12 | 13 | func NewOtter(p Parser) *Otter { 14 | return &Otter{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (o *Otter) Name() string { 20 | return otterName 21 | } 22 | 23 | func (o *Otter) Version() string { 24 | return o.p.Version(otterVersionRegexp, 1) 25 | } 26 | 27 | func (o *Otter) Match() bool { 28 | return o.p.Match(otterMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/otter_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewOtter(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | So(NewOtter(NewUAParser("")), ShouldHaveSameTypeAs, &Otter{}) 12 | }) 13 | } 14 | 15 | func TestOtterName(t *testing.T) { 16 | Convey("Subject: #Name", t, func() { 17 | Convey("It should return Otter", func() { 18 | s := NewOtter(NewUAParser("")) 19 | So(s.Name(), ShouldEqual, "Otter") 20 | }) 21 | }) 22 | } 23 | 24 | func TestOtterVersion(t *testing.T) { 25 | Convey("Subject: #Version", t, func() { 26 | Convey("It should return the version", func() { 27 | ua := testUserAgents["otter"] 28 | 29 | So(NewOtter(NewUAParser(ua.Windows)).Version(), ShouldEqual, "1.0.02") 30 | So(NewOtter(NewUAParser(ua.Mac)).Version(), ShouldEqual, "0.9.11") 31 | So(NewOtter(NewUAParser(ua.Linux)).Version(), ShouldEqual, "0.9.08") 32 | }) 33 | 34 | Convey("It should return default version", func() { 35 | o := NewOtter(NewUAParser(testUserAgents["alipay"].Linux)) 36 | So(o.Version(), ShouldEqual, "0.0") 37 | }) 38 | }) 39 | } 40 | 41 | func TestOtterMatch(t *testing.T) { 42 | Convey("Subject: #Match", t, func() { 43 | Convey("When the user agent matches", func() { 44 | Convey("It should return true", func() { 45 | ua := testUserAgents["otter"] 46 | So(NewOtter(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 47 | So(NewOtter(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 48 | So(NewOtter(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 49 | }) 50 | }) 51 | 52 | Convey("When the user agent doesn't match", func() { 53 | Convey("It should return false", func() { 54 | ua := testUserAgents["safari"] 55 | So(NewOtter(NewUAParser(ua.Windows)).Match(), ShouldBeFalse) 56 | }) 57 | }) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /matchers/pale_moon.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type PaleMoon struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | paleMoonName = "Pale Moon" 9 | paleMoonVersionRegexp = []string{`PaleMoon/([\d.]+)`} 10 | paleMoonMatchRegex = []string{`PaleMoon`} 11 | ) 12 | 13 | func NewPaleMoon(p Parser) *PaleMoon { 14 | return &PaleMoon{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (pa *PaleMoon) Name() string { 20 | return paleMoonName 21 | } 22 | 23 | func (pa *PaleMoon) Version() string { 24 | return pa.p.Version(paleMoonVersionRegexp, 1) 25 | } 26 | 27 | func (pa *PaleMoon) Match() bool { 28 | return pa.p.Match(paleMoonMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/pale_moon_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewPaleMoon(t *testing.T) { 10 | Convey("Subject: #NewPaleMoon", t, func() { 11 | Convey("It should return a new PaleMoon instance", func() { 12 | So(NewPaleMoon(NewUAParser("")), ShouldHaveSameTypeAs, &PaleMoon{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestPaleMoonName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return PaleMoon", func() { 20 | So(NewPaleMoon(NewUAParser("")).Name(), ShouldEqual, "Pale Moon") 21 | }) 22 | }) 23 | } 24 | 25 | func TestPaleMoonVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | Convey("It should return the version", func() { 29 | ua := testUserAgents["pale-moon"] 30 | 31 | So(NewPaleMoon(NewUAParser(ua.Linux)).Version(), ShouldEqual, "25.6.0") 32 | So(NewPaleMoon(NewUAParser(ua.Mac)).Version(), ShouldEqual, "25.3.1") 33 | So(NewPaleMoon(NewUAParser(ua.Windows)).Version(), ShouldEqual, "25.6.0") 34 | So(NewPaleMoon(NewUAParser(ua.Android)).Version(), ShouldEqual, "25.4.1") 35 | }) 36 | }) 37 | 38 | Convey("When the version is not matched", func() { 39 | Convey("It should return default version", func() { 40 | pm := NewPaleMoon(NewUAParser(testUserAgents["chrome"].Linux)) 41 | So(pm.Version(), ShouldEqual, "0.0") 42 | }) 43 | }) 44 | }) 45 | } 46 | 47 | func TestPaleMoonMatch(t *testing.T) { 48 | Convey("Subject: #Match", t, func() { 49 | Convey("When user agent matches PaleMoon", func() { 50 | Convey("It should return true", func() { 51 | ua := testUserAgents["pale-moon"] 52 | 53 | So(NewPaleMoon(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 54 | So(NewPaleMoon(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 55 | So(NewPaleMoon(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 56 | So(NewPaleMoon(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 57 | }) 58 | }) 59 | 60 | Convey("When user agent does not match PaleMoon", func() { 61 | Convey("It should return false", func() { 62 | ua := testUserAgents["chrome"] 63 | 64 | So(NewPaleMoon(NewUAParser(ua.Linux)).Match(), ShouldBeFalse) 65 | }) 66 | }) 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /matchers/parser.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | // Parser is an interface for user agent parsers. 8 | type Parser interface { 9 | String() string 10 | Version([]string, int) string 11 | Match([]string) bool 12 | } 13 | 14 | // compile time check if UAParser implements Parser interface 15 | var _ Parser = (*UAParser)(nil) 16 | 17 | type UAParser struct { 18 | userAgent string 19 | } 20 | 21 | func NewUAParser(userAgent string) *UAParser { 22 | return &UAParser{ 23 | userAgent: userAgent, 24 | } 25 | } 26 | 27 | func (b UAParser) String() string { 28 | return b.userAgent 29 | } 30 | 31 | // Version returns the first match of the given patterns. 32 | // The pattern is a list of regular expressions. 33 | // The order is the index of the match group in the regular expression. 34 | // If the order is greater than the number of matches, it returns "0.0". 35 | func (b UAParser) Version(patterns []string, order int) string { 36 | for _, pattern := range patterns { 37 | re := regexp.MustCompile(pattern) 38 | matches := re.FindStringSubmatch(b.userAgent) 39 | 40 | if len(matches) > order { 41 | return matches[order] 42 | } 43 | } 44 | return "0.0" 45 | } 46 | 47 | // Match returns true if the user agent matches the pattern. 48 | // The pattern is a list of regular expressions. 49 | func (b UAParser) Match(patterns []string) bool { 50 | for _, pattern := range patterns { 51 | if regexp.MustCompile(pattern).MatchString(b.userAgent) { 52 | return true 53 | } 54 | } 55 | return false 56 | } 57 | -------------------------------------------------------------------------------- /matchers/parser_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestUAParserMatch(t *testing.T) { 10 | Convey("Subject: #match", t, func() { 11 | Convey("When user agent matches", func() { 12 | Convey("It should return true", func() { 13 | b := NewUAParser("Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Mobile Safari/537.36") 14 | So(b.Match([]string{`Android`}), ShouldBeTrue) 15 | }) 16 | }) 17 | 18 | Convey("When user agent does not match", func() { 19 | Convey("It should return false", func() { 20 | b := NewUAParser("Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Mobile Safari/537.36") 21 | So(b.Match([]string{`Konqueror`}), ShouldBeFalse) 22 | }) 23 | }) 24 | }) 25 | } 26 | 27 | func TestUAParserVersion(t *testing.T) { 28 | Convey("Subject: #version", t, func() { 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the correct version", func() { 31 | b := NewUAParser("Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Mobile Safari/537.36") 32 | So(b.Version([]string{`Chrome/(\d+)\.(\d+)`}, 1), ShouldEqual, "87") 33 | }) 34 | }) 35 | 36 | Convey("When the version is not matched", func() { 37 | Convey("It should return 0.0", func() { 38 | b := NewUAParser("Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko)") 39 | So(b.Version([]string{`Chrome/(\d+)\.(\d+)`}, 1), ShouldEqual, "0.0") 40 | }) 41 | }) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /matchers/puffin.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Puffin struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | puffinName = "Puffin" 9 | puffinVersionReg = []string{`(?i)Puffin/([\d.]+)`} 10 | puffinMatchRegex = []string{`(?i)Puffin`} 11 | ) 12 | 13 | func NewPuffin(p Parser) *Puffin { 14 | return &Puffin{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (pu *Puffin) Name() string { 20 | return puffinName 21 | } 22 | 23 | func (pu *Puffin) Version() string { 24 | return pu.p.Version(puffinVersionReg, 1) 25 | } 26 | 27 | func (pu *Puffin) Match() bool { 28 | return pu.p.Match(puffinMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/puffin_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewPuffin(t *testing.T) { 10 | Convey("Subject: #NewPuffin", t, func() { 11 | Convey("It should return a new Puffin instance", func() { 12 | So(NewPuffin(NewUAParser("")), ShouldHaveSameTypeAs, &Puffin{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestPuffinName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Puffin", func() { 20 | p := NewPuffin(NewUAParser("")) 21 | So(p.Name(), ShouldEqual, "Puffin") 22 | }) 23 | }) 24 | } 25 | 26 | func TestPuffinVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ua := testUserAgents["puffin"] 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the version", func() { 31 | So(NewPuffin(NewUAParser(ua.Android)).Version(), ShouldEqual, "4.3.0.1852") 32 | So(NewPuffin(NewUAParser(ua.IOS)).Version(), ShouldEqual, "4.5.0") 33 | So(NewPuffin(NewUAParser(ua.Linux)).Version(), ShouldEqual, "610") 34 | }) 35 | }) 36 | 37 | Convey("When the version is not matched", func() { 38 | Convey("It should return default version", func() { 39 | So(NewPuffin(NewUAParser(ua.Mac)).Version(), ShouldEqual, "0.0") 40 | So(NewPuffin(NewUAParser(ua.Windows)).Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestPuffinMatch(t *testing.T) { 47 | Convey("Subject: #Match", t, func() { 48 | Convey("When user agent matches Puffin", func() { 49 | Convey("It should return true", func() { 50 | ua := testUserAgents["puffin"] 51 | 52 | So(NewPuffin(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 53 | So(NewPuffin(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 54 | So(NewPuffin(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 55 | So(NewPuffin(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 56 | }) 57 | }) 58 | 59 | Convey("When user agent does not match Puffin", func() { 60 | Convey("It should return false", func() { 61 | ua := testUserAgents["chrome"] 62 | 63 | So(NewPuffin(NewUAParser(ua.Linux)).Match(), ShouldBeFalse) 64 | }) 65 | }) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /matchers/qq.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type QQ struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | qqName = "QQ Browser" 9 | qqVersionRegexp = []string{`(?i)(?:MQQBrowser|QQBrowserLite|QQBrowser|QQ)/([\d.]+)`} 10 | qqMatchRegexp = []string{`QQ/|QQBrowser`} 11 | ) 12 | 13 | func NewQQ(p Parser) *QQ { 14 | return &QQ{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (q *QQ) Name() string { 20 | return qqName 21 | } 22 | 23 | func (q *QQ) Version() string { 24 | return q.p.Version(qqVersionRegexp, 1) 25 | } 26 | 27 | func (q *QQ) Match() bool { 28 | return q.p.Match(qqMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/qq_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewQQ(t *testing.T) { 10 | Convey("Subject: #NewQQ", t, func() { 11 | Convey("It should return a new QQ instance", func() { 12 | So(NewQQ(NewUAParser("")), ShouldHaveSameTypeAs, &QQ{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestQQName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return QQ", func() { 20 | qq := NewQQ(NewUAParser("")) 21 | So(qq.Name(), ShouldEqual, qqName) 22 | }) 23 | }) 24 | } 25 | 26 | func TestQQVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | ua := testUserAgents["qq"] 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the version", func() { 31 | So(NewQQ(NewUAParser(ua.Windows)).Version(), ShouldEqual, "9.1.3441.400") 32 | So(NewQQ(NewUAParser(ua.Mac)).Version(), ShouldEqual, "3.8.3858.400") 33 | So(NewQQ(NewUAParser(ua.Android)).Version(), ShouldEqual, "6.1") 34 | So(NewQQ(NewUAParser(ua.IOS)).Version(), ShouldEqual, "451") 35 | So(NewQQ(NewUAParser(ua.Linux)).Version(), ShouldEqual, "3.3") 36 | }) 37 | }) 38 | 39 | Convey("When the version is not matched", func() { 40 | Convey("It should return default version", func() { 41 | ua := testUserAgents["chrome"] 42 | So(NewQQ(NewUAParser(ua.Linux)).Version(), ShouldEqual, "0.0") 43 | }) 44 | }) 45 | }) 46 | } 47 | 48 | func TestQQMatch(t *testing.T) { 49 | Convey("Subject: #Match", t, func() { 50 | Convey("When user agent matches QQ", func() { 51 | Convey("It should return true", func() { 52 | ua := testUserAgents["qq"] 53 | 54 | So(NewQQ(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 55 | So(NewQQ(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 56 | So(NewQQ(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 57 | So(NewQQ(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 58 | So(NewQQ(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 59 | }) 60 | }) 61 | 62 | Convey("When user agent does not match QQ", func() { 63 | Convey("It should return false", func() { 64 | ua := testUserAgents["chrome"] 65 | 66 | So(NewQQ(NewUAParser(ua.Linux)).Match(), ShouldBeFalse) 67 | }) 68 | }) 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /matchers/safari.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Safari struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | safariName = "Safari" 9 | safariVersionRegexp = []string{`Version/([\d.]+)`, `Safari/([\d.]+)`, `AppleWebKit/([\d.]+)`} 10 | safariMatchRegex = []string{`Safari`} 11 | ) 12 | 13 | func NewSafari(p Parser) *Safari { 14 | return &Safari{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (s *Safari) Name() string { 20 | return safariName 21 | } 22 | 23 | func (s *Safari) Version() string { 24 | return s.p.Version(safariVersionRegexp, 1) 25 | } 26 | 27 | func (s *Safari) Match() bool { 28 | return s.p.Match(safariMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/safari_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewSafari(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | ua := testUserAgents["safari"] 12 | So(NewSafari(NewUAParser(ua.Mac)), ShouldHaveSameTypeAs, &Safari{}) 13 | }) 14 | } 15 | 16 | func TestSafariName(t *testing.T) { 17 | Convey("Subject: #Name", t, func() { 18 | Convey("It should return Safari", func() { 19 | ua := testUserAgents["safari"] 20 | So(NewSafari(NewUAParser(ua.Mac)).Name(), ShouldEqual, safariName) 21 | }) 22 | }) 23 | } 24 | 25 | func TestSafariVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | ua := testUserAgents["safari"] 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | So(NewSafari(NewUAParser(ua.Mac)).Version(), ShouldEqual, "8.0.7") 31 | So(NewSafari(NewUAParser(ua.IOS)).Version(), ShouldEqual, "8.0") 32 | }) 33 | }) 34 | 35 | Convey("When the version is not matched", func() { 36 | Convey("It should return default version", func() { 37 | So(NewSafari(NewUAParser("")).Version(), ShouldEqual, "0.0") 38 | }) 39 | }) 40 | }) 41 | } 42 | 43 | func TestSafariMatch(t *testing.T) { 44 | Convey("Subject: #Match", t, func() { 45 | Convey("When user agent matches Safari", func() { 46 | Convey("It should return true", func() { 47 | ua := testUserAgents["safari"] 48 | 49 | So(NewSafari(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 50 | So(NewSafari(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 51 | }) 52 | }) 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /matchers/samsung_browser.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type SamsungBrowser struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | samsungBrowserName = "Samsung Browser" 9 | samsungBrowserVersionRegex = []string{ 10 | `(?i)SamsungBrowser/([\d.]+)`, 11 | `Chrome/([\d.]+)`, 12 | `CriOS/([\d.]+)`, 13 | `Safari/([\d.]+)`, 14 | `AppleWebKit/([\d.]+)`, 15 | } 16 | samsungBrowserMatchRegex = []string{`SamsungBrowser`} 17 | ) 18 | 19 | func NewSamsungBrowser(p Parser) *SamsungBrowser { 20 | return &SamsungBrowser{ 21 | p: p, 22 | } 23 | } 24 | 25 | func (s *SamsungBrowser) Name() string { 26 | return samsungBrowserName 27 | } 28 | 29 | func (s *SamsungBrowser) Version() string { 30 | return s.p.Version(samsungBrowserVersionRegex, 1) 31 | } 32 | 33 | func (s *SamsungBrowser) Match() bool { 34 | return s.p.Match(samsungBrowserMatchRegex) 35 | } 36 | -------------------------------------------------------------------------------- /matchers/samsung_browser_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewSamsungBrowser(t *testing.T) { 10 | Convey("Subject: #NewSamsungBrowser", t, func() { 11 | Convey("It should return a new SamsungBrowser instance", func() { 12 | So(NewSamsungBrowser(NewUAParser("")), ShouldHaveSameTypeAs, &SamsungBrowser{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestSamsungBrowserName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Samsung Browser", func() { 20 | sb := NewSamsungBrowser(NewUAParser("")) 21 | So(sb.Name(), ShouldEqual, "Samsung Browser") 22 | }) 23 | }) 24 | } 25 | 26 | func TestSamsungBrowserVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | sb := testUserAgents["samsung-browser"] 31 | 32 | So(NewSamsungBrowser(NewUAParser(sb.Android)).Version(), ShouldEqual, "3.2") 33 | So(NewSamsungBrowser(NewUAParser(sb.IOS)).Version(), ShouldEqual, "5.4") 34 | So(NewSamsungBrowser(NewUAParser(sb.Linux)).Version(), ShouldEqual, "1.0") 35 | So(NewSamsungBrowser(NewUAParser(sb.Windows)).Version(), ShouldEqual, "5.2") 36 | }) 37 | }) 38 | 39 | Convey("When the version is not matched", func() { 40 | Convey("It should return default version", func() { 41 | sb := NewSamsungBrowser(NewUAParser("Mozilla/5.0 (Linux; Android 4.4.2; SM-G900F Build/KOT49H)")) 42 | So(sb.Version(), ShouldEqual, "0.0") 43 | }) 44 | }) 45 | }) 46 | } 47 | 48 | func TestSamsungBrowserMatch(t *testing.T) { 49 | Convey("Subject: #Match", t, func() { 50 | Convey("When user agent matches Samsung Browser", func() { 51 | Convey("It should return true", func() { 52 | sb := testUserAgents["samsung-browser"] 53 | 54 | So(NewSamsungBrowser(NewUAParser(sb.Android)).Match(), ShouldBeTrue) 55 | So(NewSamsungBrowser(NewUAParser(sb.IOS)).Match(), ShouldBeTrue) 56 | So(NewSamsungBrowser(NewUAParser(sb.Linux)).Match(), ShouldBeTrue) 57 | So(NewSamsungBrowser(NewUAParser(sb.Windows)).Match(), ShouldBeTrue) 58 | }) 59 | }) 60 | 61 | Convey("When user agent does not match Samsung Browser", func() { 62 | Convey("It should return false", func() { 63 | sb := NewSamsungBrowser(NewUAParser(testUserAgents["chrome"].Linux)) 64 | So(sb.Match(), ShouldBeFalse) 65 | }) 66 | }) 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /matchers/snapchat.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Snapchat struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | snapchatName = "Snapchat" 9 | snapchatVersionRegexp = []string{`Snapchat(?: |/)?([\d.]+)`} 10 | snapchatMatchRegexp = []string{`Snapchat`} 11 | ) 12 | 13 | func NewSnapchat(p Parser) *Snapchat { 14 | return &Snapchat{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (s *Snapchat) Name() string { 20 | return snapchatName 21 | } 22 | 23 | func (s *Snapchat) Version() string { 24 | return s.p.Version(snapchatVersionRegexp, 1) 25 | } 26 | 27 | func (s *Snapchat) Match() bool { 28 | return s.p.Match(snapchatMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/snapchat_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewSnapchat(t *testing.T) { 10 | Convey("Subject: #NewSnapchat", t, func() { 11 | Convey("It should return a new Snapchat instance", func() { 12 | So(NewSnapchat(NewUAParser("")), ShouldHaveSameTypeAs, &Snapchat{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestSnapchatName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Snapchat", func() { 20 | s := NewSnapchat(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "Snapchat") 22 | }) 23 | }) 24 | } 25 | 26 | func TestSnapchatVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | s := testUserAgents["snapchat"] 31 | 32 | So(NewSnapchat(NewUAParser(s.Android)).Version(), ShouldEqual, "10.18.2.0") 33 | So(NewSnapchat(NewUAParser(s.IOS)).Version(), ShouldEqual, "10.22.2.0") 34 | So(NewSnapchat(NewUAParser(s.Windows)).Version(), ShouldEqual, "10.72.5.0") 35 | }) 36 | }) 37 | }) 38 | } 39 | 40 | func TestSnapchatMatch(t *testing.T) { 41 | Convey("Subject: #Match", t, func() { 42 | Convey("When user agent matches Snapchat", func() { 43 | Convey("It should return true", func() { 44 | s := testUserAgents["snapchat"] 45 | 46 | So(NewSnapchat(NewUAParser(s.Android)).Match(), ShouldBeTrue) 47 | So(NewSnapchat(NewUAParser(s.IOS)).Match(), ShouldBeTrue) 48 | So(NewSnapchat(NewUAParser(s.Windows)).Match(), ShouldBeTrue) 49 | }) 50 | }) 51 | 52 | Convey("When user agent does not match Snapchat", func() { 53 | Convey("It should return false", func() { 54 | s := testUserAgents["chrome"] 55 | 56 | So(NewSnapchat(NewUAParser(s.Linux)).Match(), ShouldBeFalse) 57 | }) 58 | }) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /matchers/sogou_browser.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type SogouBrowser struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | sogouBrowserName = "Sogou Browser" 9 | sogouBrowserVersionRegexp = []string{`(?i)(?:SogouMobileBrowser)/([\d.]+)`} 10 | sogouBrowserMatchRegexp = []string{`(?i)SogouMobileBrowser`, `\bSE\b`} 11 | ) 12 | 13 | func NewSogouBrowser(p Parser) *SogouBrowser { 14 | return &SogouBrowser{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (s *SogouBrowser) Name() string { 20 | return sogouBrowserName 21 | } 22 | 23 | func (s *SogouBrowser) Version() string { 24 | return s.p.Version(sogouBrowserVersionRegexp, 1) 25 | } 26 | 27 | func (s *SogouBrowser) Match() bool { 28 | return s.p.Match(sogouBrowserMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/sogou_browser_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewSogouBrowser(t *testing.T) { 10 | Convey("Subject: #NewSogouBrowser", t, func() { 11 | Convey("It should return a new SogouBrowser instance", func() { 12 | So(NewSogouBrowser(NewUAParser("")), ShouldHaveSameTypeAs, &SogouBrowser{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestSogouBrowserName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Sogou Browser", func() { 20 | s := NewSogouBrowser(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "Sogou Browser") 22 | }) 23 | }) 24 | } 25 | 26 | func TestSogouBrowserVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | s := testUserAgents["sogou-browser"] 31 | 32 | So(NewSogouBrowser(NewUAParser(s.Android)).Version(), ShouldEqual, "3.8.2") 33 | So(NewSogouBrowser(NewUAParser(s.IOS)).Version(), ShouldEqual, "5.3.0") 34 | }) 35 | }) 36 | }) 37 | } 38 | 39 | func TestSogouBrowserMatch(t *testing.T) { 40 | Convey("Subject: #Match", t, func() { 41 | Convey("When user agent matches Sogou Browser", func() { 42 | Convey("It should return true", func() { 43 | s := testUserAgents["sogou-browser"] 44 | 45 | So(NewSogouBrowser(NewUAParser(s.Android)).Match(), ShouldBeTrue) 46 | So(NewSogouBrowser(NewUAParser(s.IOS)).Match(), ShouldBeTrue) 47 | }) 48 | }) 49 | 50 | Convey("When user agent does not match Sogou Browser", func() { 51 | Convey("It should return false", func() { 52 | s := testUserAgents["chrome"] 53 | 54 | So(NewSogouBrowser(NewUAParser(s.Linux)).Match(), ShouldBeFalse) 55 | }) 56 | }) 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /matchers/sputnik.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Sputnik struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | sputnikName = "Sputnik" 9 | sputnikVersionRegexp = []string{`SputnikBrowser/([\d.]+)`} 10 | sputnikMatchRegexp = []string{`SputnikBrowser`} 11 | ) 12 | 13 | func NewSputnik(p Parser) *Sputnik { 14 | return &Sputnik{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (s *Sputnik) Name() string { 20 | return sputnikName 21 | } 22 | 23 | func (s *Sputnik) Version() string { 24 | return s.p.Version(sputnikVersionRegexp, 1) 25 | } 26 | 27 | func (s *Sputnik) Match() bool { 28 | return s.p.Match(sputnikMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/sputnik_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewSputnik(t *testing.T) { 10 | Convey("Subject: #NewSputnik", t, func() { 11 | Convey("It should return a new Sputnik instance", func() { 12 | So(NewSputnik(NewUAParser("")), ShouldHaveSameTypeAs, &Sputnik{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestSputnikName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Sputnik", func() { 20 | s := NewSputnik(NewUAParser("")) 21 | So(s.Name(), ShouldEqual, "Sputnik") 22 | }) 23 | }) 24 | } 25 | 26 | func TestSputnikVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | s := testUserAgents["sputnik"] 31 | 32 | So(NewSputnik(NewUAParser(s.Android)).Version(), ShouldEqual, "0.6.1") 33 | So(NewSputnik(NewUAParser(s.IOS)).Version(), ShouldEqual, "2.3.8") 34 | So(NewSputnik(NewUAParser(s.Linux)).Version(), ShouldEqual, "3.3.2038.2") 35 | So(NewSputnik(NewUAParser(s.Windows)).Version(), ShouldEqual, "1.9.48.2") 36 | }) 37 | }) 38 | }) 39 | } 40 | 41 | func TestSputnikMatch(t *testing.T) { 42 | Convey("Subject: #Match", t, func() { 43 | Convey("When user agent matches Sputnik", func() { 44 | Convey("It should return true", func() { 45 | s := testUserAgents["sputnik"] 46 | 47 | So(NewSputnik(NewUAParser(s.Android)).Match(), ShouldBeTrue) 48 | So(NewSputnik(NewUAParser(s.IOS)).Match(), ShouldBeTrue) 49 | So(NewSputnik(NewUAParser(s.Linux)).Match(), ShouldBeTrue) 50 | So(NewSputnik(NewUAParser(s.Windows)).Match(), ShouldBeTrue) 51 | }) 52 | }) 53 | 54 | Convey("When user agent does not match Sputnik", func() { 55 | Convey("It should return false", func() { 56 | s := testUserAgents["chrome"] 57 | 58 | So(NewSputnik(NewUAParser(s.Linux)).Match(), ShouldBeFalse) 59 | }) 60 | }) 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /matchers/uc_browser.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type UCBrowser struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | ucBrowserName = "UCBrowser" 9 | ucBrowserVersionRegexp = []string{`UCBrowser/([\d.]+)`, `UCWEB(?:/)?([\d.]+)`} 10 | ucBrowserMatchRegexp = []string{`UC(Browser|WEB)`} 11 | ) 12 | 13 | func NewUCBrowser(p Parser) *UCBrowser { 14 | return &UCBrowser{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (u *UCBrowser) Name() string { 20 | return ucBrowserName 21 | } 22 | 23 | func (u *UCBrowser) Version() string { 24 | return u.p.Version(ucBrowserVersionRegexp, 1) 25 | } 26 | 27 | func (u *UCBrowser) Match() bool { 28 | return u.p.Match(ucBrowserMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/uc_browser_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewUCBrowser(t *testing.T) { 10 | Convey("Subject: #NewUCBrowser", t, func() { 11 | Convey("It should return a new UCBrowser instance", func() { 12 | So(NewUCBrowser(NewUAParser("")), ShouldHaveSameTypeAs, &UCBrowser{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestUCBrowserName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return UCBrowser", func() { 20 | uc := NewUCBrowser(NewUAParser("")) 21 | So(uc.Name(), ShouldEqual, "UCBrowser") 22 | }) 23 | }) 24 | } 25 | 26 | func TestUCBrowserVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | uc := testUserAgents["uc-browser"] 31 | 32 | So(NewUCBrowser(NewUAParser(uc.Android)).Version(), ShouldEqual, "10.6.2.599") 33 | So(NewUCBrowser(NewUAParser(uc.IOS)).Version(), ShouldEqual, "10.6.5.627") 34 | So(NewUCBrowser(NewUAParser(uc.Linux)).Version(), ShouldEqual, "7.9.0.94") 35 | So(NewUCBrowser(NewUAParser(uc.Mac)).Version(), ShouldEqual, "3.2.0.417") 36 | So(NewUCBrowser(NewUAParser(uc.Windows)).Version(), ShouldEqual, "4.2.1.541") 37 | }) 38 | }) 39 | 40 | Convey("When the version is not matched", func() { 41 | Convey("It should return default version", func() { 42 | uc := NewUCBrowser(NewUAParser(testUserAgents["chrome"].Linux)) 43 | So(uc.Version(), ShouldEqual, "0.0") 44 | }) 45 | }) 46 | }) 47 | } 48 | 49 | func TestUCBrowserMatch(t *testing.T) { 50 | Convey("Subject: #Match", t, func() { 51 | Convey("When user agent matches UC Browser", func() { 52 | Convey("It should return true", func() { 53 | uc := testUserAgents["uc-browser"] 54 | 55 | So(NewUCBrowser(NewUAParser(uc.Android)).Match(), ShouldBeTrue) 56 | So(NewUCBrowser(NewUAParser(uc.IOS)).Match(), ShouldBeTrue) 57 | So(NewUCBrowser(NewUAParser(uc.Linux)).Match(), ShouldBeTrue) 58 | So(NewUCBrowser(NewUAParser(uc.Mac)).Match(), ShouldBeTrue) 59 | So(NewUCBrowser(NewUAParser(uc.Windows)).Match(), ShouldBeTrue) 60 | }) 61 | }) 62 | 63 | Convey("When user agent does not match UC Browser", func() { 64 | Convey("It should return false", func() { 65 | uc := NewUCBrowser(NewUAParser(testUserAgents["chrome"].Linux)) 66 | So(uc.Match(), ShouldBeFalse) 67 | }) 68 | }) 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /matchers/unknown.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import "regexp" 4 | 5 | type Unknown struct { 6 | p Parser 7 | } 8 | 9 | var ( 10 | unknownName = "Unknown Browser" 11 | unknownVersionRegexp = []string{`QuickTime/([\d.]+)`, `CoreMedia v([\d.]+)`, `AppleCoreMedia/([\d.]+)`} 12 | ) 13 | 14 | func NewUnknown(p Parser) *Unknown { 15 | return &Unknown{ 16 | p: p, 17 | } 18 | } 19 | 20 | func (u *Unknown) Name() string { 21 | inferedUnknowns := map[string]string{"QuickTime": "QuickTime", "CoreMedia": "Apple CoreMedia"} 22 | for k, v := range inferedUnknowns { 23 | if regexp.MustCompile(k).MatchString(u.p.String()) { 24 | return v 25 | } 26 | } 27 | return unknownName 28 | } 29 | 30 | func (u *Unknown) Version() string { 31 | return u.p.Version(unknownVersionRegexp, 1) 32 | } 33 | 34 | func (u *Unknown) Match() bool { 35 | return true 36 | } 37 | -------------------------------------------------------------------------------- /matchers/unknown_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewUnknown(t *testing.T) { 10 | Convey("Subject: #NewUnknown", t, func() { 11 | Convey("Should return a new Unknown instance", func() { 12 | So(NewUnknown(NewUAParser("")), ShouldHaveSameTypeAs, &Unknown{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestUnknownName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("Should return the name of the unknown browser", func() { 20 | ua := testUserAgents["unknown"] 21 | So(NewUnknown(NewUAParser(ua.IOS)).Name(), ShouldEqual, "Apple CoreMedia") 22 | So(NewUnknown(NewUAParser(ua.Mac)).Name(), ShouldEqual, "QuickTime") 23 | So(NewUnknown(NewUAParser("")).Name(), ShouldEqual, "Unknown Browser") 24 | }) 25 | }) 26 | } 27 | 28 | func TestUnknownVersion(t *testing.T) { 29 | Convey("Subject: #Version", t, func() { 30 | Convey("Should return the version of the unknown browser", func() { 31 | ua := testUserAgents["unknown"] 32 | So(NewUnknown(NewUAParser(ua.IOS)).Version(), ShouldEqual, "1.0.0.11") 33 | So(NewUnknown(NewUAParser(ua.Mac)).Version(), ShouldEqual, "0.0") 34 | So(NewUnknown(NewUAParser("")).Version(), ShouldEqual, "0.0") 35 | }) 36 | }) 37 | } 38 | 39 | func TestUnknownMatch(t *testing.T) { 40 | Convey("Subject: #Match", t, func() { 41 | Convey("Should return true", func() { 42 | So(NewUnknown(NewUAParser("")).Match(), ShouldBeTrue) 43 | }) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /matchers/vivaldi.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | var ( 4 | vivaldiName = "Vivaldi" 5 | vivaldiVersionRegexp = []string{`Vivaldi/([\d.]+)`} 6 | vivaldiMatchRegex = []string{`(?i)Vivaldi`} 7 | ) 8 | 9 | type Vivaldi struct { 10 | p Parser 11 | } 12 | 13 | func NewVivaldi(p Parser) *Vivaldi { 14 | return &Vivaldi{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (v *Vivaldi) Name() string { 20 | return vivaldiName 21 | } 22 | 23 | func (v *Vivaldi) Version() string { 24 | return v.p.Version(vivaldiVersionRegexp, 1) 25 | } 26 | 27 | func (v *Vivaldi) Match() bool { 28 | return v.p.Match(vivaldiMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/vivaldi_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewVivaldi(t *testing.T) { 10 | Convey("Subject: #NewVivaldi", t, func() { 11 | Convey("It should return a new Vivaldi instance", func() { 12 | So(NewVivaldi(NewUAParser("")), ShouldHaveSameTypeAs, &Vivaldi{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestVivaldiName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the name", func() { 20 | So(NewVivaldi(NewUAParser("")).Name(), ShouldEqual, "Vivaldi") 21 | }) 22 | }) 23 | } 24 | 25 | func TestVivaldiVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | ua := testUserAgents["vivaldi"] 28 | Convey("When the version is captured", func() { 29 | Convey("It should return the version", func() { 30 | So(NewVivaldi(NewUAParser(ua.Linux)).Version(), ShouldEqual, "1.0.264.3") 31 | So(NewVivaldi(NewUAParser(ua.Mac)).Version(), ShouldEqual, "1.0.252.3") 32 | So(NewVivaldi(NewUAParser(ua.Windows)).Version(), ShouldEqual, "1.0.219.50") 33 | }) 34 | }) 35 | 36 | Convey("When the version is not captured", func() { 37 | ua := testUserAgents["chrome"] 38 | Convey("It should return default version", func() { 39 | So(NewVivaldi(NewUAParser(ua.Android)).Version(), ShouldEqual, "0.0") 40 | So(NewVivaldi(NewUAParser(ua.IOS)).Version(), ShouldEqual, "0.0") 41 | }) 42 | }) 43 | }) 44 | } 45 | 46 | func TestVivaldiMatch(t *testing.T) { 47 | Convey("Subject: #Match", t, func() { 48 | Convey("When the user agent matches", func() { 49 | ua := testUserAgents["vivaldi"] 50 | Convey("It should return true", func() { 51 | So(NewVivaldi(NewUAParser(ua.Linux)).Match(), ShouldBeTrue) 52 | So(NewVivaldi(NewUAParser(ua.Mac)).Match(), ShouldBeTrue) 53 | So(NewVivaldi(NewUAParser(ua.Windows)).Match(), ShouldBeTrue) 54 | }) 55 | }) 56 | 57 | Convey("When the user agent does not match", func() { 58 | ua := testUserAgents["chrome"] 59 | Convey("It should return false", func() { 60 | So(NewVivaldi(NewUAParser(ua.Android)).Match(), ShouldBeFalse) 61 | So(NewVivaldi(NewUAParser(ua.IOS)).Match(), ShouldBeFalse) 62 | }) 63 | }) 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /matchers/vivo_browser.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type VivoBrowser struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | vivoBrowserName = "Vivo Browser" 9 | vivoBrowserVersionReg = []string{`(?i)VivoBrowser/([\d.]+)`} 10 | vivoBrowserMatchRegex = []string{`(?i)VivoBrowser`} 11 | ) 12 | 13 | func NewVivoBrowser(p Parser) *VivoBrowser { 14 | return &VivoBrowser{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (v *VivoBrowser) Name() string { 20 | return vivoBrowserName 21 | } 22 | 23 | func (v *VivoBrowser) Version() string { 24 | return v.p.Version(vivoBrowserVersionReg, 1) 25 | } 26 | 27 | func (v *VivoBrowser) Match() bool { 28 | return v.p.Match(vivoBrowserMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/vivo_browser_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewVivoBrowser(t *testing.T) { 10 | Convey("Subject: #NewVivoBrowser", t, func() { 11 | Convey("It should return a new VivoBrowser instance", func() { 12 | So(NewVivoBrowser(NewUAParser("")), ShouldHaveSameTypeAs, &VivoBrowser{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestVivoBrowserName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the correct name", func() { 20 | vb := NewVivoBrowser(NewUAParser("")) 21 | So(vb.Name(), ShouldEqual, "Vivo Browser") 22 | }) 23 | }) 24 | } 25 | 26 | func TestVivoBrowserVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the correct version", func() { 30 | vb := testUserAgents["vivo-browser"] 31 | 32 | So(NewVivoBrowser(NewUAParser(vb.Android)).Version(), ShouldEqual, "5.0.10") 33 | So(NewVivoBrowser(NewUAParser(vb.IOS)).Version(), ShouldEqual, "5.0.10") 34 | So(NewVivoBrowser(NewUAParser(vb.Linux)).Version(), ShouldEqual, "5.0.2") 35 | So(NewVivoBrowser(NewUAParser(vb.Windows)).Version(), ShouldEqual, "5.4.11") 36 | }) 37 | }) 38 | 39 | Convey("When the version is not matched", func() { 40 | Convey("It should return 0.0", func() { 41 | vb := NewVivoBrowser(NewUAParser(testUserAgents["chrome"].Linux)) 42 | So(vb.Version(), ShouldEqual, "0.0") 43 | }) 44 | }) 45 | }) 46 | } 47 | 48 | func TestVivoBrowserMatch(t *testing.T) { 49 | Convey("Subject: #Match", t, func() { 50 | Convey("When user agent matches", func() { 51 | Convey("It should return true", func() { 52 | vb := testUserAgents["vivo-browser"] 53 | 54 | So(NewVivoBrowser(NewUAParser(vb.Android)).Match(), ShouldBeTrue) 55 | So(NewVivoBrowser(NewUAParser(vb.IOS)).Match(), ShouldBeTrue) 56 | So(NewVivoBrowser(NewUAParser(vb.Linux)).Match(), ShouldBeTrue) 57 | So(NewVivoBrowser(NewUAParser(vb.Windows)).Match(), ShouldBeTrue) 58 | }) 59 | }) 60 | 61 | Convey("When user agent does not match", func() { 62 | Convey("It should return false", func() { 63 | ua := testUserAgents["chrome"].Linux 64 | So(NewVivoBrowser(NewUAParser(ua)).Match(), ShouldBeFalse) 65 | }) 66 | }) 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /matchers/weibo.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Weibo struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | weiboName = "Weibo" 9 | weiboVersionRegexp = []string{`(?i)(?:__weibo__)([\d.]+)`} 10 | weiboMatchRegexp = []string{`__weibo__`} 11 | ) 12 | 13 | func NewWeibo(p Parser) *Weibo { 14 | return &Weibo{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (w *Weibo) Name() string { 20 | return weiboName 21 | } 22 | 23 | func (w *Weibo) Version() string { 24 | return w.p.Version(weiboVersionRegexp, 1) 25 | } 26 | 27 | func (w *Weibo) Match() bool { 28 | return w.p.Match(weiboMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/weibo_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewWeibo(t *testing.T) { 10 | Convey("Given a user agent string", t, func() { 11 | So(NewWeibo(NewUAParser("")), ShouldHaveSameTypeAs, &Weibo{}) 12 | }) 13 | } 14 | 15 | func TestWeiboName(t *testing.T) { 16 | Convey("Subject: #Name", t, func() { 17 | Convey("It should return Weibo", func() { 18 | s := NewWeibo(NewUAParser("")) 19 | So(s.Name(), ShouldEqual, "Weibo") 20 | }) 21 | }) 22 | } 23 | 24 | func TestWeiboVersion(t *testing.T) { 25 | Convey("Subject: #Version", t, func() { 26 | Convey("It should return default version", func() { 27 | ua := testUserAgents["weibo"] 28 | 29 | So(NewWeibo(NewUAParser(ua.Android)).Version(), ShouldEqual, "6.5.0") 30 | So(NewWeibo(NewUAParser(ua.IOS)).Version(), ShouldEqual, "10.9.2") 31 | }) 32 | }) 33 | } 34 | 35 | func TestWeiboMatch(t *testing.T) { 36 | Convey("Subject: #Match", t, func() { 37 | Convey("When the user agent matches", func() { 38 | Convey("It should return true", func() { 39 | ua := testUserAgents["weibo"] 40 | So(NewWeibo(NewUAParser(ua.Android)).Match(), ShouldBeTrue) 41 | So(NewWeibo(NewUAParser(ua.IOS)).Match(), ShouldBeTrue) 42 | }) 43 | }) 44 | 45 | Convey("When the user agent doesn't match", func() { 46 | Convey("It should return false", func() { 47 | ua := testUserAgents["safari"] 48 | So(NewWeibo(NewUAParser(ua.Android)).Match(), ShouldBeFalse) 49 | }) 50 | }) 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /matchers/yaani_browser.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type YaaniBrowser struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | yaaniBrowserName = "Yaani Browser" 9 | yaaniBrowserVersionRegexp = []string{`YaaniBrowser/([\d.]+)`, `Turkcell-YaaniBrowser/([\d.]+)`, `Chrome/([\d.]+)`} 10 | yaaniBrowserMatchRegex = []string{`YaaniBrowser`} 11 | ) 12 | 13 | func NewYaaniBrowser(p Parser) *YaaniBrowser { 14 | return &YaaniBrowser{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (y *YaaniBrowser) Name() string { 20 | return yaaniBrowserName 21 | } 22 | 23 | func (y *YaaniBrowser) Version() string { 24 | return y.p.Version(yaaniBrowserVersionRegexp, 1) 25 | } 26 | 27 | func (y *YaaniBrowser) Match() bool { 28 | return y.p.Match(yaaniBrowserMatchRegex) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/yaani_browser_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewYaaniBrowser(t *testing.T) { 10 | Convey("Subject: #NewYaaniBrowser", t, func() { 11 | Convey("It should return a new YaaniBrowser instance", func() { 12 | So(NewYaaniBrowser(NewUAParser("")), ShouldHaveSameTypeAs, &YaaniBrowser{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestYaaniBrowserName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the correct name", func() { 20 | yb := NewYaaniBrowser(NewUAParser("")) 21 | So(yb.Name(), ShouldEqual, "Yaani Browser") 22 | }) 23 | }) 24 | } 25 | 26 | func TestYaaniBrowserVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the correct version", func() { 30 | yb := testUserAgents["yaani-browser"] 31 | 32 | So(NewYaaniBrowser(NewUAParser(yb.Android)).Version(), ShouldEqual, "3.3.0.301") 33 | So(NewYaaniBrowser(NewUAParser(yb.IOS)).Version(), ShouldEqual, "3.6.355") 34 | So(NewYaaniBrowser(NewUAParser(yb.Linux)).Version(), ShouldEqual, "3.5.0.441") 35 | So(NewYaaniBrowser(NewUAParser(yb.Mac)).Version(), ShouldEqual, "5.1.2") 36 | So(NewYaaniBrowser(NewUAParser(yb.Windows)).Version(), ShouldEqual, "0.1") 37 | }) 38 | }) 39 | 40 | Convey("When the version is not matched", func() { 41 | Convey("It should return 0.0", func() { 42 | yb := NewYaaniBrowser(NewUAParser(testUserAgents["blackberry"].Windows)) 43 | So(yb.Version(), ShouldEqual, "0.0") 44 | }) 45 | }) 46 | }) 47 | } 48 | 49 | func TestYaaniBrowserMatch(t *testing.T) { 50 | Convey("Subject: #Match", t, func() { 51 | Convey("When user agent matches", func() { 52 | Convey("It should return true", func() { 53 | yb := testUserAgents["yaani-browser"] 54 | 55 | So(NewYaaniBrowser(NewUAParser(yb.Android)).Match(), ShouldBeTrue) 56 | So(NewYaaniBrowser(NewUAParser(yb.IOS)).Match(), ShouldBeTrue) 57 | So(NewYaaniBrowser(NewUAParser(yb.Linux)).Match(), ShouldBeTrue) 58 | So(NewYaaniBrowser(NewUAParser(yb.Mac)).Match(), ShouldBeTrue) 59 | So(NewYaaniBrowser(NewUAParser(yb.Windows)).Match(), ShouldBeTrue) 60 | }) 61 | }) 62 | 63 | Convey("When user agent does not match", func() { 64 | Convey("It should return false", func() { 65 | ua := testUserAgents["chrome"].Linux 66 | 67 | So(NewYaaniBrowser(NewUAParser(ua)).Match(), ShouldBeFalse) 68 | }) 69 | }) 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /matchers/yandex.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | type Yandex struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | yandexName = "Yandex" 9 | yandexVersionRegexp = []string{`YaBrowser/([\d.]+)`} 10 | yandexMatchRegexp = []string{`YaBrowser`} 11 | ) 12 | 13 | func NewYandex(p Parser) *Yandex { 14 | return &Yandex{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (y *Yandex) Name() string { 20 | return yandexName 21 | } 22 | 23 | func (y *Yandex) Version() string { 24 | return y.p.Version(yandexVersionRegexp, 1) 25 | } 26 | 27 | func (y *Yandex) Match() bool { 28 | return y.p.Match(yandexMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /matchers/yandex_test.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewYandex(t *testing.T) { 10 | Convey("Subject: #NewYandex", t, func() { 11 | Convey("It should return a new Yandex instance", func() { 12 | So(NewYandex(NewUAParser("")), ShouldHaveSameTypeAs, &Yandex{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestYandexName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Yandex", func() { 20 | yandex := NewYandex(NewUAParser("")) 21 | So(yandex.Name(), ShouldEqual, "Yandex") 22 | }) 23 | }) 24 | } 25 | 26 | func TestYandexVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the correct version", func() { 30 | yandex := testUserAgents["yandex"] 31 | 32 | So(NewYandex(NewUAParser(yandex.Android)).Version(), ShouldEqual, "15.4.2272.3842.01") 33 | So(NewYandex(NewUAParser(yandex.IOS)).Version(), ShouldEqual, "15.9.2403.2772.10") 34 | So(NewYandex(NewUAParser(yandex.Linux)).Version(), ShouldEqual, "15.9.2403.2150") 35 | So(NewYandex(NewUAParser(yandex.Mac)).Version(), ShouldEqual, "15.4.2272.3909") 36 | So(NewYandex(NewUAParser(yandex.Windows)).Version(), ShouldEqual, "15.6.2311.5029") 37 | }) 38 | }) 39 | 40 | Convey("When the version is not matched", func() { 41 | Convey("It should return 0.0", func() { 42 | yandex := NewYandex(NewUAParser(testUserAgents["chrome"].Linux)) 43 | So(yandex.Version(), ShouldEqual, "0.0") 44 | }) 45 | }) 46 | }) 47 | } 48 | 49 | func TestYandexMatch(t *testing.T) { 50 | Convey("Subject: #Match", t, func() { 51 | Convey("When user agent matches Yandex", func() { 52 | Convey("It should return true", func() { 53 | yandex := testUserAgents["yandex"] 54 | 55 | So(NewYandex(NewUAParser(yandex.Android)).Match(), ShouldBeTrue) 56 | So(NewYandex(NewUAParser(yandex.IOS)).Match(), ShouldBeTrue) 57 | So(NewYandex(NewUAParser(yandex.Linux)).Match(), ShouldBeTrue) 58 | So(NewYandex(NewUAParser(yandex.Mac)).Match(), ShouldBeTrue) 59 | So(NewYandex(NewUAParser(yandex.Windows)).Match(), ShouldBeTrue) 60 | }) 61 | }) 62 | 63 | Convey("When user agent does not match Yandex", func() { 64 | Convey("It should return false", func() { 65 | yandex := NewYandex(NewUAParser(testUserAgents["chrome"].Linux)) 66 | So(yandex.Match(), ShouldBeFalse) 67 | }) 68 | }) 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /platforms/adobe_air.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type AdobeAir struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | adobeAirName = "Adobe AIR" 9 | adobeAirVersionRegexp = []string{`AdobeAIR/([\d.]+)`} 10 | adobeAirMatchRegexp = []string{`AdobeAIR`} 11 | ) 12 | 13 | func NewAdobeAir(p Parser) *AdobeAir { 14 | return &AdobeAir{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (a *AdobeAir) Name() string { 20 | return adobeAirName 21 | } 22 | 23 | func (a *AdobeAir) Version() string { 24 | return a.p.Version(adobeAirVersionRegexp, 1, "") 25 | } 26 | 27 | func (a *AdobeAir) Match() bool { 28 | return a.p.Match(adobeAirMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /platforms/adobe_air_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewAdobeAir(t *testing.T) { 10 | Convey("Subject: #NewAdobeAir", t, func() { 11 | Convey("It should return a new AdobeAir instance", func() { 12 | So(NewAdobeAir(NewUAParser("")), ShouldHaveSameTypeAs, &AdobeAir{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestAdobeAirName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return AdobeAir", func() { 20 | aa := NewAdobeAir(NewUAParser("")) 21 | So(aa.Name(), ShouldEqual, "Adobe AIR") 22 | }) 23 | }) 24 | } 25 | 26 | func TestAdobeAirVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the correct version", func() { 30 | aa := testPlatforms["adobe-air"] 31 | 32 | So(NewAdobeAir(NewUAParser(aa)).Version(), ShouldEqual, "18.0") 33 | }) 34 | }) 35 | 36 | Convey("When the version is not matched", func() { 37 | Convey("It should return default version", func() { 38 | a := testPlatforms["android-10"] 39 | So(NewAdobeAir(NewUAParser(a)).Version(), ShouldEqual, "") 40 | }) 41 | }) 42 | }) 43 | } 44 | 45 | func TestAdobeAirMatch(t *testing.T) { 46 | Convey("Subject: #Match", t, func() { 47 | Convey("When user agent matches AdobeAir", func() { 48 | Convey("It should return true", func() { 49 | aa := testPlatforms["adobe-air"] 50 | So(NewAdobeAir(NewUAParser(aa)).Match(), ShouldBeTrue) 51 | }) 52 | }) 53 | 54 | Convey("When user agent does not match", func() { 55 | Convey("It should return false", func() { 56 | a := testPlatforms["android-10"] 57 | So(NewAdobeAir(NewUAParser(a)).Match(), ShouldBeFalse) 58 | }) 59 | }) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /platforms/android.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type Android struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | androidName = "Android" 9 | androidVersionRegexp = []string{`(?i)Android ([\d.]+)`} 10 | androidMatchRegexp = []string{`(?i)Android`} 11 | ) 12 | 13 | func NewAndroid(p Parser) *Android { 14 | return &Android{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (a *Android) Name() string { 20 | return androidName 21 | } 22 | 23 | func (a *Android) Version() string { 24 | return a.p.Version(androidVersionRegexp, 1, "") 25 | } 26 | 27 | func (a *Android) Match() bool { 28 | return a.p.Match(androidMatchRegexp) && !a.p.Match([]string{`KAIOS`}) 29 | } 30 | -------------------------------------------------------------------------------- /platforms/black_berry.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type BlackBerry struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | blackBerryName = "BlackBerry" 9 | blackBerryVersionRegexp = []string{`(?:Version|BlackBerry[\da-z]+)/([\d.]+)`} 10 | blackBerryMatchRegexp = []string{`BB10|BlackBerry`} 11 | ) 12 | 13 | func NewBlackBerry(p Parser) *BlackBerry { 14 | return &BlackBerry{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (b *BlackBerry) Name() string { 20 | return blackBerryName 21 | } 22 | 23 | func (b *BlackBerry) Version() string { 24 | return b.p.Version(blackBerryVersionRegexp, 1, "") 25 | } 26 | 27 | func (b *BlackBerry) Match() bool { 28 | return b.p.Match(blackBerryMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /platforms/black_berry_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewBlackBerry(t *testing.T) { 10 | Convey("Subject: #NewBlackBerry", t, func() { 11 | Convey("It should return a new BlackBerry instance", func() { 12 | So(NewBlackBerry(NewUAParser("")), ShouldHaveSameTypeAs, &BlackBerry{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestBlackBerryName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the correct name", func() { 20 | So(NewBlackBerry(NewUAParser("")).Name(), ShouldEqual, "BlackBerry") 21 | }) 22 | }) 23 | } 24 | 25 | func TestBlackBerryVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | Convey("It should return the correct version", func() { 29 | p := testPlatforms 30 | So(NewBlackBerry(NewUAParser(p["blackberry"])).Version(), ShouldEqual, "4.1.0") 31 | So(NewBlackBerry(NewUAParser(p["blackberry-4"])).Version(), ShouldEqual, "4.2.1") 32 | So(NewBlackBerry(NewUAParser(p["blackberry-5"])).Version(), ShouldEqual, "5.0.0.187") 33 | So(NewBlackBerry(NewUAParser(p["blackberry-6"])).Version(), ShouldEqual, "6.0.0.480") 34 | So(NewBlackBerry(NewUAParser(p["blackberry-7"])).Version(), ShouldEqual, "7.1.0.336") 35 | So(NewBlackBerry(NewUAParser(p["blackberry-10"])).Version(), ShouldEqual, "10.3.2.2339") 36 | }) 37 | }) 38 | }) 39 | } 40 | 41 | func TestBlackBerryMatch(t *testing.T) { 42 | Convey("Subject: #Match", t, func() { 43 | Convey("When user agent matches", func() { 44 | Convey("It should return true", func() { 45 | p := testPlatforms 46 | So(NewBlackBerry(NewUAParser(p["blackberry"])).Match(), ShouldBeTrue) 47 | So(NewBlackBerry(NewUAParser(p["blackberry-4"])).Match(), ShouldBeTrue) 48 | So(NewBlackBerry(NewUAParser(p["blackberry-5"])).Match(), ShouldBeTrue) 49 | So(NewBlackBerry(NewUAParser(p["blackberry-6"])).Match(), ShouldBeTrue) 50 | So(NewBlackBerry(NewUAParser(p["blackberry-7"])).Match(), ShouldBeTrue) 51 | So(NewBlackBerry(NewUAParser(p["blackberry-10"])).Match(), ShouldBeTrue) 52 | }) 53 | }) 54 | 55 | Convey("When user agent does not match", func() { 56 | Convey("It should return false", func() { 57 | So(NewBlackBerry(NewUAParser("")).Match(), ShouldBeFalse) 58 | }) 59 | }) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /platforms/chrome_os.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type ChromeOS struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | chromeOSName = "Chrome OS" 9 | chromeOSVersionRegexp = []string{`CrOS [^\s]+ ([\d.]+)`} 10 | chromeOSMatchRegexp = []string{`CrOS`} 11 | ) 12 | 13 | func NewChromeOS(p Parser) *ChromeOS { 14 | return &ChromeOS{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (c *ChromeOS) Name() string { 20 | return chromeOSName 21 | } 22 | 23 | func (c *ChromeOS) Version() string { 24 | return c.p.Version(chromeOSVersionRegexp, 1, "") 25 | } 26 | 27 | func (c *ChromeOS) Match() bool { 28 | return c.p.Match(chromeOSMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /platforms/chrome_os_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewChromeOS(t *testing.T) { 10 | Convey("Subject: #NewChromeOS", t, func() { 11 | Convey("It should return a new ChromeOS instance", func() { 12 | So(NewChromeOS(NewUAParser("")), ShouldHaveSameTypeAs, &ChromeOS{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestChromeOSName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Chrome OS", func() { 20 | So(NewChromeOS(NewUAParser("")).Name(), ShouldEqual, "Chrome OS") 21 | }) 22 | }) 23 | } 24 | 25 | func TestChromeOSVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | Convey("It should return the correct version", func() { 29 | p := testPlatforms 30 | So(NewChromeOS(NewUAParser(p["chrome-os-armv7l"])).Version(), ShouldEqual, "7077.134.0") 31 | So(NewChromeOS(NewUAParser(p["chrome-os-armv7i"])).Version(), ShouldEqual, "6158.49.0") 32 | So(NewChromeOS(NewUAParser(p["chrome-os-armv8l"])).Version(), ShouldEqual, "8872.54.0") 33 | So(NewChromeOS(NewUAParser(p["chrome-os-x86-64"])).Version(), ShouldEqual, "7647.84.0") 34 | So(NewChromeOS(NewUAParser(p["chrome-os-i686"])).Version(), ShouldEqual, "6310.68.0") 35 | So(NewChromeOS(NewUAParser(p["chrome-os-aarch64"])).Version(), ShouldEqual, "9202.64.0") 36 | }) 37 | }) 38 | }) 39 | } 40 | 41 | func TestChromeOSMatch(t *testing.T) { 42 | Convey("Subject: #Match", t, func() { 43 | Convey("When user agent matches Chrome OS", func() { 44 | Convey("It should return true", func() { 45 | p := testPlatforms 46 | So(NewChromeOS(NewUAParser(p["chrome-os-armv7l"])).Match(), ShouldBeTrue) 47 | So(NewChromeOS(NewUAParser(p["chrome-os-armv7i"])).Match(), ShouldBeTrue) 48 | So(NewChromeOS(NewUAParser(p["chrome-os-armv8l"])).Match(), ShouldBeTrue) 49 | So(NewChromeOS(NewUAParser(p["chrome-os-x86-64"])).Match(), ShouldBeTrue) 50 | So(NewChromeOS(NewUAParser(p["chrome-os-i686"])).Match(), ShouldBeTrue) 51 | So(NewChromeOS(NewUAParser(p["chrome-os-aarch64"])).Match(), ShouldBeTrue) 52 | }) 53 | }) 54 | 55 | Convey("When user agent does not match", func() { 56 | Convey("It should return false", func() { 57 | So(NewChromeOS(NewUAParser(testPlatforms["android-10"])).Match(), ShouldBeFalse) 58 | }) 59 | }) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /platforms/firefox_os.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type FirefoxOS struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | firefoxOSName = "Firefox OS" 9 | firefoxOSMatchRegexp = []string{`Firefox`} 10 | firefoxDeviceExcludeRegexp = []string{`Android|Linux|BlackBerry|Windows|Mac`} 11 | ) 12 | 13 | func NewFirefoxOS(p Parser) *FirefoxOS { 14 | return &FirefoxOS{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (f *FirefoxOS) Name() string { 20 | return firefoxOSName 21 | } 22 | 23 | func (f *FirefoxOS) Version() string { 24 | return "0" 25 | } 26 | 27 | func (f *FirefoxOS) Match() bool { 28 | return !f.isExcludeDevice() && f.p.Match(firefoxOSMatchRegexp) 29 | } 30 | 31 | func (f *FirefoxOS) isExcludeDevice() bool { 32 | return f.p.Match(firefoxDeviceExcludeRegexp) 33 | } 34 | -------------------------------------------------------------------------------- /platforms/firefox_os_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewFirefoxOS(t *testing.T) { 10 | Convey("Subject: #NewFirefoxOS", t, func() { 11 | Convey("It should return a new FirefoxOS instance", func() { 12 | So(NewFirefoxOS(NewUAParser("")), ShouldHaveSameTypeAs, &FirefoxOS{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestFirefoxOSName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Firefox OS", func() { 20 | So(NewFirefoxOS(NewUAParser("")).Name(), ShouldEqual, "Firefox OS") 21 | }) 22 | }) 23 | } 24 | 25 | func TestFirefoxOSVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | p := testPlatforms 29 | So(NewFirefoxOS(NewUAParser(p["firefox"])).Version(), ShouldEqual, "0") 30 | }) 31 | }) 32 | } 33 | 34 | func TestFirefoxOSMatch(t *testing.T) { 35 | Convey("Subject: #Match", t, func() { 36 | Convey("When user agent matches Firefox OS", func() { 37 | Convey("It should return true", func() { 38 | p := testPlatforms 39 | So(NewFirefoxOS(NewUAParser(p["firefox"])).Match(), ShouldBeTrue) 40 | }) 41 | }) 42 | 43 | Convey("When user agent does not match", func() { 44 | Convey("It should return false", func() { 45 | So(NewFirefoxOS(NewUAParser(testPlatforms["android-10"])).Match(), ShouldBeFalse) 46 | }) 47 | }) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /platforms/ios.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | type IOS struct { 10 | p Parser 11 | } 12 | 13 | var ( 14 | iOSName = "iOS" 15 | iOSVersionRegexp = `OS (\d+)_(\d+)_?(\d+)?` 16 | iOSMatchRegexp = []string{`(iPhone|iPad|iPod)`} 17 | ) 18 | 19 | func NewIOS(p Parser) *IOS { 20 | return &IOS{ 21 | p: p, 22 | } 23 | } 24 | 25 | func (i *IOS) Name() string { 26 | return fmt.Sprintf("%s (%s)", iOSName, i.device()) 27 | } 28 | 29 | func (i *IOS) device() string { 30 | re := regexp.MustCompile(iOSMatchRegexp[0]) 31 | matches := re.FindStringSubmatch(i.p.String()) 32 | if len(matches) > 1 { 33 | return matches[1] 34 | } 35 | 36 | return "" 37 | } 38 | 39 | func (i *IOS) Version() string { 40 | re := regexp.MustCompile(iOSVersionRegexp) 41 | matches := re.FindStringSubmatch(i.p.String()) 42 | if len(matches) == 0 { 43 | return "0" 44 | } 45 | 46 | versions := []string{matches[1]} 47 | if matches[3] != "" { 48 | versions = append(versions, matches[2], matches[3]) 49 | } else if matches[2] != "0" { 50 | versions = append(versions, matches[2]) 51 | } 52 | 53 | return strings.Join(versions, ".") 54 | } 55 | 56 | func (i *IOS) Match() bool { 57 | return i.p.Match(iOSMatchRegexp) 58 | } 59 | -------------------------------------------------------------------------------- /platforms/ios_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewIOS(t *testing.T) { 10 | Convey("Subject: #NewIOS", t, func() { 11 | Convey("It should return a new IOS instance", func() { 12 | So(NewIOS(NewUAParser("")), ShouldHaveSameTypeAs, &IOS{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestIOSName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return iOS", func() { 20 | So(NewIOS(NewUAParser(testPlatforms["ios"])).Name(), ShouldEqual, "iOS (iPhone)") 21 | So(NewIOS(NewUAParser(testPlatforms["ios-3"])).Name(), ShouldEqual, "iOS (iPhone)") 22 | So(NewIOS(NewUAParser(testPlatforms["ios-4"])).Name(), ShouldEqual, "iOS (iPhone)") 23 | So(NewIOS(NewUAParser(testPlatforms["ios-6"])).Name(), ShouldEqual, "iOS (iPad)") 24 | So(NewIOS(NewUAParser(testPlatforms["ios-10"])).Name(), ShouldEqual, "iOS (iPhone)") 25 | So(NewIOS(NewUAParser(testPlatforms["ios-11"])).Name(), ShouldEqual, "iOS (iPhone)") 26 | }) 27 | }) 28 | } 29 | 30 | func TestIOSVersion(t *testing.T) { 31 | Convey("Subject: #Version", t, func() { 32 | Convey("When the version is matched", func() { 33 | p := testPlatforms 34 | Convey("It should return the version", func() { 35 | So(NewIOS(NewUAParser(p["ios"])).Version(), ShouldEqual, "0.1") 36 | So(NewIOS(NewUAParser(p["ios-3"])).Version(), ShouldEqual, "3") 37 | So(NewIOS(NewUAParser(p["ios-4"])).Version(), ShouldEqual, "4.0.2") 38 | So(NewIOS(NewUAParser(p["ios-5"])).Version(), ShouldEqual, "5.1.1") 39 | So(NewIOS(NewUAParser(p["ios-6"])).Version(), ShouldEqual, "6.0.1") 40 | So(NewIOS(NewUAParser(p["ios-7"])).Version(), ShouldEqual, "7.8.1") 41 | So(NewIOS(NewUAParser(p["ios-8"])).Version(), ShouldEqual, "8.7.2") 42 | So(NewIOS(NewUAParser(p["ios-12"])).Version(), ShouldEqual, "12.1") 43 | So(NewIOS(NewUAParser(p["ios-13"])).Version(), ShouldEqual, "13.1.1") 44 | So(NewIOS(NewUAParser(p["ios-14"])).Version(), ShouldEqual, "14.7") 45 | So(NewIOS(NewUAParser(p["ios-15"])).Version(), ShouldEqual, "15") 46 | So(NewIOS(NewUAParser(p["ios-16"])).Version(), ShouldEqual, "16") 47 | So(NewIOS(NewUAParser(p["ios-17"])).Version(), ShouldEqual, "17.6") 48 | }) 49 | }) 50 | 51 | Convey("When the version is not matched", func() { 52 | Convey("It should return default version", func() { 53 | So(NewIOS(NewUAParser(testPlatforms["firefo)x"])).Version(), ShouldEqual, "0") 54 | }) 55 | }) 56 | }) 57 | } 58 | 59 | func TestIOSMatch(t *testing.T) { 60 | Convey("Subject: #Match", t, func() { 61 | Convey("When user agent matches iOS", func() { 62 | Convey("It should return true", func() { 63 | p := testPlatforms 64 | So(NewIOS(NewUAParser(p["ios"])).Match(), ShouldBeTrue) 65 | So(NewIOS(NewUAParser(p["ios-3"])).Match(), ShouldBeTrue) 66 | So(NewIOS(NewUAParser(p["ios-4"])).Match(), ShouldBeTrue) 67 | So(NewIOS(NewUAParser(p["ios-5"])).Match(), ShouldBeTrue) 68 | So(NewIOS(NewUAParser(p["ios-6"])).Match(), ShouldBeTrue) 69 | So(NewIOS(NewUAParser(p["ios-7"])).Match(), ShouldBeTrue) 70 | So(NewIOS(NewUAParser(p["ios-8"])).Match(), ShouldBeTrue) 71 | So(NewIOS(NewUAParser(p["ios-12"])).Match(), ShouldBeTrue) 72 | So(NewIOS(NewUAParser(p["ios-13"])).Match(), ShouldBeTrue) 73 | So(NewIOS(NewUAParser(p["ios-14"])).Match(), ShouldBeTrue) 74 | So(NewIOS(NewUAParser(p["ios-15"])).Match(), ShouldBeTrue) 75 | So(NewIOS(NewUAParser(p["ios-16"])).Match(), ShouldBeTrue) 76 | So(NewIOS(NewUAParser(p["ios-17"])).Match(), ShouldBeTrue) 77 | }) 78 | }) 79 | 80 | Convey("When user agent does not match iOS", func() { 81 | Convey("It should return false", func() { 82 | So(NewIOS(NewUAParser(testPlatforms["firefox"])).Match(), ShouldBeFalse) 83 | }) 84 | }) 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /platforms/kai.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type KaiOS struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | kaiOSName = "Kai OS" 9 | kaiOSVersionRegexp = []string{`KaiOS/([\d.]+)`} 10 | kaiOSMatchRegexp = []string{`KaiOS`} 11 | ) 12 | 13 | func NewKaiOS(p Parser) *KaiOS { 14 | return &KaiOS{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (k *KaiOS) Name() string { 20 | return kaiOSName 21 | } 22 | 23 | func (k *KaiOS) Version() string { 24 | return k.p.Version(kaiOSVersionRegexp, 1, "") 25 | } 26 | 27 | func (k *KaiOS) Match() bool { 28 | return k.p.Match(kaiOSMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /platforms/kai_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewKaiOS(t *testing.T) { 10 | Convey("Subject: #NewKaiOS", t, func() { 11 | Convey("It should return a new KaiOS instance", func() { 12 | So(NewKaiOS(NewUAParser("")), ShouldHaveSameTypeAs, &KaiOS{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestKaiOSName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return KaiOS", func() { 20 | So(NewKaiOS(NewUAParser("")).Name(), ShouldEqual, "Kai OS") 21 | }) 22 | }) 23 | } 24 | 25 | func TestKaiOSVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | Convey("It should return the version", func() { 29 | So(NewKaiOS(NewUAParser(testPlatforms["kai-os-1"])).Version(), ShouldEqual, "1.0") 30 | So(NewKaiOS(NewUAParser(testPlatforms["kai-os-2"])).Version(), ShouldEqual, "2.0") 31 | }) 32 | }) 33 | 34 | Convey("When the version is not matched", func() { 35 | Convey("It should return default version", func() { 36 | So(NewKaiOS(NewUAParser(testPlatforms["firefox"])).Version(), ShouldEqual, "") 37 | }) 38 | }) 39 | }) 40 | } 41 | 42 | func TestKaiOSMatch(t *testing.T) { 43 | Convey("Subject: #Match", t, func() { 44 | Convey("When user agent matches KaiOS", func() { 45 | Convey("It should return true", func() { 46 | So(NewKaiOS(NewUAParser(testPlatforms["kai-os-1"])).Match(), ShouldBeTrue) 47 | So(NewKaiOS(NewUAParser(testPlatforms["kai-os-2"])).Match(), ShouldBeTrue) 48 | }) 49 | }) 50 | 51 | Convey("When user agent does not match KaiOS", func() { 52 | Convey("It should return false", func() { 53 | So(NewKaiOS(NewUAParser(testPlatforms["firefox"])).Match(), ShouldBeFalse) 54 | }) 55 | }) 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /platforms/linux.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type Linux struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | linuxName = "Generic Linux" 9 | linuxMatchRegexp = []string{`Linux`} 10 | ) 11 | 12 | func NewLinux(p Parser) *Linux { 13 | return &Linux{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (l *Linux) Name() string { 19 | return linuxName 20 | } 21 | 22 | func (l *Linux) Version() string { 23 | return "0" 24 | } 25 | 26 | func (l *Linux) Match() bool { 27 | return l.p.Match(linuxMatchRegexp) 28 | } 29 | -------------------------------------------------------------------------------- /platforms/linux_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewLinux(t *testing.T) { 10 | Convey("Subject: #NewLinux", t, func() { 11 | Convey("It should return a new Linux instance", func() { 12 | So(NewLinux(NewUAParser("")), ShouldHaveSameTypeAs, &Linux{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestLinuxName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Linux", func() { 20 | So(NewLinux(NewUAParser("")).Name(), ShouldEqual, "Generic Linux") 21 | }) 22 | }) 23 | } 24 | 25 | func TestLinuxVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | Convey("It should return the version", func() { 29 | So(NewLinux(NewUAParser(testPlatforms["linux"])).Version(), ShouldEqual, "0") 30 | }) 31 | }) 32 | }) 33 | } 34 | 35 | func TestLinuxMatch(t *testing.T) { 36 | Convey("Subject: #Match", t, func() { 37 | Convey("When user agent matches Linux", func() { 38 | Convey("It should return true", func() { 39 | So(NewLinux(NewUAParser(testPlatforms["linux"])).Match(), ShouldBeTrue) 40 | }) 41 | }) 42 | 43 | Convey("When user agent does not match Linux", func() { 44 | Convey("It should return false", func() { 45 | So(NewLinux(NewUAParser(testPlatforms["firefox"])).Match(), ShouldBeFalse) 46 | }) 47 | }) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /platforms/mac.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/dineshgowda24/browser/utils" 7 | ) 8 | 9 | type Mac struct { 10 | p Parser 11 | } 12 | 13 | var ( 14 | macName = "macOS" 15 | macXName = "Mac OS X" 16 | macVersionRegexp = []string{`(?:Mac|MAC) OS X\s*([0-9_.]+)?`} 17 | macMatchRegexp = []string{`M(ac|AC)|macOS`} 18 | ) 19 | 20 | func NewMac(p Parser) *Mac { 21 | return &Mac{ 22 | p: p, 23 | } 24 | } 25 | 26 | func (m *Mac) Name() string { 27 | if utils.VersionGTE(m.Version(), "10.12") { 28 | return macName 29 | } 30 | return macXName 31 | } 32 | 33 | func (m *Mac) Version() string { 34 | version := m.p.Version(macVersionRegexp, 1, "0") 35 | return strings.Replace(version, "_", ".", -1) 36 | } 37 | 38 | func (m *Mac) Match() bool { 39 | return m.p.Match(macMatchRegexp) 40 | } 41 | -------------------------------------------------------------------------------- /platforms/mac_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewMac(t *testing.T) { 10 | Convey("Subject: #NewMac", t, func() { 11 | Convey("It should return a new Mac instance", func() { 12 | So(NewMac(NewUAParser("")), ShouldHaveSameTypeAs, &Mac{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestMacName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return macOS", func() { 20 | So(NewMac(NewUAParser(testPlatforms["mac-os"])).Name(), ShouldEqual, "Mac OS X") 21 | So(NewMac(NewUAParser(testPlatforms["mac-os-x"])).Name(), ShouldEqual, "Mac OS X") 22 | So(NewMac(NewUAParser(testPlatforms["mac-os-1"])).Name(), ShouldEqual, "macOS") 23 | }) 24 | }) 25 | } 26 | 27 | func TestMacVersion(t *testing.T) { 28 | Convey("Subject: #Version", t, func() { 29 | Convey("When the version is matched", func() { 30 | Convey("It should return the version", func() { 31 | So(NewMac(NewUAParser(testPlatforms["mac-os-x"])).Version(), ShouldEqual, "10.11") 32 | So(NewMac(NewUAParser(testPlatforms["mac-os-1"])).Version(), ShouldEqual, "10.14.6") 33 | }) 34 | }) 35 | 36 | Convey("When the version is not matched", func() { 37 | Convey("It should return default version", func() { 38 | So(NewMac(NewUAParser(testPlatforms["mac-os"])).Version(), ShouldEqual, "0") 39 | }) 40 | }) 41 | }) 42 | } 43 | 44 | func TestMacMatch(t *testing.T) { 45 | Convey("Subject: #Match", t, func() { 46 | Convey("When user agent matches Mac", func() { 47 | Convey("It should return true", func() { 48 | So(NewMac(NewUAParser(testPlatforms["mac-os"])).Match(), ShouldBeTrue) 49 | So(NewMac(NewUAParser(testPlatforms["mac-os-x"])).Match(), ShouldBeTrue) 50 | So(NewMac(NewUAParser(testPlatforms["mac-os-1"])).Match(), ShouldBeTrue) 51 | }) 52 | }) 53 | 54 | Convey("When user agent does not match Mac", func() { 55 | Convey("It should return false", func() { 56 | So(NewMac(NewUAParser(testPlatforms["firefox"])).Match(), ShouldBeFalse) 57 | }) 58 | }) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /platforms/parser.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import "regexp" 4 | 5 | type Parser interface { 6 | Version([]string, int, string) string 7 | Match([]string) bool 8 | String() string 9 | } 10 | 11 | type UAParser struct { 12 | userAgent string 13 | } 14 | 15 | // NewUAParser returns a new UAParser. 16 | func NewUAParser(userAgent string) *UAParser { 17 | return &UAParser{ 18 | userAgent: userAgent, 19 | } 20 | } 21 | 22 | func (b UAParser) String() string { 23 | return b.userAgent 24 | } 25 | 26 | // Version returns the version of the platform. 27 | // The pattern is a list of regular expressions. 28 | // The version is extracted from the user agent string using the given patterns. 29 | // The order parameter specifies which match to return. 30 | func (b UAParser) Version(patterns []string, order int, defaultVersion string) string { 31 | for _, pattern := range patterns { 32 | re := regexp.MustCompile(pattern) 33 | matches := re.FindStringSubmatch(b.userAgent) 34 | if len(matches) > order { 35 | return matches[order] 36 | } 37 | } 38 | return defaultVersion 39 | } 40 | 41 | // Match returns true if the user agent matches the pattern. 42 | // The pattern is a list of regular expressions. 43 | func (b UAParser) Match(patterns []string) bool { 44 | for _, pattern := range patterns { 45 | re := regexp.MustCompile(pattern) 46 | if re.MatchString(b.userAgent) { 47 | return true 48 | } 49 | } 50 | return false 51 | } 52 | -------------------------------------------------------------------------------- /platforms/parser_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestParserMatching(t *testing.T) { 10 | Convey("Subject: #Match", t, func() { 11 | userAgent := "Mozilla/5.0 (Linux; Android 4.4.2; SM-G900F Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Mobile Safari/537.36" 12 | Convey("When platform is matched", func() { 13 | Convey("It should return true", func() { 14 | parser := NewUAParser(userAgent) 15 | So(parser.Match([]string{"Android"}), ShouldBeTrue) 16 | }) 17 | }) 18 | 19 | Convey("When platform is not matched", func() { 20 | Convey("It should return false", func() { 21 | parser := NewUAParser(userAgent) 22 | So(parser.Match([]string{"Windows"}), ShouldBeFalse) 23 | }) 24 | }) 25 | }) 26 | } 27 | 28 | func TestParserVersion(t *testing.T) { 29 | Convey("Subject: #version", t, func() { 30 | androidUserAgent := "Mozilla/5.0 (Linux; Android 4.4.2; SM-G900F Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Mobile Safari/537.36" 31 | Convey("When version is matched", func() { 32 | Convey("When order is 1", func() { 33 | Convey("It should return version", func() { 34 | parser := NewUAParser(androidUserAgent) 35 | So(parser.Version([]string{`Android ([\d.]+)`}, 1, ""), ShouldEqual, "4.4.2") 36 | }) 37 | }) 38 | 39 | Convey("When order is 2", func() { 40 | snapUserAgent := "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Mobile/15E148 Snapchat/12.33.0.36 (like Safari/8615.1.26.100.1, panda)" 41 | Convey("It should return version", func() { 42 | parser := NewUAParser(snapUserAgent) 43 | So(parser.Version([]string{`Snapchat( ?|/)([\d.]+)`}, 2, ""), ShouldEqual, "12.33.0.36") 44 | }) 45 | }) 46 | }) 47 | 48 | Convey("When version is not matched", func() { 49 | Convey("It should return default version", func() { 50 | parser := NewUAParser(androidUserAgent) 51 | So(parser.Version([]string{`Android ([\d.]+)`}, 2, "0"), ShouldEqual, "0") 52 | }) 53 | }) 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /platforms/platforms_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "strings" 8 | "testing" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // testPlatforms is a map of user agent strings for each platform from the platforms.yml file 14 | var testPlatforms map[string]string 15 | 16 | // initTestPlatforms reads the platforms.yml file and 17 | // unmarshals it into the testPlatforms map. 18 | func initTestPlatforms() { 19 | wd, err := os.Getwd() 20 | if err != nil { 21 | log.Fatalf("failed to get working directory: %v", err) 22 | } 23 | 24 | wd = strings.Split(wd, "/browser")[0] 25 | yamlFile, err := os.ReadFile(fmt.Sprintf("%s/browser/assets/test/platforms.yml", wd)) 26 | if err != nil { 27 | log.Fatalf("failed to read file: %v", err) 28 | } 29 | 30 | var data map[string]string 31 | if err := yaml.Unmarshal(yamlFile, &data); err != nil { 32 | log.Fatalf("failed to unmarshal: %v", err) 33 | } 34 | 35 | testPlatforms = data 36 | } 37 | 38 | // TestMain is the entry point for running tests in this package. 39 | func TestMain(m *testing.M) { 40 | initTestPlatforms() 41 | exitCode := m.Run() 42 | os.Exit(exitCode) 43 | } 44 | -------------------------------------------------------------------------------- /platforms/unknown.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | // Unknown is the platform information when the user agent is not recognized. 4 | // It is used as a fallback when the user agent is not recognized. 5 | // Its last in the list of platform matchers. 6 | type Unknown struct { 7 | p Parser 8 | } 9 | 10 | var unknownName = "Unknown" 11 | 12 | func NewUnknown(p Parser) *Unknown { 13 | return &Unknown{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (u *Unknown) Name() string { 19 | return unknownName 20 | } 21 | 22 | func (u *Unknown) Version() string { 23 | return "0" 24 | } 25 | 26 | func (u *Unknown) Match() bool { 27 | return true 28 | } 29 | -------------------------------------------------------------------------------- /platforms/unknown_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewUnknown(t *testing.T) { 10 | Convey("Subject: #NewUnknown", t, func() { 11 | Convey("It should return a new Unknown instance", func() { 12 | So(NewUnknown(NewUAParser("")), ShouldHaveSameTypeAs, &Unknown{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestUnknownName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Unknown", func() { 20 | u := NewUnknown(NewUAParser("")) 21 | So(u.Name(), ShouldEqual, "Unknown") 22 | }) 23 | }) 24 | } 25 | 26 | func TestUnknownVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("It should return 0", func() { 29 | So(NewUnknown(NewUAParser("")).Version(), ShouldEqual, "0") 30 | }) 31 | }) 32 | } 33 | 34 | func TestUnknownMatch(t *testing.T) { 35 | Convey("Subject: #Match", t, func() { 36 | Convey("It should return true", func() { 37 | So(NewUnknown(NewUAParser("")).Match(), ShouldBeTrue) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /platforms/watch_os.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type WatchOS struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | watchOSName = "Apple Watch OS" 9 | watchOSVersionRegexp = []string{`(?i)Watch\s*OS[ ,/]([\d.]+)`, `Watch[^/]+/([\d.]+)`} 10 | watchOSMatchRegexp = []string{`(?i)Watch\s*OS`, `Watch[\d+]`} 11 | ) 12 | 13 | func NewWatchOS(p Parser) *WatchOS { 14 | return &WatchOS{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (w *WatchOS) Name() string { 20 | return watchOSName 21 | } 22 | 23 | func (w *WatchOS) Version() string { 24 | return w.p.Version(watchOSVersionRegexp, 1, "0.0") 25 | } 26 | 27 | func (w *WatchOS) Match() bool { 28 | return w.p.Match(watchOSMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /platforms/watch_os_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewWatchOS(t *testing.T) { 10 | Convey("Subject: #NewWatchOS", t, func() { 11 | Convey("It should return a new WatchOS instance", func() { 12 | So(NewWatchOS(NewUAParser("")), ShouldHaveSameTypeAs, &WatchOS{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestWatchOSName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return the correct name", func() { 20 | So(NewWatchOS(NewUAParser(testPlatforms["watch-3"])).Name(), ShouldEqual, "Apple Watch OS") 21 | }) 22 | }) 23 | } 24 | 25 | func TestWatchOSVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | Convey("It should return the correct version", func() { 29 | So(NewWatchOS(NewUAParser(testPlatforms["apple-watch-3"])).Version(), ShouldEqual, "5.2.1") 30 | So(NewWatchOS(NewUAParser(testPlatforms["apple-watch-4"])).Version(), ShouldEqual, "5.3") 31 | So(NewWatchOS(NewUAParser(testPlatforms["apple-watch-5"])).Version(), ShouldEqual, "6.0.1") 32 | So(NewWatchOS(NewUAParser(testPlatforms["apple-watch-6"])).Version(), ShouldEqual, "7.0") 33 | }) 34 | }) 35 | 36 | Convey("When the version is not matched", func() { 37 | Convey("It should return 0.0", func() { 38 | So(NewWatchOS(NewUAParser(testPlatforms["mac-os"])).Version(), ShouldEqual, "0.0") 39 | }) 40 | }) 41 | }) 42 | } 43 | 44 | func TestWatchOSMatch(t *testing.T) { 45 | Convey("Subject: #Match", t, func() { 46 | Convey("When user agent matches WatchOS", func() { 47 | Convey("It should return true", func() { 48 | So(NewWatchOS(NewUAParser(testPlatforms["apple-watch-3"])).Match(), ShouldBeTrue) 49 | So(NewWatchOS(NewUAParser(testPlatforms["apple-watch-4"])).Match(), ShouldBeTrue) 50 | So(NewWatchOS(NewUAParser(testPlatforms["apple-watch-5"])).Match(), ShouldBeTrue) 51 | So(NewWatchOS(NewUAParser(testPlatforms["apple-watch-6"])).Match(), ShouldBeTrue) 52 | }) 53 | }) 54 | 55 | Convey("When user agent does not match WatchOS", func() { 56 | Convey("It should return false", func() { 57 | So(NewWatchOS(NewUAParser(testPlatforms["mac-os"])).Match(), ShouldBeFalse) 58 | }) 59 | }) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /platforms/windows.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type Windows struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | windowsName = "Windows" 9 | windowsVersionRegexp = []string{`Windows NT\s*([0-9_.]+)?`} 10 | windowsMatchRegexp = []string{`Windows`} 11 | ) 12 | 13 | func NewWindows(p Parser) *Windows { 14 | return &Windows{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (w *Windows) Name() string { 20 | return windowsName 21 | } 22 | 23 | func (w *Windows) Version() string { 24 | return w.p.Version(windowsVersionRegexp, 1, "0") 25 | } 26 | 27 | func (w *Windows) Match() bool { 28 | return w.p.Match(windowsMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /platforms/windows_mobile.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type WindowsMobile struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | windowsMobileName = "Windows Mobile" 9 | windowsMobileMatchRegexp = []string{`Windows CE`} 10 | ) 11 | 12 | func NewWindowsMobile(p Parser) *WindowsMobile { 13 | return &WindowsMobile{ 14 | p: p, 15 | } 16 | } 17 | 18 | func (w *WindowsMobile) Name() string { 19 | return windowsMobileName 20 | } 21 | 22 | func (w *WindowsMobile) Version() string { 23 | return "0" 24 | } 25 | 26 | func (w *WindowsMobile) Match() bool { 27 | return w.p.Match(windowsMobileMatchRegexp) 28 | } 29 | -------------------------------------------------------------------------------- /platforms/windows_mobile_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewWindowsMobile(t *testing.T) { 10 | Convey("Subject: #NewWindowsMobile", t, func() { 11 | Convey("It should return a new WindowsMobile instance", func() { 12 | So(NewWindowsMobile(NewUAParser("")), ShouldHaveSameTypeAs, &WindowsMobile{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestWindowsMobileName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Windows Mobile", func() { 20 | wm := NewWindowsMobile(NewUAParser("")) 21 | So(wm.Name(), ShouldEqual, "Windows Mobile") 22 | }) 23 | }) 24 | } 25 | 26 | func TestWindowsMobileVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("It should return 0", func() { 29 | So(NewWindowsMobile(NewUAParser(testPlatforms["windows-ce"])).Version(), ShouldEqual, "0") 30 | }) 31 | }) 32 | } 33 | 34 | func TestWindowsMobileMatch(t *testing.T) { 35 | Convey("Subject: #Match", t, func() { 36 | Convey("When user agent matches Windows Mobile", func() { 37 | Convey("It should return true", func() { 38 | So(NewWindowsMobile(NewUAParser(testPlatforms["windows-ce"])).Match(), ShouldBeTrue) 39 | }) 40 | }) 41 | 42 | Convey("When user agent does not match Windows Mobile", func() { 43 | Convey("It should return false", func() { 44 | So(NewWindowsMobile(NewUAParser(testPlatforms["windows"])).Match(), ShouldBeFalse) 45 | }) 46 | }) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /platforms/windows_phone.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | type WindowsPhone struct { 4 | p Parser 5 | } 6 | 7 | var ( 8 | windowsPhoneName = "Windows Phone" 9 | windowsPhoneVersionRegexp = []string{`Windows Phone ([\d.]+)`} 10 | windowsPhoneMatchRegexp = []string{`Windows Phone`} 11 | ) 12 | 13 | func NewWindowsPhone(p Parser) *WindowsPhone { 14 | return &WindowsPhone{ 15 | p: p, 16 | } 17 | } 18 | 19 | func (w *WindowsPhone) Name() string { 20 | return windowsPhoneName 21 | } 22 | 23 | func (w *WindowsPhone) Version() string { 24 | return w.p.Version(windowsPhoneVersionRegexp, 1, "") 25 | } 26 | 27 | func (w *WindowsPhone) Match() bool { 28 | return w.p.Match(windowsPhoneMatchRegexp) 29 | } 30 | -------------------------------------------------------------------------------- /platforms/windows_phone_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewWindowsPhone(t *testing.T) { 10 | Convey("Subject: #NewWindowsPhone", t, func() { 11 | Convey("It should return a new WindowsPhone instance", func() { 12 | So(NewWindowsPhone(NewUAParser("")), ShouldHaveSameTypeAs, &WindowsPhone{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestWindowsPhoneName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Windows Phone", func() { 20 | So(NewWindowsPhone(NewUAParser("")).Name(), ShouldEqual, "Windows Phone") 21 | }) 22 | }) 23 | } 24 | 25 | func TestWindowsPhoneVersion(t *testing.T) { 26 | Convey("Subject: #Version", t, func() { 27 | Convey("When the version is matched", func() { 28 | Convey("It should return the version", func() { 29 | wp := NewWindowsPhone(NewUAParser(testPlatforms["windows-phone"])) 30 | So(wp.Version(), ShouldEqual, "10.0") 31 | }) 32 | }) 33 | 34 | Convey("When the version is not matched", func() { 35 | Convey("It should return default version", func() { 36 | wp := NewWindowsPhone(NewUAParser(testPlatforms["windows-ce"])) 37 | So(wp.Version(), ShouldEqual, "") 38 | }) 39 | }) 40 | }) 41 | } 42 | 43 | func TestWindowsPhoneMatch(t *testing.T) { 44 | Convey("Subject: #Match", t, func() { 45 | Convey("When user agent matches Windows Phone", func() { 46 | Convey("It should return true", func() { 47 | So(NewWindowsPhone(NewUAParser(testPlatforms["windows-phone"])).Match(), ShouldBeTrue) 48 | }) 49 | }) 50 | 51 | Convey("When user agent does not match Windows Phone", func() { 52 | Convey("It should return false", func() { 53 | So(NewWindowsPhone(NewUAParser(testPlatforms["windows-ce"])).Match(), ShouldBeFalse) 54 | }) 55 | }) 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /platforms/windows_test.go: -------------------------------------------------------------------------------- 1 | package platforms 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestNewWindows(t *testing.T) { 10 | Convey("Subject: #NewWindows", t, func() { 11 | Convey("It should return a new Windows instance", func() { 12 | So(NewWindows(NewUAParser(testPlatforms["windows-xp"])), ShouldHaveSameTypeAs, &Windows{}) 13 | }) 14 | }) 15 | } 16 | 17 | func TestWindowsName(t *testing.T) { 18 | Convey("Subject: #Name", t, func() { 19 | Convey("It should return Windows", func() { 20 | w := NewWindows(NewUAParser(testPlatforms["windows-xp"])) 21 | So(w.Name(), ShouldEqual, windowsName) 22 | }) 23 | }) 24 | } 25 | 26 | func TestWindowsVersion(t *testing.T) { 27 | Convey("Subject: #Version", t, func() { 28 | Convey("When the version is matched", func() { 29 | Convey("It should return the version", func() { 30 | So(NewWindows(NewUAParser(testPlatforms["windows-xp"])).Version(), ShouldEqual, "5.1") 31 | So(NewWindows(NewUAParser(testPlatforms["windows-vista"])).Version(), ShouldEqual, "6.0") 32 | So(NewWindows(NewUAParser(testPlatforms["windows-7"])).Version(), ShouldEqual, "6.1") 33 | So(NewWindows(NewUAParser(testPlatforms["windows-8"])).Version(), ShouldEqual, "6.2") 34 | So(NewWindows(NewUAParser(testPlatforms["windows-8-1"])).Version(), ShouldEqual, "6.3") 35 | So(NewWindows(NewUAParser(testPlatforms["windows-10"])).Version(), ShouldEqual, "10.0") 36 | }) 37 | }) 38 | 39 | Convey("When the version is not matched", func() { 40 | Convey("It should return default version", func() { 41 | So(NewWindows(NewUAParser(testPlatforms["linux"])).Version(), ShouldEqual, "0") 42 | }) 43 | }) 44 | }) 45 | } 46 | 47 | func TestWindowsMatch(t *testing.T) { 48 | Convey("Subject: #Match", t, func() { 49 | Convey("When user agent matches Windows", func() { 50 | Convey("It should return true", func() { 51 | So(NewWindows(NewUAParser(testPlatforms["windows-xp"])).Match(), ShouldBeTrue) 52 | So(NewWindows(NewUAParser(testPlatforms["windows-vista"])).Match(), ShouldBeTrue) 53 | So(NewWindows(NewUAParser(testPlatforms["windows-7"])).Match(), ShouldBeTrue) 54 | So(NewWindows(NewUAParser(testPlatforms["windows-8"])).Match(), ShouldBeTrue) 55 | So(NewWindows(NewUAParser(testPlatforms["windows-8-1"])).Match(), ShouldBeTrue) 56 | So(NewWindows(NewUAParser(testPlatforms["windows-10"])).Match(), ShouldBeTrue) 57 | }) 58 | }) 59 | 60 | Convey("When user agent does not match Windows", func() { 61 | Convey("It should return false", func() { 62 | So(NewWindows(NewUAParser(testPlatforms["linux"])).Match(), ShouldBeFalse) 63 | }) 64 | }) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /utils/version.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/blang/semver/v4" 5 | ) 6 | 7 | // VersionGTE returns true if the a is greater than or equal to the e. 8 | func VersionGTE(a, e string) bool { 9 | if e == "" { 10 | return true 11 | } 12 | 13 | if a == "" { 14 | return false 15 | } 16 | 17 | ev, err := semver.ParseTolerant(e) 18 | if err != nil { 19 | return false 20 | } 21 | 22 | av, err := semver.ParseTolerant(a) 23 | if err != nil { 24 | return false 25 | } 26 | 27 | return av.GTE(ev) 28 | } 29 | 30 | // VersionGT returns true if the a is greater than the e. 31 | func VersionGT(a, e string) bool { 32 | if e == "" { 33 | return true 34 | } 35 | 36 | if a == "" { 37 | return false 38 | } 39 | 40 | ev, err := semver.ParseTolerant(e) 41 | if err != nil { 42 | return false 43 | } 44 | 45 | av, err := semver.ParseTolerant(a) 46 | if err != nil { 47 | return false 48 | } 49 | 50 | return av.GT(ev) 51 | } 52 | 53 | // VersionLTE returns true if the a is less than or equal to the e. 54 | func VersionLTE(a, e string) bool { 55 | if a == "" { 56 | return true 57 | } 58 | 59 | if e == "" { 60 | return false 61 | } 62 | 63 | ev, err := semver.ParseTolerant(e) 64 | if err != nil { 65 | return false 66 | } 67 | 68 | av, err := semver.ParseTolerant(a) 69 | if err != nil { 70 | return false 71 | } 72 | 73 | return av.LTE(ev) 74 | } 75 | 76 | // VersionLT returns true if the a is less than the e. 77 | func VersionLT(a, e string) bool { 78 | if a == "" { 79 | return true 80 | } 81 | 82 | if e == "" { 83 | return false 84 | } 85 | 86 | ev, err := semver.ParseTolerant(e) 87 | if err != nil { 88 | return false 89 | } 90 | 91 | av, err := semver.ParseTolerant(a) 92 | if err != nil { 93 | return false 94 | } 95 | 96 | return av.LT(ev) 97 | } 98 | --------------------------------------------------------------------------------