refactor: restructure API, deduplicate code, expand test coverage
- Extract shared DeferClose helper, removing 14 duplicate copies - Rename PlayWright-prefixed types to cleaner names (BrowserOptions, BrowserSelection, NewBrowser, etc.) - Rename fields: ServerAddress, RequireServer (was DontLaunchOnConnectFailure) - Extract shared initBrowser/mergeOptions into browser_init.go, deduplicating ~120 lines between NewBrowser and NewInteractiveBrowser - Remove unused locator field from document struct - Add tests for all previously untested packages (archive, aislegopher, wegmans, useragents, powerball) and expand existing test suites - Add MIGRATION.md documenting all breaking API changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
136
interactive.go
136
interactive.go
@@ -56,48 +56,17 @@ type interactiveBrowser struct {
|
||||
|
||||
// NewInteractiveBrowser creates a headless browser with a page ready for interactive control.
|
||||
// The context is only used for cancellation during setup.
|
||||
func NewInteractiveBrowser(ctx context.Context, opts ...PlayWrightBrowserOptions) (InteractiveBrowser, error) {
|
||||
func NewInteractiveBrowser(ctx context.Context, opts ...BrowserOptions) (InteractiveBrowser, error) {
|
||||
var thirtySeconds = 30 * time.Second
|
||||
opt := PlayWrightBrowserOptions{
|
||||
opt := mergeOptions(BrowserOptions{
|
||||
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0",
|
||||
Browser: PlayWrightBrowserSelectionChromium,
|
||||
Browser: BrowserChromium,
|
||||
Timeout: &thirtySeconds,
|
||||
Dimensions: Size{
|
||||
Width: 1280,
|
||||
Height: 720,
|
||||
},
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
if o.UserAgent != "" {
|
||||
opt.UserAgent = o.UserAgent
|
||||
}
|
||||
if o.Browser != "" {
|
||||
opt.Browser = o.Browser
|
||||
}
|
||||
if o.Timeout != nil {
|
||||
opt.Timeout = o.Timeout
|
||||
}
|
||||
if o.CookieJar != nil {
|
||||
opt.CookieJar = o.CookieJar
|
||||
}
|
||||
if o.Dimensions.Width > 0 && o.Dimensions.Height > 0 {
|
||||
opt.Dimensions = o.Dimensions
|
||||
}
|
||||
if o.DarkMode {
|
||||
opt.DarkMode = true
|
||||
}
|
||||
if o.PlayWrightServerAddress != "" {
|
||||
opt.PlayWrightServerAddress = o.PlayWrightServerAddress
|
||||
}
|
||||
if o.DontLaunchOnConnectFailure {
|
||||
opt.DontLaunchOnConnectFailure = true
|
||||
}
|
||||
if o.UseLocalOnly {
|
||||
opt.UseLocalOnly = true
|
||||
}
|
||||
opt.ShowBrowser = o.ShowBrowser
|
||||
}
|
||||
}, opts)
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
@@ -111,98 +80,13 @@ func NewInteractiveBrowser(ctx context.Context, opts ...PlayWrightBrowserOptions
|
||||
ch := make(chan result, 1)
|
||||
|
||||
go func() {
|
||||
pw, err := playwright.Run()
|
||||
res, err := initBrowser(opt)
|
||||
if err != nil {
|
||||
err = playwright.Install()
|
||||
if err != nil {
|
||||
ch <- result{nil, fmt.Errorf("failed to install playwright: %w", err)}
|
||||
return
|
||||
}
|
||||
pw, err = playwright.Run()
|
||||
if err != nil {
|
||||
ch <- result{nil, fmt.Errorf("failed to start playwright: %w", err)}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var bt playwright.BrowserType
|
||||
switch opt.Browser {
|
||||
case PlayWrightBrowserSelectionChromium:
|
||||
bt = pw.Chromium
|
||||
case PlayWrightBrowserSelectionFirefox:
|
||||
bt = pw.Firefox
|
||||
case PlayWrightBrowserSelectionWebKit:
|
||||
bt = pw.WebKit
|
||||
default:
|
||||
ch <- result{nil, ErrInvalidBrowserSelection}
|
||||
ch <- result{nil, err}
|
||||
return
|
||||
}
|
||||
|
||||
var browser playwright.Browser
|
||||
var launch = true
|
||||
|
||||
if opt.PlayWrightServerAddress != "" && !opt.UseLocalOnly {
|
||||
launch = false
|
||||
var timeout float64 = 30000
|
||||
browser, err = bt.Connect(opt.PlayWrightServerAddress, playwright.BrowserTypeConnectOptions{Timeout: &timeout})
|
||||
if err != nil {
|
||||
if opt.DontLaunchOnConnectFailure {
|
||||
ch <- result{nil, err}
|
||||
return
|
||||
}
|
||||
launch = true
|
||||
}
|
||||
}
|
||||
|
||||
if launch {
|
||||
browser, err = bt.Launch(playwright.BrowserTypeLaunchOptions{
|
||||
Headless: playwright.Bool(!opt.ShowBrowser),
|
||||
})
|
||||
if err != nil {
|
||||
ch <- result{nil, fmt.Errorf("failed to launch browser: %w", err)}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
viewport := &playwright.Size{
|
||||
Width: opt.Dimensions.Width,
|
||||
Height: opt.Dimensions.Height,
|
||||
}
|
||||
|
||||
var scheme *playwright.ColorScheme
|
||||
if opt.DarkMode {
|
||||
scheme = playwright.ColorSchemeDark
|
||||
} else {
|
||||
scheme = playwright.ColorSchemeNoPreference
|
||||
}
|
||||
|
||||
bctx, err := browser.NewContext(playwright.BrowserNewContextOptions{
|
||||
UserAgent: playwright.String(opt.UserAgent),
|
||||
Viewport: viewport,
|
||||
ColorScheme: scheme,
|
||||
})
|
||||
if err != nil {
|
||||
ch <- result{nil, fmt.Errorf("failed to create browser context: %w", err)}
|
||||
return
|
||||
}
|
||||
|
||||
if opt.CookieJar != nil {
|
||||
cookies, err := opt.CookieJar.GetAll()
|
||||
if err != nil {
|
||||
ch <- result{nil, fmt.Errorf("error getting cookies from cookie jar: %w", err)}
|
||||
return
|
||||
}
|
||||
pwCookies := make([]playwright.OptionalCookie, len(cookies))
|
||||
for i, c := range cookies {
|
||||
pwCookies[i] = cookieToPlaywrightOptionalCookie(c)
|
||||
}
|
||||
if err := bctx.AddCookies(pwCookies); err != nil {
|
||||
ch <- result{nil, fmt.Errorf("error adding cookies: %w", err)}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
page, err := bctx.NewPage()
|
||||
page, err := res.bctx.NewPage()
|
||||
if err != nil {
|
||||
ch <- result{nil, fmt.Errorf("failed to create page: %w", err)}
|
||||
return
|
||||
@@ -210,9 +94,9 @@ func NewInteractiveBrowser(ctx context.Context, opts ...PlayWrightBrowserOptions
|
||||
|
||||
ch <- result{
|
||||
ib: &interactiveBrowser{
|
||||
pw: pw,
|
||||
browser: browser,
|
||||
ctx: bctx,
|
||||
pw: res.pw,
|
||||
browser: res.browser,
|
||||
ctx: res.bctx,
|
||||
page: page,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user