From 38efd4586c85c801aefe0245690767fedb335b22 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Mon, 11 Nov 2024 14:31:38 +0000 Subject: [PATCH 1/2] Refactor waitFor so that it returns a handle The fix requires the reuse of waitFor, but it also requires the handle value to be returned. This refactors waitFor to return Handle. --- common/frame.go | 10 +++++----- common/locator.go | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/frame.go b/common/frame.go index 51e44855f..4e3cbe950 100644 --- a/common/frame.go +++ b/common/frame.go @@ -564,12 +564,12 @@ func (f *Frame) waitForSelector(selector string, opts *FrameWaitForSelectorOptio return handle, nil } -func (f *Frame) waitFor(selector string, opts *FrameWaitForSelectorOptions, retryCount int) error { +func (f *Frame) waitFor(selector string, opts *FrameWaitForSelectorOptions, retryCount int) (_ *ElementHandle, rerr error) { f.log.Debugf("Frame:waitFor", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector) retryCount-- if retryCount < 0 { - return errors.New("waitFor retry threshold reached") + return nil, errors.New("waitFor retry threshold reached") } document, err := f.document() @@ -577,10 +577,10 @@ func (f *Frame) waitFor(selector string, opts *FrameWaitForSelectorOptions, retr if strings.Contains(err.Error(), "Cannot find context with specified id") { return f.waitFor(selector, opts, retryCount) } - return err + return nil, err } - _, err = document.waitForSelector(f.ctx, selector, opts) + handle, err := document.waitForSelector(f.ctx, selector, opts) if err != nil { if strings.Contains(err.Error(), "Inspected target navigated or closed") { return f.waitFor(selector, opts, retryCount) @@ -593,7 +593,7 @@ func (f *Frame) waitFor(selector string, opts *FrameWaitForSelectorOptions, retr } } - return err + return handle, err } // ChildFrames returns a list of child frames. diff --git a/common/locator.go b/common/locator.go index 0bc69cc3b..01e193b2c 100644 --- a/common/locator.go +++ b/common/locator.go @@ -621,7 +621,8 @@ func (l *Locator) WaitFor(opts sobek.Value) error { func (l *Locator) waitFor(opts *FrameWaitForSelectorOptions) error { opts.Strict = true - return l.frame.waitFor(l.selector, opts, 20) + _, err := l.frame.waitFor(l.selector, opts, 20) + return err } // DefaultTimeout returns the default timeout for the locator. From 982cab91cfa423a6b6a32fedcc57d84ffcb209c6 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Mon, 11 Nov 2024 14:32:42 +0000 Subject: [PATCH 2/2] Fix waitForSelector by calling waitFor The fix is in waitFor, and waitForSelector just needs to call waitFor to enable the fix. It basically will auto retry up to 20 times when certain errors are received from chrome. This can happens when the underlying DOM is changing due to a navigation, and the expected element that matches the selector is in the newly navigated DOM. --- common/frame.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/frame.go b/common/frame.go index 4e3cbe950..390f85e09 100644 --- a/common/frame.go +++ b/common/frame.go @@ -523,15 +523,16 @@ func (f *Frame) waitForSelectorRetry( return nil, err } +// waitForSelector will wait for the given selector to reach a defined state in +// opts. +// +// It will auto retry on certain errors until the retryCount is below 0. The +// retry workaround is needed since the underlying DOM can change when the +// wait action is performed during a navigation. func (f *Frame) waitForSelector(selector string, opts *FrameWaitForSelectorOptions) (_ *ElementHandle, rerr error) { f.log.Debugf("Frame:waitForSelector", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector) - document, err := f.document() - if err != nil { - return nil, err - } - - handle, err := document.waitForSelector(f.ctx, selector, opts) + handle, err := f.waitFor(selector, opts, 20) if err != nil { return nil, err }