Add automatic handling of "ElementNotInteractable" exceptions

In the WebDriver protocol, when a command fails because it can not
interact with the target element, an "element not interactable" error is
generated. It can be a transitive issue (for example, due to an
animation), so when the error is received the command should be tried
again, just like done, for example, with "ElementNotVisible" exceptions.

However, the last version of the "instaclick/php-webdriver" library
compatible with the Selenium Driver of Mink did not support yet that
WebDriver error. And even if Chrome is run using the old protocol an
unknown "element not interactable" error can be received anyway in some
cases. When an unknown error is received by the
"instaclick/php-webdriver" library it is thrown as a generic Exception
so, until the library can be updated, the message of generic exceptions
is checked and the command is retried if it matched.

For the time being "element not interactable" errors are handled like
"ElementNotVisible" exceptions; this may need to change once the error
is better understood.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez 2021-04-18 22:21:38 +02:00
parent 9e1246eba5
commit 4b376a107b
1 changed files with 24 additions and 1 deletions

View File

@ -66,6 +66,11 @@
* exception instead of an ElementNotVisible exception, so those cases are
* handled like ElementNotVisible exceptions.
*
* ElementNotInteractable exceptions are thrown in Selenium 3 when the command
* needs to interact with an element but that is not possible. This could be a
* transitive situation (for example, due to an animation), so the command is
* executed again after a small timeout.
*
* Despite the automatic handling it is possible for the commands to throw those
* exceptions when they are executed again; this class does not handle cases
* like an element becoming stale several times in a row (uncommon) or an
@ -73,7 +78,10 @@
* that the timeout is too short or that the test has to, indeed, fail). In a
* similar way, MoveTargetOutOfBounds exceptions would be thrown again if
* originally they were thrown because the element was visible but "out of
* reach".
* reach". ElementNotInteractable exceptions would be thrown again if it is not
* possible to interact yet with the element after the wait (which could mean
* that the test has to, indeed, fail, although it could mean too that the
* automatic handling needs to be improved).
*
* If needed, automatically handling failed commands can be disabled calling
* "doNotHandleFailedCommands()"; as it returns the ElementWrapper it can be
@ -279,6 +287,13 @@ class ElementWrapper {
* If an ElementNotVisible or a MoveTargetOutOfBounds exception is thrown it
* is waited for the wrapped element to be visible and, then, the command is
* executed again.
* If an ElementNotInteractable exception is thrown it is also waited for
* the wrapped element to be visible. It is very likely that the element was
* visible already, but it is not possible to easily check if the element
* can be interacted with, retrying will be only useful if it was a
* transitive situation that resolves itself with a wait (for example, due
* to an animation) and waiting for the element to be visible will always
* start with a wait.
*
* @param \Closure $commandCallback the command to execute.
* @param string $errorMessage an error message that describes the failed
@ -295,6 +310,14 @@ class ElementWrapper {
$this->printFailedCommandMessage($exception, $errorMessage);
} catch (\WebDriver\Exception\MoveTargetOutOfBounds $exception) {
$this->printFailedCommandMessage($exception, $errorMessage);
} catch (\Exception $exception) {
// The "ElementNotInteractable" exception is not available yet in
// the current "instaclick/php-webdriver" version, so it is thrown
// as a generic exception with a specific message.
if (stripos($exception->getMessage(), "element not interactable") === false) {
throw $exception;
}
$this->printFailedCommandMessage($exception, $errorMessage);
}
$this->waitForElementToBeVisible();