diff options
Diffstat (limited to 'tests/acceptance/features/core')
-rw-r--r-- | tests/acceptance/features/core/Actor.php | 214 | ||||
-rw-r--r-- | tests/acceptance/features/core/ActorAware.php | 36 | ||||
-rw-r--r-- | tests/acceptance/features/core/ActorAwareInterface.php | 29 | ||||
-rw-r--r-- | tests/acceptance/features/core/ActorContext.php | 194 | ||||
-rw-r--r-- | tests/acceptance/features/core/ElementFinder.php | 203 | ||||
-rw-r--r-- | tests/acceptance/features/core/ElementWrapper.php | 358 | ||||
-rw-r--r-- | tests/acceptance/features/core/Locator.php | 313 | ||||
-rw-r--r-- | tests/acceptance/features/core/NextcloudTestServerContext.php | 126 | ||||
-rw-r--r-- | tests/acceptance/features/core/NextcloudTestServerHelper.php | 71 | ||||
-rw-r--r-- | tests/acceptance/features/core/NextcloudTestServerLocalApacheHelper.php | 128 | ||||
-rw-r--r-- | tests/acceptance/features/core/NextcloudTestServerLocalBuiltInHelper.php | 142 | ||||
-rw-r--r-- | tests/acceptance/features/core/NoSuchElementException.php | 35 | ||||
-rw-r--r-- | tests/acceptance/features/core/Utils.php | 88 |
13 files changed, 0 insertions, 1937 deletions
diff --git a/tests/acceptance/features/core/Actor.php b/tests/acceptance/features/core/Actor.php deleted file mode 100644 index abe9a390920..00000000000 --- a/tests/acceptance/features/core/Actor.php +++ /dev/null @@ -1,214 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * An actor in a test scenario. - * - * Every Actor object is intended to be used only in a single test scenario. - * An Actor can control its web browser thanks to the Mink Session received when - * it was created, so in each scenario each Actor must have its own Mink - * Session; the same Mink Session can be used by different Actors in different - * scenarios, but never by different Actors in the same scenario. - * - * The test servers used in an scenario can change between different test runs, - * so an Actor stores the base URL for the current test server being used; in - * most cases the tests are specified using relative paths that can be converted - * to the appropriate absolute URL using locatePath() in the step - * implementation. - * - * An Actor can find elements in its Mink Session using its find() method; it is - * a wrapper over the find() method provided by Mink that extends it with - * several features: the element can be looked for based on a Locator object, an - * exception is thrown if the element is not found, and, optionally, it is - * possible to try again to find the element several times before giving up. - * - * The returned object is also a wrapper over the element itself that - * automatically handles common causes of failed commands, like clicking on a - * hidden element; in this case, the wrapper would wait for the element to be - * visible up to the timeout set to find the element. - * - * The amount of time to wait before giving up is specified in each call to - * find(). However, a general multiplier to be applied to every timeout can be - * set using setFindTimeoutMultiplier(); this makes possible to retry longer - * before giving up without modifying the tests themselves. Note that the - * multiplier affects the timeout, but not the timeout step; the rate at which - * find() will try again to find the element does not change. - * - * All actors share a notebook in which data can be annotated. This makes - * possible to share data between different test steps, no matter which Actor - * performs them. - */ -class Actor { - /** - * @var string - */ - private $name; - - /** - * @var \Behat\Mink\Session - */ - private $session; - - /** - * @var string - */ - private $baseUrl; - - /** - * @var float - */ - private $findTimeoutMultiplier; - - /** - * @var array - */ - private $sharedNotebook; - - /** - * Creates a new Actor. - * - * @param string $name the name of the actor. - * @param \Behat\Mink\Session $session the Mink Session used to control its - * web browser. - * @param string $baseUrl the base URL used when solving relative URLs. - * @param array $sharedNotebook the notebook shared between all actors. - */ - public function __construct($name, \Behat\Mink\Session $session, $baseUrl, &$sharedNotebook) { - $this->name = $name; - $this->session = $session; - $this->baseUrl = $baseUrl; - $this->sharedNotebook = &$sharedNotebook; - $this->findTimeoutMultiplier = 1; - } - - /** - * Returns the name of this Actor. - * - * @return string the name of this Actor. - */ - public function getName() { - return $this->name; - } - - /** - * Sets the base URL. - * - * @param string $baseUrl the base URL used when solving relative URLs. - */ - public function setBaseUrl($baseUrl) { - $this->baseUrl = $baseUrl; - } - - /** - * Returns the multiplier for find timeouts. - * - * @return float the multiplier to apply to find timeouts. - */ - public function getFindTimeoutMultiplier() { - return $this->findTimeoutMultiplier; - } - - /** - * Sets the multiplier for find timeouts. - * - * @param float $findTimeoutMultiplier the multiplier to apply to find - * timeouts. - */ - public function setFindTimeoutMultiplier($findTimeoutMultiplier) { - $this->findTimeoutMultiplier = $findTimeoutMultiplier; - } - - /** - * Returns the Mink Session used to control its web browser. - * - * @return \Behat\Mink\Session the Mink Session used to control its web - * browser. - */ - public function getSession() { - return $this->session; - } - - /** - * Returns the full path for the given relative path based on the base URL. - * - * @param string relativePath the relative path. - * @return string the full path. - */ - public function locatePath($relativePath) { - return $this->baseUrl . $relativePath; - } - - /** - * Finds an element in the Mink Session of this Actor. - * - * The given element locator is relative to its ancestor (either another - * locator or an actual element); if it has no ancestor then the base - * document element is used. - * - * Sometimes an element may not be found simply because it has not appeared - * yet; for those cases this method supports trying again to find the - * element several times before giving up. The timeout parameter controls - * how much time to wait, at most, to find the element; the timeoutStep - * parameter controls how much time to wait before trying again to find the - * element. If ancestor locators need to be found the timeout is applied - * individually to each one, that is, if the timeout is 10 seconds the - * method will wait up to 10 seconds to find the ancestor of the ancestor - * and, then, up to 10 seconds to find the ancestor and, then, up to 10 - * seconds to find the element. By default the timeout is 0, so the element - * and its ancestor will be looked for just once; the default time to wait - * before retrying is half a second. If the timeout is not 0 it will be - * affected by the multiplier set using setFindTimeoutMultiplier(), if any. - * - * When found, the element is returned wrapped in an ElementWrapper; the - * ElementWrapper handles common causes of failures when executing commands - * in an element, like clicking on a hidden element. - * - * In any case, if the element, or its ancestors, can not be found a - * NoSuchElementException is thrown. - * - * @param Locator $elementLocator the locator for the element. - * @param float $timeout the number of seconds (decimals allowed) to wait at - * most for the element to appear. - * @param float $timeoutStep the number of seconds (decimals allowed) to - * wait before trying to find the element again. - * @return ElementWrapper an ElementWrapper object for the element. - * @throws NoSuchElementException if the element, or its ancestor, can not - * be found. - */ - public function find(Locator $elementLocator, $timeout = 0, $timeoutStep = 0.5) { - $timeout = $timeout * $this->findTimeoutMultiplier; - - $elementFinder = new ElementFinder($this->session, $elementLocator, $timeout, $timeoutStep); - - return new ElementWrapper($elementFinder); - } - - /** - * Returns the shared notebook of the Actors. - * - * @return array the shared notebook of the Actors. - */ - public function &getSharedNotebook() { - return $this->sharedNotebook; - } -} diff --git a/tests/acceptance/features/core/ActorAware.php b/tests/acceptance/features/core/ActorAware.php deleted file mode 100644 index c734d7e1906..00000000000 --- a/tests/acceptance/features/core/ActorAware.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -trait ActorAware { - /** - * @var Actor - */ - protected $actor; - - /** - * @param Actor $actor - */ - public function setCurrentActor(Actor $actor) { - $this->actor = $actor; - } -} diff --git a/tests/acceptance/features/core/ActorAwareInterface.php b/tests/acceptance/features/core/ActorAwareInterface.php deleted file mode 100644 index 7b855aed4d3..00000000000 --- a/tests/acceptance/features/core/ActorAwareInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -interface ActorAwareInterface { - /** - * @param Actor $actor - */ - public function setCurrentActor(Actor $actor); -} diff --git a/tests/acceptance/features/core/ActorContext.php b/tests/acceptance/features/core/ActorContext.php deleted file mode 100644 index 7f152a1f3eb..00000000000 --- a/tests/acceptance/features/core/ActorContext.php +++ /dev/null @@ -1,194 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -use Behat\Behat\Hook\Scope\BeforeStepScope; -use Behat\MinkExtension\Context\RawMinkContext; - -/** - * Behat context to set the actor used in sibling contexts. - * - * This helper context provides a step definition ("I act as XXX") to change the - * current actor of the scenario, which makes possible to use different browser - * sessions in the same scenario. - * - * Sibling contexts that want to have access to the current actor of the - * scenario must implement the ActorAwareInterface; this can be done just by - * using the ActorAware trait. - * - * Besides updating the current actor in sibling contexts the ActorContext also - * propagates its inherited "base_url" Mink parameter to the Actors as needed. - * - * By default no multiplier for the find timeout is set in the Actors. However, - * it can be customized using the "actorTimeoutMultiplier" parameter of the - * ActorContext in "behat.yml". This parameter also affects the overall timeout - * to start a session for an Actor before giving up. - * - * Every actor used in the scenarios must have a corresponding Mink session - * declared in "behat.yml" with the same name as the actor. All used sessions - * are stopped after each scenario is run. - */ -class ActorContext extends RawMinkContext { - /** - * @var array - */ - private $actors; - - /** - * @var array - */ - private $sharedNotebook; - - /** - * @var Actor - */ - private $currentActor; - - /** - * @var float - */ - private $actorTimeoutMultiplier; - - /** - * Creates a new ActorContext. - * - * @param float $actorTimeoutMultiplier the timeout multiplier for Actor - * related timeouts. - */ - public function __construct($actorTimeoutMultiplier = 1) { - $this->actorTimeoutMultiplier = $actorTimeoutMultiplier; - } - - /** - * Sets a Mink parameter. - * - * When the "base_url" parameter is set its value is propagated to all the - * Actors. - * - * @param string $name the name of the parameter. - * @param string $value the value of the parameter. - */ - public function setMinkParameter($name, $value) { - parent::setMinkParameter($name, $value); - - if ($name === "base_url") { - foreach ($this->actors as $actor) { - $actor->setBaseUrl($value); - } - } - } - - /** - * Returns the session with the given name. - * - * If the session is not started it is started before returning it; if the - * session fails to start (typically due to a timeout connecting with the - * web browser) it will be tried again up to $actorTimeoutMultiplier times - * in total (rounded up to the next integer) before giving up. - * - * @param string|null $sname the name of the session to get, or null for the - * default session. - * @return \Behat\Mink\Session the session. - */ - public function getSession($name = null) { - for ($i = 0; $i < ($this->actorTimeoutMultiplier - 1); $i++) { - try { - return parent::getSession($name); - } catch (\Behat\Mink\Exception\DriverException $exception) { - echo "Exception when getting " . ($name == null? "default session": "session '$name'") . ": " . $exception->getMessage() . "\n"; - echo "Trying again\n"; - } - } - - return parent::getSession($name); - } - - /** - * @BeforeScenario - * - * Initializes the Actors for the new Scenario with the default Actor. - * - * Other Actors are added (and their Mink Sessions started) only when they - * are used in an "I act as XXX" step. - */ - public function initializeActors() { - $this->actors = []; - $this->sharedNotebook = []; - - $this->getSession()->start(); - - $this->getSession()->maximizeWindow(); - - $this->actors["default"] = new Actor("default", $this->getSession(), $this->getMinkParameter("base_url"), $this->sharedNotebook); - $this->actors["default"]->setFindTimeoutMultiplier($this->actorTimeoutMultiplier); - - $this->currentActor = $this->actors["default"]; - } - - /** - * @BeforeStep - */ - public function setCurrentActorInSiblingActorAwareContexts(BeforeStepScope $scope) { - $environment = $scope->getEnvironment(); - - foreach ($environment->getContexts() as $context) { - if ($context instanceof ActorAwareInterface) { - $context->setCurrentActor($this->currentActor); - } - } - } - - /** - * @Given I act as :actorName - */ - public function iActAs($actorName) { - if (!array_key_exists($actorName, $this->actors)) { - $this->getSession($actorName)->start(); - - $this->getSession($actorName)->maximizeWindow(); - - $this->actors[$actorName] = new Actor($actorName, $this->getSession($actorName), $this->getMinkParameter("base_url"), $this->sharedNotebook); - $this->actors[$actorName]->setFindTimeoutMultiplier($this->actorTimeoutMultiplier); - } - - $this->currentActor = $this->actors[$actorName]; - - // Ensure that the browser window of the actor is the one in the - // foreground; this works around a bug in the Firefox driver of Selenium - // and/or maybe in Firefox itself when interacting with a window in the - // background, but also reflects better how the user would interact with - // the browser in real life. - $session = $this->actors[$actorName]->getSession(); - $session->switchToWindow($session->getWindowName()); - } - - /** - * @AfterScenario - * - * Stops all the Mink Sessions used in the last Scenario. - */ - public function cleanUpSessions() { - foreach ($this->actors as $actor) { - $actor->getSession()->stop(); - } - } -} diff --git a/tests/acceptance/features/core/ElementFinder.php b/tests/acceptance/features/core/ElementFinder.php deleted file mode 100644 index 714b100bfa2..00000000000 --- a/tests/acceptance/features/core/ElementFinder.php +++ /dev/null @@ -1,203 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * Command object to find Mink elements. - * - * The element locator is relative to its ancestor (either another locator or an - * actual element); if it has no ancestor then the base document element is - * used. - * - * Sometimes an element may not be found simply because it has not appeared yet; - * for those cases ElementFinder supports trying again to find the element - * several times before giving up. The timeout parameter controls how much time - * to wait, at most, to find the element; the timeoutStep parameter controls how - * much time to wait before trying again to find the element. If ancestor - * locators need to be found the timeout is applied individually to each one, - * that is, if the timeout is 10 seconds the method will wait up to 10 seconds - * to find the ancestor of the ancestor and, then, up to 10 seconds to find the - * ancestor and, then, up to 10 seconds to find the element. By default the - * timeout is 0, so the element and its ancestor will be looked for just once; - * the default time to wait before retrying is half a second. - * - * In any case, if the element, or its ancestors, can not be found a - * NoSuchElementException is thrown. - */ -class ElementFinder { - /** - * Finds an element in the given Mink Session. - * - * @see ElementFinder - */ - private static function findInternal(\Behat\Mink\Session $session, Locator $elementLocator, $timeout, $timeoutStep) { - $element = null; - $selector = $elementLocator->getSelector(); - $locator = $elementLocator->getLocator(); - $ancestorElement = self::findAncestorElement($session, $elementLocator, $timeout, $timeoutStep); - - $findCallback = function () use (&$element, $selector, $locator, $ancestorElement) { - $element = $ancestorElement->find($selector, $locator); - - return $element !== null; - }; - if (!Utils::waitFor($findCallback, $timeout, $timeoutStep)) { - $message = $elementLocator->getDescription() . " could not be found"; - if ($timeout > 0) { - $message = $message . " after $timeout seconds"; - } - throw new NoSuchElementException($message); - } - - return $element; - } - - /** - * Returns the ancestor element from which the given locator will be looked - * for. - * - * If the ancestor of the given locator is another locator the element for - * the ancestor locator is found and returned. If the ancestor of the given - * locator is already an element that element is the one returned. If the - * given locator has no ancestor then the base document element is returned. - * - * The timeout is used only when finding the element for the ancestor - * locator; if the timeout expires a NoSuchElementException is thrown. - * - * @param \Behat\Mink\Session $session the Mink Session to get the ancestor - * element from. - * @param Locator $elementLocator the locator for the element to get its - * ancestor. - * @param float $timeout the number of seconds (decimals allowed) to wait at - * most for the ancestor element to appear. - * @param float $timeoutStep the number of seconds (decimals allowed) to - * wait before trying to find the ancestor element again. - * @return \Behat\Mink\Element\Element the ancestor element found. - * @throws NoSuchElementException if the ancestor element can not be found. - */ - private static function findAncestorElement(\Behat\Mink\Session $session, Locator $elementLocator, $timeout, $timeoutStep) { - $ancestorElement = $elementLocator->getAncestor(); - if ($ancestorElement instanceof Locator) { - try { - $ancestorElement = self::findInternal($session, $ancestorElement, $timeout, $timeoutStep); - } catch (NoSuchElementException $exception) { - // Little hack to show the stack of ancestor elements that could - // not be found, as Behat only shows the message of the last - // exception in the chain. - $message = $exception->getMessage() . "\n" . - $elementLocator->getDescription() . " could not be found"; - if ($timeout > 0) { - $message = $message . " after $timeout seconds"; - } - throw new NoSuchElementException($message, $exception); - } - } - - if ($ancestorElement === null) { - $ancestorElement = $session->getPage(); - } - - return $ancestorElement; - } - - /** - * @var \Behat\Mink\Session - */ - private $session; - - /** - * @param Locator - */ - private $elementLocator; - - /** - * @var float - */ - private $timeout; - - /** - * @var float - */ - private $timeoutStep; - - /** - * Creates a new ElementFinder. - * - * @param \Behat\Mink\Session $session the Mink Session to get the element - * from. - * @param Locator $elementLocator the locator for the element. - * @param float $timeout the number of seconds (decimals allowed) to wait at - * most for the element to appear. - * @param float $timeoutStep the number of seconds (decimals allowed) to - * wait before trying to find the element again. - */ - public function __construct(\Behat\Mink\Session $session, Locator $elementLocator, $timeout, $timeoutStep) { - $this->session = $session; - $this->elementLocator = $elementLocator; - $this->timeout = $timeout; - $this->timeoutStep = $timeoutStep; - } - - /** - * Returns the description of the element to find. - * - * @return string the description of the element to find. - */ - public function getDescription() { - return $this->elementLocator->getDescription(); - } - - /** - * Returns the timeout. - * - * @return float the number of seconds (decimals allowed) to wait at most - * for the element to appear. - */ - public function getTimeout() { - return $this->timeout; - } - - /** - * Returns the timeout step. - * - * @return float the number of seconds (decimals allowed) to wait before - * trying to find the element again. - */ - public function getTimeoutStep() { - return $this->timeoutStep; - } - - /** - * Finds an element using the parameters set in the constructor of this - * ElementFinder. - * - * If the element, or its ancestors, can not be found a - * NoSuchElementException is thrown. - * - * @return \Behat\Mink\Element\Element the element found. - * @throws NoSuchElementException if the element, or its ancestor, can not - * be found. - */ - public function find() { - return self::findInternal($this->session, $this->elementLocator, $this->timeout, $this->timeoutStep); - } -} diff --git a/tests/acceptance/features/core/ElementWrapper.php b/tests/acceptance/features/core/ElementWrapper.php deleted file mode 100644 index 6ac9a6b8e8f..00000000000 --- a/tests/acceptance/features/core/ElementWrapper.php +++ /dev/null @@ -1,358 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * Wrapper to automatically handle failed commands on Mink elements. - * - * Commands executed on Mink elements may fail for several reasons. The - * ElementWrapper frees the caller of the commands from handling the most common - * reasons of failure. - * - * StaleElementReference exceptions are thrown when the command is executed on - * an element that is no longer attached to the DOM. This can happen even in - * a chained call like "$actor->find($locator)->click()"; in the milliseconds - * between finding the element and clicking it the element could have been - * removed from the page (for example, if a previous interaction with the page - * started an asynchronous update of the DOM). Every command executed through - * the ElementWrapper is guarded against StaleElementReference exceptions; if - * the element is stale it is found again using the same parameters to find it - * in the first place. - * - * NoSuchElement exceptions are sometimes thrown instead of - * StaleElementReference exceptions. This can happen when the Selenium2 driver - * for Mink performs an action on an element through the WebDriver session - * instead of directly through the WebDriver element. In that case, if the - * element with the given ID does not exist, a NoSuchElement exception would be - * thrown instead of a StaleElementReference exception, so those cases are - * handled like StaleElementReference exceptions. - * - * ElementNotVisible exceptions are thrown when the command requires the element - * to be visible but the element is not. Finding an element only guarantees that - * (at that time) the element is attached to the DOM, but it does not provide - * any guarantee regarding its visibility. Due to that, a call like - * "$actor->find($locator)->click()" can fail if the element was hidden and - * meant to be made visible by a previous interaction with the page, but that - * interaction triggered an asynchronous update that was not finished when the - * click command is executed. All commands executed through the ElementWrapper - * that require the element to be visible are guarded against ElementNotVisible - * exceptions; if the element is not visible it is waited for it to be visible - * up to the timeout set to find it. - * - * MoveTargetOutOfBounds exceptions are sometimes thrown instead of - * ElementNotVisible exceptions. This can happen when the Selenium2 driver for - * Mink moves the cursor on an element using the "moveto" method of the - * WebDriver session, for example, before clicking on an element. In that case, - * if the element is not visible, "moveto" would throw a MoveTargetOutOfBounds - * 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 - * element not becoming visible before the timeout expires (which would mean - * 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". 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 - * chained with the command to execute (but note that automatically handling - * failed commands will still be disabled if further commands are executed on - * the ElementWrapper). - */ -class ElementWrapper { - /** - * @var ElementFinder - */ - private $elementFinder; - - /** - * @var \Behat\Mink\Element\Element - */ - private $element; - - /** - * @param boolean - */ - private $handleFailedCommands; - - /** - * Creates a new ElementWrapper. - * - * The wrapped element is found in the constructor itself using the - * ElementFinder. - * - * @param ElementFinder $elementFinder the command object to find the - * wrapped element. - * @throws NoSuchElementException if the element, or its ancestor, can not - * be found. - */ - public function __construct(ElementFinder $elementFinder) { - $this->elementFinder = $elementFinder; - $this->element = $elementFinder->find(); - $this->handleFailedCommands = true; - } - - /** - * Returns the raw Mink element. - * - * @return \Behat\Mink\Element\Element the wrapped element. - */ - public function getWrappedElement() { - return $this->element; - } - - /** - * Prevents the automatic handling of failed commands. - * - * @return ElementWrapper this ElementWrapper. - */ - public function doNotHandleFailedCommands() { - $this->handleFailedCommands = false; - - return $this; - } - - /** - * Returns whether the wrapped element is visible or not. - * - * @return bool true if the wrapped element is visible, false otherwise. - */ - public function isVisible() { - $commandCallback = function () { - return $this->element->isVisible(); - }; - return $this->executeCommand($commandCallback, "visibility could not be got"); - } - - /** - * Returns whether the wrapped element is checked or not. - * - * @return bool true if the wrapped element is checked, false otherwise. - */ - public function isChecked() { - $commandCallback = function () { - return $this->element->isChecked(); - }; - return $this->executeCommand($commandCallback, "check state could not be got"); - } - - /** - * Returns the text of the wrapped element. - * - * If the wrapped element is not visible the returned text is an empty - * string. - * - * @return string the text of the wrapped element, or an empty string if it - * is not visible. - */ - public function getText() { - $commandCallback = function () { - return $this->element->getText(); - }; - return $this->executeCommand($commandCallback, "text could not be got"); - } - - /** - * Returns the value of the wrapped element. - * - * The value can be got even if the wrapped element is not visible. - * - * @return string the value of the wrapped element. - */ - public function getValue() { - $commandCallback = function () { - return $this->element->getValue(); - }; - return $this->executeCommand($commandCallback, "value could not be got"); - } - - /** - * Sets the given value on the wrapped element. - * - * If automatically waits for the wrapped element to be visible (up to the - * timeout set when finding it). - * - * @param string $value the value to set. - */ - public function setValue($value) { - $commandCallback = function () use ($value) { - $this->element->setValue($value); - }; - $this->executeCommandOnVisibleElement($commandCallback, "value could not be set"); - } - - /** - * Clicks on the wrapped element. - * - * If automatically waits for the wrapped element to be visible (up to the - * timeout set when finding it). - */ - public function click() { - $commandCallback = function () { - $this->element->click(); - }; - $this->executeCommandOnVisibleElement($commandCallback, "could not be clicked"); - } - - /** - * Check the wrapped element. - * - * If automatically waits for the wrapped element to be visible (up to the - * timeout set when finding it). - */ - public function check() { - $commandCallback = function () { - $this->element->check(); - }; - $this->executeCommand($commandCallback, "could not be checked"); - } - - /** - * uncheck the wrapped element. - * - * If automatically waits for the wrapped element to be visible (up to the - * timeout set when finding it). - */ - public function uncheck() { - $commandCallback = function () { - $this->element->uncheck(); - }; - $this->executeCommand($commandCallback, "could not be unchecked"); - } - - /** - * Executes the given command. - * - * If a StaleElementReference or a NoSuchElement exception is thrown the - * wrapped element is found again and, then, the command is executed again. - * - * @param \Closure $commandCallback the command to execute. - * @param string $errorMessage an error message that describes the failed - * command (appended to the description of the element). - */ - private function executeCommand(\Closure $commandCallback, $errorMessage) { - if (!$this->handleFailedCommands) { - return $commandCallback(); - } - - try { - return $commandCallback(); - } catch (\WebDriver\Exception\StaleElementReference $exception) { - $this->printFailedCommandMessage($exception, $errorMessage); - } catch (\WebDriver\Exception\NoSuchElement $exception) { - $this->printFailedCommandMessage($exception, $errorMessage); - } - - $this->element = $this->elementFinder->find(); - - return $commandCallback(); - } - - /** - * Executes the given command on a visible element. - * - * If a StaleElementReference or a NoSuchElement exception is thrown the - * wrapped element is found again and, then, the command is executed again. - * 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 - * command (appended to the description of the element). - */ - private function executeCommandOnVisibleElement(\Closure $commandCallback, $errorMessage) { - if (!$this->handleFailedCommands) { - return $commandCallback(); - } - - try { - return $this->executeCommand($commandCallback, $errorMessage); - } catch (\WebDriver\Exception\ElementNotVisible $exception) { - $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(); - - return $commandCallback(); - } - - /** - * Prints information about the failed command. - * - * @param \Exception exception the exception thrown by the command. - * @param string $errorMessage an error message that describes the failed - * command (appended to the description of the locator of the element). - */ - private function printFailedCommandMessage(\Exception $exception, $errorMessage) { - echo $this->elementFinder->getDescription() . " " . $errorMessage . "\n"; - echo "Exception message: " . $exception->getMessage() . "\n"; - echo "Trying again\n"; - } - - /** - * Waits for the wrapped element to be visible. - * - * This method waits up to the timeout used when finding the wrapped - * element; therefore, it may return when the element is still not visible. - * - * @return boolean true if the element is visible after the wait, false - * otherwise. - */ - private function waitForElementToBeVisible() { - $isVisibleCallback = function () { - return $this->isVisible(); - }; - $timeout = $this->elementFinder->getTimeout(); - $timeoutStep = $this->elementFinder->getTimeoutStep(); - - return Utils::waitFor($isVisibleCallback, $timeout, $timeoutStep); - } -} diff --git a/tests/acceptance/features/core/Locator.php b/tests/acceptance/features/core/Locator.php deleted file mode 100644 index 1b933399e6b..00000000000 --- a/tests/acceptance/features/core/Locator.php +++ /dev/null @@ -1,313 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * Data object for the information needed to locate an element in a web page - * using Mink. - * - * Locators can be created directly using the constructor, or through a more - * fluent interface with Locator::forThe(). - */ -class Locator { - /** - * @var string - */ - private $description; - - /** - * @var string - */ - private $selector; - - /** - * @var string|array - */ - private $locator; - - /** - * @var null|Locator|\Behat\Mink\Element\ElementInterface - */ - private $ancestor; - - /** - * Starting point for the fluent interface to create Locators. - * - * @return LocatorBuilder - */ - public static function forThe() { - return new LocatorBuilder(); - } - - /** - * @param string $description - * @param string $selector - * @param string|array $locator - * @param null|Locator|\Behat\Mink\Element\ElementInterface $ancestor - */ - public function __construct($description, $selector, $locator, $ancestor = null) { - $this->description = $description; - $this->selector = $selector; - $this->locator = $locator; - $this->ancestor = $ancestor; - } - - /** - * @return string - */ - public function getDescription() { - return $this->description; - } - - /** - * @return string - */ - public function getSelector() { - return $this->selector; - } - - /** - * @return string|array - */ - public function getLocator() { - return $this->locator; - } - - /** - * @return null|Locator|\Behat\Mink\Element\ElementInterface - */ - public function getAncestor() { - return $this->ancestor; - } -} - -class LocatorBuilder { - /** - * @param string $selector - * @param string|array $locator - * @return LocatorBuilderSecondStep - */ - public function customSelector($selector, $locator) { - return new LocatorBuilderSecondStep($selector, $locator); - } - - /** - * @param string $cssExpression - * @return LocatorBuilderSecondStep - */ - public function css($cssExpression) { - return $this->customSelector("css", $cssExpression); - } - - /** - * @param string $xpathExpression - * @return LocatorBuilderSecondStep - */ - public function xpath($xpathExpression) { - return $this->customSelector("xpath", $xpathExpression); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function id($value) { - return $this->customSelector("named_exact", ["id", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function idOrName($value) { - return $this->customSelector("named_exact", ["id_or_name", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function link($value) { - return $this->customSelector("named_exact", ["link", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function button($value) { - return $this->customSelector("named_exact", ["button", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function linkOrButton($value) { - return $this->customSelector("named_exact", ["link_or_button", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function field($value) { - return $this->customSelector("named_exact", ["field", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function selectField($value) { - return $this->customSelector("named_exact", ["select", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function checkbox($value) { - return $this->customSelector("named_exact", ["checkbox", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function radioButton($value) { - return $this->customSelector("named_exact", ["radio", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function fileInput($value) { - return $this->customSelector("named_exact", ["file", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function optionGroup($value) { - return $this->customSelector("named_exact", ["optgroup", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function option($value) { - return $this->customSelector("named_exact", ["option", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function fieldSet($value) { - return $this->customSelector("named_exact", ["fieldset", $value]); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function table($value) { - return $this->customSelector("named_exact", ["table", $value]); - } -} - -class LocatorBuilderSecondStep { - /** - * @var string - */ - private $selector; - - /** - * @var string|array - */ - private $locator; - - /** - * @param string $selector - * @param string|array $locator - */ - public function __construct($selector, $locator) { - $this->selector = $selector; - $this->locator = $locator; - } - - /** - * @param Locator|\Behat\Mink\Element\ElementInterface $ancestor - * @return LocatorBuilderThirdStep - */ - public function descendantOf($ancestor) { - return new LocatorBuilderThirdStep($this->selector, $this->locator, $ancestor); - } - - /** - * @param string $description - * @return Locator - */ - public function describedAs($description) { - return new Locator($description, $this->selector, $this->locator); - } -} - -class LocatorBuilderThirdStep { - /** - * @var string - */ - private $selector; - - /** - * @var string|array - */ - private $locator; - - /** - * @var Locator|\Behat\Mink\Element\ElementInterface - */ - private $ancestor; - - /** - * @param string $selector - * @param string|array $locator - * @param Locator|\Behat\Mink\Element\ElementInterface $ancestor - */ - public function __construct($selector, $locator, $ancestor) { - $this->selector = $selector; - $this->locator = $locator; - $this->ancestor = $ancestor; - } - - /** - * @param string $description - * @return Locator - */ - public function describedAs($description) { - return new Locator($description, $this->selector, $this->locator, $this->ancestor); - } -} diff --git a/tests/acceptance/features/core/NextcloudTestServerContext.php b/tests/acceptance/features/core/NextcloudTestServerContext.php deleted file mode 100644 index d0dc0c32181..00000000000 --- a/tests/acceptance/features/core/NextcloudTestServerContext.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -use Behat\Behat\Context\Context; -use Behat\Behat\Hook\Scope\BeforeScenarioScope; - -/** - * Behat context to run each scenario against a clean Nextcloud server. - * - * Before each scenario is run, this context sets up a fresh Nextcloud server - * with predefined data and configuration. Thanks to this every scenario is - * independent from the others and they all know the initial state of the - * server. - * - * This context is expected to be used along with RawMinkContext contexts (or - * subclasses). As the server address can be different for each scenario, this - * context automatically sets the "base_url" parameter of all its sibling - * RawMinkContexts; just add NextcloudTestServerContext to the context list of a - * suite in "behat.yml". - * - * The Nextcloud server is provided by an instance of NextcloudTestServerHelper; - * its class must be specified when this context is created. By default, - * "NextcloudTestServerLocalBuiltInHelper" is used, although that can be - * customized using the "nextcloudTestServerHelper" parameter in "behat.yml". In - * the same way, the parameters to be passed to the helper when it is created - * can be customized using the "nextcloudTestServerHelperParameters" parameter, - * which is an array (without keys) with the value of the parameters in the same - * order as in the constructor of the helper class (by default, [ ]). - * - * Example of custom parameters in "behat.yml": - * default: - * suites: - * default: - * contexts: - * - NextcloudTestServerContext: - * nextcloudTestServerHelper: NextcloudTestServerCustomHelper - * nextcloudTestServerHelperParameters: - * - first-parameter-value - * - second-parameter-value - */ -class NextcloudTestServerContext implements Context { - /** - * @var NextcloudTestServerHelper - */ - private $nextcloudTestServerHelper; - - /** - * Creates a new NextcloudTestServerContext. - * - * @param string $nextcloudTestServerHelper the name of the - * NextcloudTestServerHelper implementing class to use. - * @param array $nextcloudTestServerHelperParameters the parameters for the - * constructor of the $nextcloudTestServerHelper class. - */ - public function __construct($nextcloudTestServerHelper = "NextcloudTestServerLocalBuiltInHelper", - $nextcloudTestServerHelperParameters = [ ]) { - $nextcloudTestServerHelperClass = new ReflectionClass($nextcloudTestServerHelper); - - if ($nextcloudTestServerHelperParameters === null) { - $nextcloudTestServerHelperParameters = []; - } - - $this->nextcloudTestServerHelper = $nextcloudTestServerHelperClass->newInstanceArgs($nextcloudTestServerHelperParameters); - } - - /** - * @BeforeScenario - * - * Sets up the Nextcloud test server before each scenario. - * - * Once the Nextcloud test server is set up, the "base_url" parameter of the - * sibling RawMinkContexts is set to the base URL of the Nextcloud test - * server. - * - * @param \Behat\Behat\Hook\Scope\BeforeScenarioScope $scope the - * BeforeScenario hook scope. - * @throws \Exception if the Nextcloud test server can not be set up or its - * base URL got. - */ - public function setUpNextcloudTestServer(BeforeScenarioScope $scope) { - $this->nextcloudTestServerHelper->setUp(); - - $this->setBaseUrlInSiblingRawMinkContexts($scope, $this->nextcloudTestServerHelper->getBaseUrl()); - } - - /** - * @AfterScenario - * - * Cleans up the Nextcloud test server after each scenario. - * - * @throws \Exception if the Nextcloud test server can not be cleaned up. - */ - public function cleanUpNextcloudTestServer() { - $this->nextcloudTestServerHelper->cleanUp(); - } - - private function setBaseUrlInSiblingRawMinkContexts(BeforeScenarioScope $scope, $baseUrl) { - $environment = $scope->getEnvironment(); - - foreach ($environment->getContexts() as $context) { - if ($context instanceof Behat\MinkExtension\Context\RawMinkContext) { - $context->setMinkParameter("base_url", $baseUrl); - } - } - } -} diff --git a/tests/acceptance/features/core/NextcloudTestServerHelper.php b/tests/acceptance/features/core/NextcloudTestServerHelper.php deleted file mode 100644 index 69b8ce70505..00000000000 --- a/tests/acceptance/features/core/NextcloudTestServerHelper.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * Interface for classes that manage a Nextcloud server during acceptance tests. - * - * A NextcloudTestServerHelper takes care of setting up a Nextcloud server to be - * used in acceptance tests through its "setUp" method. It does not matter - * wheter the server is a fresh new server just started or an already running - * server; in any case, the state of the server must comply with the initial - * state expected by the tests (like having performed the Nextcloud installation - * or having an admin user with certain password). - * - * As the IP address and thus its the base URL of the server is not known - * beforehand, the NextcloudTestServerHelper must provide it through its - * "getBaseUrl" method. Note that this must be the base URL from the point of - * view of the Selenium server, which may be a different value than the base URL - * from the point of view of the acceptance tests themselves. - * - * Once the Nextcloud test server is no longer needed the "cleanUp" method will - * be called; depending on how the Nextcloud test server was set up it may not - * need to do anything. - * - * All the methods throw an exception if they fail to execute; as, due to the - * current use of this interface, it is just a warning for the test runner and - * nothing to be explicitly catched a plain base Exception is used. - */ -interface NextcloudTestServerHelper { - /** - * Sets up the Nextcloud test server. - * - * @throws \Exception if the Nextcloud test server can not be set up. - */ - public function setUp(); - - /** - * Cleans up the Nextcloud test server. - * - * @throws \Exception if the Nextcloud test server can not be cleaned up. - */ - public function cleanUp(); - - /** - * Returns the base URL of the Nextcloud test server (from the point of view - * of the Selenium server). - * - * @return string the base URL of the Nextcloud test server. - * @throws \Exception if the base URL can not be determined. - */ - public function getBaseUrl(); -} diff --git a/tests/acceptance/features/core/NextcloudTestServerLocalApacheHelper.php b/tests/acceptance/features/core/NextcloudTestServerLocalApacheHelper.php deleted file mode 100644 index 367e950931d..00000000000 --- a/tests/acceptance/features/core/NextcloudTestServerLocalApacheHelper.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * Helper to manage a Nextcloud test server started directly by the acceptance - * tests themselves using the Apache web server. - * - * The Nextcloud test server is executed using the Apache web server; the - * default Apache directory is expected to have been set to the root directory - * of the Nextcloud server (for example, by linking "var/www/html" to it); in - * any case, note that the acceptance tests must be run from the acceptance - * tests directory. The "setUp" method resets the Nextcloud server to its - * initial state and starts it, while the "cleanUp" method stops it. To be able - * to reset the Nextcloud server to its initial state a Git repository must be - * provided in the root directory of the Nextcloud server; the last commit in - * that Git repository must provide the initial state for the Nextcloud server - * expected by the acceptance tests. When the Nextcloud server is reset the - * owner of "apps", "config" and "data" must be set to the user that Apache - * server is run as; it is assumed that Apache is run as "www-data". - * - * The Nextcloud server is available at "$nextcloudServerDomain", which can be - * optionally specified when the NextcloudTestServerLocalApacheHelper is - * created; if no value is given "127.0.0.1" is used by default. In any case, - * the value of "$nextcloudServerDomain" must be seen as a trusted domain by the - * Nextcloud server (which would be the case for "127.0.0.1" if it was installed - * by running "occ maintenance:install"). The base URL to access the Nextcloud - * server can be got from "getBaseUrl". - */ -class NextcloudTestServerLocalApacheHelper implements NextcloudTestServerHelper { - /** - * @var string - */ - private $nextcloudServerDomain; - - /** - * Creates a new NextcloudTestServerLocalApacheHelper. - */ - public function __construct($nextcloudServerDomain = "127.0.0.1") { - $this->nextcloudServerDomain = $nextcloudServerDomain; - } - - /** - * Sets up the Nextcloud test server. - * - * It resets the Nextcloud test server restoring its last saved Git state - * and then waits for the Nextcloud test server to start again; if the - * server can not be reset or if it does not start again after some time an - * exception is thrown (as it is just a warning for the test runner and - * nothing to be explicitly catched a plain base Exception is used). - * - * @throws \Exception if the Nextcloud test server can not be reset or - * started again. - */ - public function setUp(): void { - // Ensure that previous Apache server is not running (as cleanUp may not - // have been called). - $this->stopApacheServer(); - - $this->execOrException("cd ../../ && git reset --hard HEAD"); - $this->execOrException("cd ../../ && git clean -d --force"); - $this->execOrException("cd ../../ && chown -R www-data:www-data apps config data"); - - $this->execOrException("service apache2 start"); - - $timeout = 60; - if (!Utils::waitForServer($this->getBaseUrl(), $timeout)) { - throw new Exception("Nextcloud test server could not be started"); - } - } - - /** - * Cleans up the Nextcloud test server. - * - * It stops the running Nextcloud test server, if any. - */ - public function cleanUp() { - $this->stopApacheServer(); - } - - /** - * Returns the base URL of the Nextcloud test server. - * - * @return string the base URL of the Nextcloud test server. - */ - public function getBaseUrl() { - return "http://" . $this->nextcloudServerDomain . "/index.php"; - } - - /** - * Executes the given command, throwing an Exception if it fails. - * - * @param string $command the command to execute. - * @throws \Exception if the command fails to execute. - */ - private function execOrException($command) { - exec($command . " 2>&1", $output, $returnValue); - if ($returnValue != 0) { - throw new Exception("'$command' could not be executed: " . implode("\n", $output)); - } - } - - /** - * Stops the Apache server started in setUp, if any. - */ - private function stopApacheServer() { - $this->execOrException("service apache2 stop"); - } -} diff --git a/tests/acceptance/features/core/NextcloudTestServerLocalBuiltInHelper.php b/tests/acceptance/features/core/NextcloudTestServerLocalBuiltInHelper.php deleted file mode 100644 index a1ab1f8720a..00000000000 --- a/tests/acceptance/features/core/NextcloudTestServerLocalBuiltInHelper.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * Helper to manage a Nextcloud test server started directly by the acceptance - * tests themselves using the PHP built-in web server. - * - * The Nextcloud test server is executed using the PHP built-in web server - * directly from the grandparent directory of the acceptance tests directory - * (that is, the root directory of the Nextcloud server); note that the - * acceptance tests must be run from the acceptance tests directory. The "setUp" - * method resets the Nextcloud server to its initial state and starts it, while - * the "cleanUp" method stops it. To be able to reset the Nextcloud server to - * its initial state a Git repository must be provided in the root directory of - * the Nextcloud server; the last commit in that Git repository must provide the - * initial state for the Nextcloud server expected by the acceptance tests. - * - * The Nextcloud server is available at "$nextcloudServerDomain", which can be - * optionally specified when the NextcloudTestServerLocalBuiltInHelper is - * created; if no value is given "127.0.0.1" is used by default. In any case, - * the value of "$nextcloudServerDomain" must be seen as a trusted domain by the - * Nextcloud server (which would be the case for "127.0.0.1" if it was installed - * by running "occ maintenance:install"). The base URL to access the Nextcloud - * server can be got from "getBaseUrl". - */ -class NextcloudTestServerLocalBuiltInHelper implements NextcloudTestServerHelper { - /** - * @var string - */ - private $nextcloudServerDomain; - - /** - * @var string - */ - private $phpServerPid; - - /** - * Creates a new NextcloudTestServerLocalBuiltInHelper. - */ - public function __construct($nextcloudServerDomain = "127.0.0.1") { - $this->nextcloudServerDomain = $nextcloudServerDomain; - - $this->phpServerPid = ""; - } - - /** - * Sets up the Nextcloud test server. - * - * It resets the Nextcloud test server restoring its last saved Git state - * and then waits for the Nextcloud test server to start again; if the - * server can not be reset or if it does not start again after some time an - * exception is thrown (as it is just a warning for the test runner and - * nothing to be explicitly catched a plain base Exception is used). - * - * @throws \Exception if the Nextcloud test server can not be reset or - * started again. - */ - public function setUp(): void { - // Ensure that previous PHP server is not running (as cleanUp may not - // have been called). - $this->killPhpServer(); - - $this->execOrException("cd ../../ && git reset --hard HEAD"); - $this->execOrException("cd ../../ && git clean -d --force"); - - // execOrException is not used because the server is started in the - // background, so the command will always succeed even if the server - // itself fails. - $this->phpServerPid = exec("php -S " . $this->nextcloudServerDomain . ":80 -t ../../ >/dev/null 2>&1 & echo $!"); - - $timeout = 60; - if (!Utils::waitForServer($this->getBaseUrl(), $timeout)) { - throw new Exception("Nextcloud test server could not be started"); - } - } - - /** - * Cleans up the Nextcloud test server. - * - * It kills the running Nextcloud test server, if any. - */ - public function cleanUp() { - $this->killPhpServer(); - } - - /** - * Returns the base URL of the Nextcloud test server. - * - * @return string the base URL of the Nextcloud test server. - */ - public function getBaseUrl() { - return "http://" . $this->nextcloudServerDomain . "/index.php"; - } - - /** - * Executes the given command, throwing an Exception if it fails. - * - * @param string $command the command to execute. - * @throws \Exception if the command fails to execute. - */ - private function execOrException($command) { - exec($command . " 2>&1", $output, $returnValue); - if ($returnValue != 0) { - throw new Exception("'$command' could not be executed: " . implode("\n", $output)); - } - } - - /** - * Kills the PHP built-in web server started in setUp, if any. - */ - private function killPhpServer() { - if ($this->phpServerPid == "") { - return; - } - - // execOrException is not used because the PID may no longer exist when - // trying to kill it. - exec("kill " . $this->phpServerPid); - - $this->phpServerPid = ""; - } -} diff --git a/tests/acceptance/features/core/NoSuchElementException.php b/tests/acceptance/features/core/NoSuchElementException.php deleted file mode 100644 index 35583c7e63f..00000000000 --- a/tests/acceptance/features/core/NoSuchElementException.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * Exception to signal that the element looked for could not be found. - */ -class NoSuchElementException extends \Exception { - /** - * @param string $message - * @param null|\Exception $previous - */ - public function __construct($message, \Exception $previous = null) { - parent::__construct($message, 0, $previous); - } -} diff --git a/tests/acceptance/features/core/Utils.php b/tests/acceptance/features/core/Utils.php deleted file mode 100644 index eb7c65e993a..00000000000 --- a/tests/acceptance/features/core/Utils.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php - -/** - * - * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -class Utils { - /** - * Waits at most $timeout seconds for the given condition to be true, - * checking it again every $timeoutStep seconds. - * - * Note that the timeout is no longer taken into account when a condition is - * met; that is, true will be returned if the condition is met before the - * timeout expires, but also if it is met exactly when the timeout expires. - * For example, even if the timeout is set to 0, the condition will be - * checked at least once, and true will be returned in that case if the - * condition was met. - * - * @param \Closure $conditionCallback the condition to wait for, as a - * function that returns a boolean. - * @param float $timeout the number of seconds (decimals allowed) to wait at - * most for the condition to be true. - * @param float $timeoutStep the number of seconds (decimals allowed) to - * wait before checking the condition again. - * @return boolean true if the condition is met before (or exactly when) the - * timeout expires, false otherwise. - */ - public static function waitFor($conditionCallback, $timeout, $timeoutStep) { - $elapsedTime = 0; - $conditionMet = false; - - while (!($conditionMet = $conditionCallback()) && $elapsedTime < $timeout) { - usleep($timeoutStep * 1000000); - - $elapsedTime += $timeoutStep; - } - - return $conditionMet; - } - - /** - * Waits at most $timeout seconds for the server at the given URL to be up, - * checking it again every $timeoutStep seconds. - * - * Note that it does not verify whether the URL returns a valid HTTP status - * or not; it simply checks that the server at the given URL is accessible. - * - * @param string $url the URL for the server to check. - * @param float $timeout the number of seconds (decimals allowed) to wait at - * most for the server. - * @param float $timeoutStep the number of seconds (decimals allowed) to - * wait before checking the server again; by default, 0.5 seconds. - * @return boolean true if the server was found, false otherwise. - */ - public static function waitForServer($url, $timeout, $timeoutStep = 0.5) { - $isServerUpCallback = function () use ($url) { - $curlHandle = curl_init($url); - - // Returning the transfer as the result of curl_exec prevents the - // transfer from being written to the output. - curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true); - - $transfer = curl_exec($curlHandle); - - curl_close($curlHandle); - - return $transfer !== false; - }; - return self::waitFor($isServerUpCallback, $timeout, $timeoutStep); - } -} |