Add retry to newAction-based locator APIs - #5657
Conversation
|
@ankur22
If you have an idea for a non-flaky test, that would also be appreciated! |
|
@janHildebrandt98 thanks for the PR!
If you are referring to the following: // Only locator based APIs should retry.
if !opts.retry {
return res, err
}Then yes, i think we need the same. The code from locator and non-locator based APIs use the same
No strong feeling here, happy to leave it in, unless the other reviewer spots something.
Good question, i'm not sure. What does Playwright do?
Yeah, this is a good observation. If you're interested in working out a better way then please go ahead, but in a new PR. I'm not sure why this is setup in this way, there could be a good reason for having multiple actionability checks, I wasn't around for when that was initially implemented and there's no clue in the git history. |
| if strings.Contains(err.Error(), "element is not attached to the DOM") { | ||
| return ErrElementNotAttachedToDOM | ||
| } |
There was a problem hiding this comment.
What's the reason for this change?
There was a problem hiding this comment.
I misunderstood how errors come out of eval(),,,
Thanks for pointing it out.
Removed.
|
Hi @ankur22, I conducted some research into playwrights codebase and I found that matching it exactly will be difficult. The retry logic from this PR mainly affects focus() and methods calling it—retrying when the target is detached, hoping it gets re-attached. Playwright handles this differently: it re-runs the entire selector, potentially resolving to a new element. It cleanly separates locator retries (does the element exist?) from actionability retries, with locator retries wrapping actionability retries wrapping the actual logic. Matching this in our codebase is difficult because selector execution happens before newAction(), so retrying the selector would require some restructuring. What we can do with minimal impact to the code:
SInce I'll be gone for the next week, you can expect the remaining changes to this PR on the following week. |
507768a to
f039b47
Compare
|
Hi @ankur22, |
| var res any | ||
| var err error | ||
|
|
||
| if retry && !force { |
There was a problem hiding this comment.
Playwright won't retry actionability when force = true.
It is not entirely comparable to our implementation because of what I tried to explain in my earlier comment in this PR but i thought doing it this way is as close as we can get right now.
|
Hi @ankur22, |
f7474fb to
4f00eeb
Compare
inancgumus
left a comment
There was a problem hiding this comment.
LGTM, @janHildebrandt98, except for two minor issues.
Thanks for your contribution 🙇
|
|
||
| func (h *ElementHandle) newAction( | ||
| states []string, fn elementHandleActionFunc, force, noWaitAfter bool, timeout time.Duration, | ||
| states []string, fn elementHandleActionFunc, force bool, retry bool, noWaitAfter bool, timeout time.Duration, |
There was a problem hiding this comment.
Successive primitive types make it easier to introduce bugs (i.e., force bool, retry bool, noWaitAfter bool). They also make it harder for the caller to understand. Could you add and use Retry, NoRetry (these names are suggestions, and you can pick whatever name you want) constants?
There was a problem hiding this comment.
Great idea. Will adjust it soon.
There was a problem hiding this comment.
@inancgumus I added the constants. Please have another look.
da1b127 to
71dff07
Compare
inancgumus
left a comment
There was a problem hiding this comment.
Good stuff. Thanks! LGTM.
|
Hi @ankur22, |
|
@ankur22 |
|
@ankur22 |
|
Just wanted to say I really enjoyed collaborating with you on this one. |
* Add retry func and test * Refactor retry methods to reuse shared logic * Cleanup * more cleanup * make linter happy * Fixx error masking * remove unnecessary error check * Dont retry for BoundingBox(), add test * Return errElementNotVisible and NotAttachedToDom from ErrFromDOMError * Return correct error * Introduce constants for retry switch
What?
Wrapping newAction() on ElementHandle with a retry function, similar to what was done in #5111.
Changed frame.BoundingBox() to call public handle.BoundingBox() because it neatly translates the errors in retryable errors.
Why?
Parallel actions on a target element may lead to it being hidden/detached after initial actioanbility checks were performed but before/during our call to the injected script. This can lead to transient errors.
This PR introduces a retry mechanic to succeed in case of transient errors because of the retry and else fail with a timeout.
Checklist
make check) and all pass.Checklist: Documentation (only for k6 maintainers and if relevant)
Please do not merge this PR until the following items are filled out.
Related PR(s)/Issue(s)
closes #5174