refactor: restructure API, deduplicate code, expand test coverage
Some checks failed
CI / build (push) Failing after 2m4s
CI / test (push) Failing after 2m6s
CI / vet (push) Failing after 2m19s

- 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:
2026-02-09 13:59:47 -05:00
parent e7b7e78796
commit cb2ed10cfd
32 changed files with 667 additions and 417 deletions

View File

@@ -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,
},
}