Merge pull request 'fix: default viewport and engine-aligned User-Agent' (#73) from fix/70-default-viewport-ua-alignment into main
All checks were successful
CI / vet (push) Successful in 1m12s
CI / build (push) Successful in 1m12s
CI / test (push) Successful in 1m13s

Reviewed-on: #73
This commit was merged in pull request #73.
This commit is contained in:
2026-02-24 01:29:37 +00:00
4 changed files with 94 additions and 11 deletions

View File

@@ -52,6 +52,16 @@ func initBrowser(opt BrowserOptions) (*browserInitResult, error) {
return nil, ErrInvalidBrowserSelection return nil, ErrInvalidBrowserSelection
} }
// Auto-select a User-Agent matching the browser engine when the caller hasn't set one.
if opt.UserAgent == "" {
switch opt.Browser {
case BrowserChromium:
opt.UserAgent = DefaultChromiumUserAgent
default:
opt.UserAgent = DefaultFirefoxUserAgent
}
}
// Collect launch args and init scripts, starting with any stealth-mode presets. // Collect launch args and init scripts, starting with any stealth-mode presets.
stealth := opt.Stealth == nil || *opt.Stealth stealth := opt.Stealth == nil || *opt.Stealth
var launchArgs []string var launchArgs []string

View File

@@ -59,10 +59,9 @@ type interactiveBrowser struct {
func NewInteractiveBrowser(ctx context.Context, opts ...BrowserOptions) (InteractiveBrowser, error) { func NewInteractiveBrowser(ctx context.Context, opts ...BrowserOptions) (InteractiveBrowser, error) {
var thirtySeconds = 30 * time.Second var thirtySeconds = 30 * time.Second
opt := mergeOptions(BrowserOptions{ opt := mergeOptions(BrowserOptions{
UserAgent: DefaultUserAgent, Browser: BrowserFirefox,
Browser: BrowserFirefox, Timeout: &thirtySeconds,
Timeout: &thirtySeconds, Stealth: Bool(true),
Stealth: Bool(true),
Dimensions: Size{ Dimensions: Size{
Width: 1280, Width: 1280,
Height: 720, Height: 720,

View File

@@ -36,8 +36,14 @@ const (
BrowserWebKit BrowserSelection = "webkit" BrowserWebKit BrowserSelection = "webkit"
) )
// DefaultUserAgent is the user-agent string used by all browser instances. // DefaultFirefoxUserAgent is the user-agent string used for Firefox browser instances.
const DefaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0" const DefaultFirefoxUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0"
// DefaultChromiumUserAgent is the user-agent string used for Chromium browser instances.
const DefaultChromiumUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
// DefaultUserAgent is an alias for DefaultFirefoxUserAgent, retained for backward compatibility.
const DefaultUserAgent = DefaultFirefoxUserAgent
// Bool returns a pointer to the given bool value. // Bool returns a pointer to the given bool value.
func Bool(v bool) *bool { return &v } func Bool(v bool) *bool { return &v }
@@ -47,7 +53,7 @@ type Size struct {
Height int Height int
} }
type BrowserOptions struct { type BrowserOptions struct {
UserAgent string // If empty, defaults to DefaultUserAgent UserAgent string // If empty, auto-selected based on Browser engine
Browser BrowserSelection // If unset defaults to Firefox. Browser BrowserSelection // If unset defaults to Firefox.
Timeout *time.Duration // If unset defaults to 30 seconds timeout. If set to 0, no timeout Timeout *time.Duration // If unset defaults to 30 seconds timeout. If set to 0, no timeout
@@ -145,10 +151,13 @@ func playwrightCookieToCookie(cookie playwright.Cookie) Cookie {
func NewBrowser(ctx context.Context, opts ...BrowserOptions) (Browser, error) { func NewBrowser(ctx context.Context, opts ...BrowserOptions) (Browser, error) {
var thirtySeconds = 30 * time.Second var thirtySeconds = 30 * time.Second
opt := mergeOptions(BrowserOptions{ opt := mergeOptions(BrowserOptions{
UserAgent: DefaultUserAgent, Browser: BrowserFirefox,
Browser: BrowserFirefox, Timeout: &thirtySeconds,
Timeout: &thirtySeconds, Stealth: Bool(true),
Stealth: Bool(true), Dimensions: Size{
Width: 1920,
Height: 1080,
},
}, opts) }, opts)
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {

View File

@@ -374,3 +374,68 @@ func TestStealthFirefoxScripts_NoChromiumMarkers(t *testing.T) {
} }
} }
} }
// --- User-Agent constants ---
func TestDefaultUserAgent_BackwardCompat(t *testing.T) {
if DefaultUserAgent != DefaultFirefoxUserAgent {
t.Fatal("DefaultUserAgent must equal DefaultFirefoxUserAgent for backward compatibility")
}
}
func TestDefaultFirefoxUserAgent_Content(t *testing.T) {
if !strings.Contains(DefaultFirefoxUserAgent, "Firefox") {
t.Fatal("DefaultFirefoxUserAgent must contain 'Firefox'")
}
if strings.Contains(DefaultFirefoxUserAgent, "Chrome") {
t.Fatal("DefaultFirefoxUserAgent must not contain 'Chrome'")
}
}
func TestDefaultChromiumUserAgent_Content(t *testing.T) {
if !strings.Contains(DefaultChromiumUserAgent, "Chrome") {
t.Fatal("DefaultChromiumUserAgent must contain 'Chrome'")
}
if strings.Contains(DefaultChromiumUserAgent, "Firefox") {
t.Fatal("DefaultChromiumUserAgent must not contain 'Firefox'")
}
}
// --- Viewport and UA defaults via mergeOptions ---
func TestMergeOptions_DefaultViewport(t *testing.T) {
base := BrowserOptions{
Dimensions: Size{Width: 1920, Height: 1080},
}
got := mergeOptions(base, nil)
if got.Dimensions.Width != 1920 || got.Dimensions.Height != 1080 {
t.Fatalf("expected default viewport 1920x1080, got %dx%d", got.Dimensions.Width, got.Dimensions.Height)
}
}
func TestMergeOptions_ViewportOverride(t *testing.T) {
base := BrowserOptions{
Dimensions: Size{Width: 1920, Height: 1080},
}
got := mergeOptions(base, []BrowserOptions{{Dimensions: Size{Width: 1280, Height: 720}}})
if got.Dimensions.Width != 1280 || got.Dimensions.Height != 720 {
t.Fatalf("expected overridden viewport 1280x720, got %dx%d", got.Dimensions.Width, got.Dimensions.Height)
}
}
func TestMergeOptions_EmptyUANotOverridden(t *testing.T) {
base := BrowserOptions{}
got := mergeOptions(base, []BrowserOptions{{Browser: BrowserChromium}})
if got.UserAgent != "" {
t.Fatalf("expected empty UserAgent after merge with no explicit UA, got %q", got.UserAgent)
}
}
func TestMergeOptions_ExplicitUAPreserved(t *testing.T) {
base := BrowserOptions{}
customUA := "MyCustomAgent/1.0"
got := mergeOptions(base, []BrowserOptions{{UserAgent: customUA}})
if got.UserAgent != customUA {
t.Fatalf("expected explicit UA %q preserved, got %q", customUA, got.UserAgent)
}
}