Files

162 lines
6.9 KiB
Go

package mailclient
import "testing"
func TestDetectOpens(t *testing.T) {
cases := []struct {
name string
ua string
want Reading
}{
{
name: "gmail proxy hides the device and the network",
ua: "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)",
want: Reading{Client: "Gmail", DeviceHidden: true, Locate: LocateNone},
},
{
name: "yahoo proxy",
ua: "YahooMailProxy; https://help.yahoo.com/kb/yahoo-mail-proxy-SLN28749.html",
want: Reading{Client: "Yahoo Mail", DeviceHidden: true, Locate: LocateNone},
},
{
name: "apple mail privacy protection is the bare product token",
ua: "Mozilla/5.0",
want: Reading{Client: "Apple Mail", DeviceHidden: true, Locate: LocateRegion},
},
{
name: "hey's proxy wears chrome on linux",
ua: "hey.com/imageproxy Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
want: Reading{Client: "HEY", DeviceHidden: true, Locate: LocateNone},
},
{
name: "fastmail's web proxy",
ua: "FastmailUA/1.0",
want: Reading{Client: "Fastmail", DeviceHidden: true, Locate: LocateNone},
},
{
// Issue #564: the new Outlook for Windows sent this from the
// reader's own network, so it is a direct fetch on an unknown device.
name: "bare webkit is mac mail or the new outlook, on no device it can name",
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko)",
want: Reading{Client: BareWebKitClient, ClientType: App},
},
{
name: "outlook for ios",
ua: "Outlook-iOS/2.0",
want: Reading{Client: "Outlook", ClientType: App, DeviceType: "mobile", OS: "iOS"},
},
{
name: "outlook for ios keeps an ipad a tablet",
ua: "Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Outlook-iOS/2.0",
want: Reading{Client: "Outlook", ClientType: App, DeviceType: "tablet", OS: "iOS"},
},
{
name: "mail on an iphone",
ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
want: Reading{Client: "Apple Mail", ClientType: App, DeviceType: "mobile", OS: "iOS"},
},
{
name: "mail on an ipad",
ua: "Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
want: Reading{Client: "Apple Mail", ClientType: App, DeviceType: "tablet", OS: "iOS"},
},
{
name: "classic outlook for windows",
ua: "Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.17928; Pro)",
want: Reading{Client: "Outlook", ClientType: App, DeviceType: "desktop", OS: "Windows"},
},
{
name: "outlook's word renderer names no platform but is only on windows",
ua: "Mozilla/4.0 (compatible; ms-office; MSOffice 16)",
want: Reading{Client: "Outlook", ClientType: App, DeviceType: "desktop", OS: "Windows"},
},
{
name: "thunderbird",
ua: "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Thunderbird/128.2.0",
want: Reading{Client: "Thunderbird", ClientType: App, DeviceType: "desktop", OS: "Linux"},
},
{
name: "an android mail app's webview",
ua: "Mozilla/5.0 (Linux; Android 14; SM-S911B Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.127 Mobile Safari/537.36",
want: Reading{ClientType: App, DeviceType: "mobile", OS: "Android"},
},
{
name: "webmail in chrome on windows",
ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
want: Reading{ClientType: Webmail, DeviceType: "desktop", OS: "Windows", Browser: "Chrome", BrowserVersion: "128.0.0.0"},
},
{
name: "webmail in safari on a mac is not the bare signature",
ua: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
want: Reading{ClientType: Webmail, DeviceType: "desktop", OS: "macOS", Browser: "Safari", BrowserVersion: "17.5"},
},
{
name: "any other outlook build still names outlook",
ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) OutlookDesktop/1.2024",
want: Reading{Client: "Outlook", ClientType: App, DeviceType: "desktop", OS: "Windows"},
},
{
name: "empty says nothing",
ua: " ",
want: Reading{},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := Detect(tc.ua, false); got != tc.want {
t.Fatalf("Detect(%q)\n got %+v\n want %+v", tc.ua, got, tc.want)
}
})
}
}
func TestDetectClicks(t *testing.T) {
// A link opened from Mail lands in Safari: the click is Safari's, not
// webmail and not Apple Mail.
safari := "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1"
got := Detect(safari, true)
want := Reading{DeviceType: "mobile", OS: "iOS", Browser: "Safari", BrowserVersion: "17.5"}
if got != want {
t.Fatalf("safari click\n got %+v\n want %+v", got, want)
}
// A client that names itself on a link made the request (Outlook checks
// a link before handing it to the browser), so the click is still its.
outlook := "Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.17928; Pro)"
if got := Detect(outlook, true); got.Client != "Outlook" || got.ClientType != App || got.DeviceType != "desktop" {
t.Fatalf("outlook click = %+v", got)
}
// A browser click names the browser and never webmail.
chrome := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
if got := Detect(chrome, true); got.Client != "" || got.ClientType != "" || got.Browser != "Chrome" {
t.Fatalf("browser click = %+v", got)
}
// A proxy only loads images: its string on a link, including Apple's bare
// product token from any HTTP library, claims nothing.
for _, ua := range []string{"Mozilla/5.0", "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)"} {
if got := Detect(ua, true); got != (Reading{}) {
t.Fatalf("proxy string on a click %q = %+v", ua, got)
}
}
// The stripped signature on a link names no browser the parser could
// read honestly (it takes the platform for one), so nothing is claimed.
if got := Detect("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko)", true); got != (Reading{}) {
t.Fatalf("bare webkit click = %+v", got)
}
}
func TestIsBareWebKit(t *testing.T) {
if !IsBareWebKit(" Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) ") {
t.Fatal("the stripped signature is bare webkit")
}
if IsBareWebKit("Mozilla/5.0 (KHTML, like Gecko)") {
t.Fatal("the compatibility suffix without an engine is not")
}
if IsBareWebKit("Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148") {
t.Fatal("iOS Mail's webview carries a build token after the engine")
}
}