From 38b88422a807c9ef0b57879aa189e3329c1258d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Sat, 6 Mar 2021 14:45:25 +0100 Subject: [PATCH 1/4] Do not send "enter" key when not needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sending the "enter" key is not needed in those input fields that auto save while the user is typing or when the focus is lost (which since version 1.4.0 the Selenium driver for Mink is automatically done after setting the value). Signed-off-by: Daniel Calviño Sánchez --- tests/acceptance/features/bootstrap/ContactsMenuContext.php | 2 +- tests/acceptance/features/bootstrap/SearchContext.php | 2 +- tests/acceptance/features/bootstrap/ThemingAppContext.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/acceptance/features/bootstrap/ContactsMenuContext.php b/tests/acceptance/features/bootstrap/ContactsMenuContext.php index 3578606215..ae4eab89ec 100644 --- a/tests/acceptance/features/bootstrap/ContactsMenuContext.php +++ b/tests/acceptance/features/bootstrap/ContactsMenuContext.php @@ -82,7 +82,7 @@ class ContactsMenuContext implements Context, ActorAwareInterface { * @When I search for the user :user */ public function iSearchForTheUser($user) { - $this->actor->find(self::contactsMenuSearchInput(), 10)->setValue($user . "\r"); + $this->actor->find(self::contactsMenuSearchInput(), 10)->setValue($user); } /** diff --git a/tests/acceptance/features/bootstrap/SearchContext.php b/tests/acceptance/features/bootstrap/SearchContext.php index ba0d9d9933..db7aeb90ff 100644 --- a/tests/acceptance/features/bootstrap/SearchContext.php +++ b/tests/acceptance/features/bootstrap/SearchContext.php @@ -86,7 +86,7 @@ class SearchContext implements Context, ActorAwareInterface { * @When I search for :query */ public function iSearchFor($query) { - $this->actor->find(self::searchBoxInput(), 10)->setValue($query . "\r"); + $this->actor->find(self::searchBoxInput(), 10)->setValue($query); } /** diff --git a/tests/acceptance/features/bootstrap/ThemingAppContext.php b/tests/acceptance/features/bootstrap/ThemingAppContext.php index 70fb2b01e1..d17d9c1810 100644 --- a/tests/acceptance/features/bootstrap/ThemingAppContext.php +++ b/tests/acceptance/features/bootstrap/ThemingAppContext.php @@ -65,7 +65,7 @@ class ThemingAppContext implements Context, ActorAwareInterface { * @When I set the :parameterName parameter in the Theming app to :parameterValue */ public function iSetTheParameterInTheThemingAppTo($parameterName, $parameterValue) { - $this->actor->find(self::inputFieldFor($parameterName), 10)->setValue($parameterValue . "\r"); + $this->actor->find(self::inputFieldFor($parameterName), 10)->setValue($parameterValue); } /** From 14b85f4eec959417db8e8ab3f765edaf18ba10d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Sat, 6 Mar 2021 14:59:36 +0100 Subject: [PATCH 2/4] Replace carriage return with WebDriver "ENTER" constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the value is set in some input fields a carriage return was sent to simulate pressing the enter key and thus confirming the input. However, different browsers use different keys (Firefox uses "\r", but Chrome uses "\n"), so the carriage return was replaced with the WebDriver "ENTER" constant which is common to both browsers. Signed-off-by: Daniel Calviño Sánchez --- tests/acceptance/features/bootstrap/FilesAppSharingContext.php | 3 ++- tests/acceptance/features/bootstrap/UsersSettingsContext.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/features/bootstrap/FilesAppSharingContext.php b/tests/acceptance/features/bootstrap/FilesAppSharingContext.php index e8515fa112..e3b63476ff 100644 --- a/tests/acceptance/features/bootstrap/FilesAppSharingContext.php +++ b/tests/acceptance/features/bootstrap/FilesAppSharingContext.php @@ -23,6 +23,7 @@ use Behat\Behat\Context\Context; use PHPUnit\Framework\Assert; +use WebDriver\Key; class FilesAppSharingContext implements Context, ActorAwareInterface { use ActorAware; @@ -427,7 +428,7 @@ class FilesAppSharingContext implements Context, ActorAwareInterface { $shareLinkMenuTriggerElement = $this->actor->find(self::shareLinkMenuTrigger(), 2); $this->actor->find(self::passwordProtectCheckbox($shareLinkMenuTriggerElement), 2)->click(); - $this->actor->find(self::passwordProtectField($shareLinkMenuTriggerElement), 2)->setValue($password . "\r"); + $this->actor->find(self::passwordProtectField($shareLinkMenuTriggerElement), 2)->setValue($password . Key::ENTER); } /** diff --git a/tests/acceptance/features/bootstrap/UsersSettingsContext.php b/tests/acceptance/features/bootstrap/UsersSettingsContext.php index 02e1825e4c..d60261d5d1 100644 --- a/tests/acceptance/features/bootstrap/UsersSettingsContext.php +++ b/tests/acceptance/features/bootstrap/UsersSettingsContext.php @@ -25,6 +25,7 @@ use Behat\Behat\Context\Context; use PHPUnit\Framework\Assert; +use WebDriver\Key; class UsersSettingsContext implements Context, ActorAwareInterface { use ActorAware; @@ -242,7 +243,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface { * @When I set the :field for :user to :value */ public function iSetTheFieldForUserTo($field, $user, $value) { - $this->actor->find(self::inputForUserInCell($field, $user), 2)->setValue($value . "\r"); + $this->actor->find(self::inputForUserInCell($field, $user), 2)->setValue($value . Key::ENTER); } /** From 9e1246eba5980446bf300b6f4bf58eb3be829843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Sat, 6 Mar 2021 15:05:54 +0100 Subject: [PATCH 3/4] Update acceptance tests to Selenium 3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The acceptance tests used the last Selenium 2 Docker container available, which provides a rather old Firefox version (Firefox 47). Nevertheless, despite some rendering issues, most things still worked as expected due to the JavaScript files being built with support for older browsers. However, now that support for Internet Explorer 11 and older browsers will be dropped things could start to fail, so a newer browser (and thus a newer Selenium version) should be used in the acceptance tests. Selenium has been standardized by the W3C, and the protocol to communicate between the Selenium server and the browser has changed due to that. Firefox >= 48 only supports the new W3C protocol, but the Selenium driver for Mink does not support it yet. The old protocol can still be used in recent Chromium/Chrome versions by explicitly forcing it, so for the time being the acceptance tests will need to be run on Chrome instead (although Firefox provides some interesting features like the fake streams that would be needed to test calls in Talk, so they should be moved again to Firefox once possible). Finally, the default shm size of Docker is 64 MiB. This does not seem enough to run newer Chrome releases and causes the browser to randomly crash during the tests ("unknown error: session deleted because of page crash" is shown in the logs). Due to this "disable-dev-shm-usage" needs to be used so Chrome writes shared memory files into "/tmp" instead of "/dev/shm" (the default shm size of Docker could have been increased instead using "docker run --shm-size...", but that seems to be problematic when the container is run in current Drone releases). Signed-off-by: Daniel Calviño Sánchez --- .drone.yml | 22 +++++++++++----------- tests/acceptance/config/behat.yml | 24 ++++++++++++++++++++++++ tests/acceptance/run.sh | 4 ++-- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/.drone.yml b/.drone.yml index be09d9b388..d2059b766b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1709,7 +1709,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -1739,7 +1739,7 @@ trigger: # #services: #- name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium +# image: selenium/standalone-chrome:3.141.59 # environment: # # Reduce default log level for Selenium server (INFO) as it is too # # verbose. @@ -1769,7 +1769,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -1799,7 +1799,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -1829,7 +1829,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -1859,7 +1859,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -1889,7 +1889,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -1919,7 +1919,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -1949,7 +1949,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -1979,7 +1979,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. @@ -2009,7 +2009,7 @@ steps: services: - name: selenium - image: selenium/standalone-firefox:2.53.1-beryllium + image: selenium/standalone-chrome:3.141.59 environment: # Reduce default log level for Selenium server (INFO) as it is too # verbose. diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index a43bcb504a..eb5c7d61f8 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -65,15 +65,39 @@ default: default: selenium2: wd_host: %selenium.server% + browser: "chrome" + capabilities: + extra_capabilities: + goog:chromeOptions: + args: ["disable-dev-shm-usage"] + w3c: false John: selenium2: wd_host: %selenium.server% + browser: "chrome" + capabilities: + extra_capabilities: + goog:chromeOptions: + args: ["disable-dev-shm-usage"] + w3c: false Jane: selenium2: wd_host: %selenium.server% + browser: "chrome" + capabilities: + extra_capabilities: + goog:chromeOptions: + args: ["disable-dev-shm-usage"] + w3c: false Jim: selenium2: wd_host: %selenium.server% + browser: "chrome" + capabilities: + extra_capabilities: + goog:chromeOptions: + args: ["disable-dev-shm-usage"] + w3c: false Rubeus: # Rubeus uses a browser that has CSS grid support. selenium2: diff --git a/tests/acceptance/run.sh b/tests/acceptance/run.sh index 48e39bcd11..9a77d717a0 100755 --- a/tests/acceptance/run.sh +++ b/tests/acceptance/run.sh @@ -108,12 +108,12 @@ function prepareSelenium() { SELENIUM_CONTAINER=selenium-nextcloud-local-test-acceptance echo "Starting Selenium server" - docker run --detach --name=$SELENIUM_CONTAINER --publish 4444:4444 --publish 5900:5900 $DOCKER_OPTIONS selenium/standalone-firefox-debug:2.53.1-beryllium + docker run --detach --name=$SELENIUM_CONTAINER --publish 4444:4444 --publish 5900:5900 $DOCKER_OPTIONS selenium/standalone-chrome-debug:3.141.59 echo "Waiting for Selenium server to be ready" if ! $TIMEOUT 10s bash -c "while ! curl 127.0.0.1:4444 >/dev/null 2>&1; do sleep 1; done"; then echo "Could not start Selenium server; running" \ - "\"docker run --rm --publish 4444:4444 --publish 5900:5900 $DOCKER_OPTIONS selenium/standalone-firefox-debug:2.53.1-beryllium\"" \ + "\"docker run --rm --publish 4444:4444 --publish 5900:5900 $DOCKER_OPTIONS selenium/standalone-chrome-debug:3.141.59\"" \ "could give you a hint of the problem" exit 1 From 4b376a107ba4f30ca5d5db559f09685378bb8730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Sun, 18 Apr 2021 22:21:38 +0200 Subject: [PATCH 4/4] Add automatic handling of "ElementNotInteractable" exceptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../features/core/ElementWrapper.php | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/features/core/ElementWrapper.php b/tests/acceptance/features/core/ElementWrapper.php index d5f7ef495c..89a73d5aa7 100644 --- a/tests/acceptance/features/core/ElementWrapper.php +++ b/tests/acceptance/features/core/ElementWrapper.php @@ -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();