Snippets

Steve Adams PHPUnit and Facebook Webdriver (Selenium) foundation

Created by Steve Adams

File AbstractBaseFeatureTest.php Added

  • Ignore whitespace
  • Hide word diff
+<?php
+
+namespace Your\Namespace\Tests\Features;
+
+use Facebook\WebDriver\WebDriverBy;
+use Facebook\WebDriver\WebDriverExpectedCondition;
+use Facebook\WebDriver\Remote\DesiredCapabilities;
+use Facebook\WebDriver\Remote\RemoteWebDriver;
+use Facebook\WebDriver\Remote\RemoteWebElement;
+use Facebook\WebDriver\Exception\StaleElementReferenceException;
+
+/**
+ * @coversNothing
+ */
+abstract class AbstractBaseFeatureTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var RemoteWebDriver
+     */
+    protected $webDriver;
+
+    /**
+     * Set this in phpunit.xml
+     * @var string
+     */
+    protected $url = WEBDRIVER_BASE_SITE_URL;
+
+    /**
+     * @var DesiredCapabilities
+     */
+    protected $desiredCapabilities;
+
+    /**
+     * Sets the desiredCapabilities field based on the WEBDRIVER_BROWSER_NAME constant defined in phpunit.xml. Currently
+     * supports chrome and firefox, other values will result in an exception.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+
+        switch (WEBDRIVER_BROWSER_NAME) {
+            case 'chrome':
+                $this->desiredCapabilities = DesiredCapabilities::chrome();
+                break;
+            case 'firefox':
+                $this->desiredCapabilities = DesiredCapabilities::firefox();
+                break;
+            default:
+                throw new \Exception('Running tests on ' . ucfirst(WEBDRIVER_BROWSER_NAME) . ' is not supported.');
+        }
+    }
+
+    protected function setUp()
+    {
+        $this->webDriver = RemoteWebDriver::create(
+            WEBDRIVER_HOST,
+            $this->desiredCapabilities
+        );
+    }
+
+    protected function tearDown()
+    {
+        $this->webDriver->quit();
+    }
+
+    /**
+     * Loads the page at $url. Returns control to the caller after the page has finished loading.
+     *
+     * @param string $url
+     */
+    protected function getPageAndWaitForLoad($url)
+    {
+        $this->webDriver->get($url);
+
+        $this->webDriver->wait(10, 100)->until(function () {
+            return $this->webDriver->executeScript('return document.readyState') == 'complete';
+        });
+    }
+
+    /**
+     * Finds the element identified by $condition and passes it to clickThroughToNewPageByElement. Returns control to
+     * the caller after the new page finishes loading.
+     *
+     * @param WebDriverBy $condition
+     */
+    protected function clickThroughToNewPageBy(WebDriverBy $condition)
+    {
+        $element = $this->webDriver->findElement($condition);
+        $this->clickThroughToNewPageByElement($element);
+    }
+
+    /**
+     * Syntax sugar for calling clickThroughToNewPageBy with a link's text.
+     *
+     * @param string $linkText
+     */
+    protected function clickThroughToNewPageByLinkText($linkText)
+    {
+        $this->clickThroughToNewPageBy(WebDriverBy::linkText($linkText));
+    }
+
+    /**
+     * Syntax sugar for calling clickThroughToNewPageBy with a CSS selector.
+     *
+     * @param string $cssSelector
+     */
+    protected function clickThroughToNewPageByCssSelector($cssSelector)
+    {
+        $this->clickThroughToNewPageBy(WebDriverBy::cssSelector($cssSelector));
+    }
+
+    /**
+     * Syntax sugar for calling clickThroughToNewPageBy with an ID.
+     *
+     * @param string $id
+     */
+    protected function clickThroughToNewPageById($id)
+    {
+        $this->clickThroughToNewPageBy(WebDriverBy::id($id));
+    }
+
+    /**
+     * Syntax sugar for calling clickThroughToNewPageBy with an XPath selector.
+     *
+     * @param string $xPath
+     */
+    protected function clickThroughToNewPageByXPath($xPath)
+    {
+        $this->clickThroughToNewPageBy(WebDriverBy::xpath($xPath));
+    }
+
+    /**
+     * Clicks on the given element and waits for a new page to load.
+     *
+     * @param RemoteWebElement $element
+     */
+    protected function clickThroughToNewPageByElement(RemoteWebElement $element)
+    {
+        $element->click();
+        $this->webDriver->wait(10, 100)->until(WebDriverExpectedCondition::stalenessOf($element));
+    }
+
+    /**
+     * Gets an element using xpath but waits until it's present by a given condition.
+     *
+     * @param string $xpath     The xpath for the element - Defaults to presenceOfElementLocated.
+     * @param string $condition A WebDriverExpectedCondition
+     *
+     * @return WebDriverRemoteElement
+     */
+    protected function getElementByXpath($xpath, $condition = "presenceOfElementLocated")
+    {
+        $this->webDriver->wait(10, 100)->until(
+            WebDriverExpectedCondition::$condition(
+                WebDriverBy::xpath($xpath)
+            )
+        );
+
+        return $this->webDriver->findElement(WebDriverBy::xpath($xpath));
+    }
+
+    /**
+     * Gets elements using xpath
+     *
+     * @param string $xpath     The xpath for the elements.
+     *
+     * @return array
+     */
+    protected function getElementsByXpath($xpath)
+    {
+        return $this->webDriver->findElements(WebDriverBy::xpath($xpath));
+    }
+
+    /**
+     * Click 'okay' on an alert.
+     *
+     * @return AbstractBaseFeatureTest
+     */
+    protected function acceptAlert()
+    {
+        $this->webDriver->wait()->until(
+            WebDriverExpectedCondition::alertIsPresent()
+        );
+
+        $this->webDriver->switchTo()->alert()->accept();
+
+        return $this;
+    }
+
+    /**
+     * Click 'cancel' on an alert.
+     *
+     * @return AbstractBaseFeatureTest
+     */
+    protected function dismissAlert()
+    {
+        $this->webDriver->wait()->until(
+            WebDriverExpectedCondition::alertIsPresent()
+        );
+
+        $this->webDriver->switchTo()->alert()->dismiss();
+
+        return $this;
+    }
+
+    /**
+     * Output text to the console during a test.
+     *
+     * @param string $value  Text to output.
+     * @param string $colour Which colour to make the text.
+     */
+    protected function output($value, $colour = null)
+    {
+        if ($colour) {
+            $colour = $this->getOutputColour($colour);
+        } else {
+            $colour = "";
+        }
+
+        fwrite(STDOUT, $colour . "{$value} \033[0m \n");
+    }
+
+    /**
+     * Get a colour code for outputting text.
+     *
+     * @param int $colour
+     *
+     * @return string The code for the given colour.
+     */
+    protected function getOutputColour($colour)
+    {
+        $colours = [
+            "white" => 37,
+            "error" => 31,
+            "success" => 32,
+            "warning" => 33,
+            "info" => 34
+        ];
+
+        return "\033[{$colours[$colour]}m";
+    }
+
+    /**
+     * Tries clicking an element. This repeated attempting pattern catches stale
+     * element exceptions which, until I wrote this method, were the bane of my existence.\
+     * This method isn't essential for every click, but it helps a lot with cases
+     * where JavaScript manages the DOM after a previous click occurred. For clicks
+     * before Javascript needs to run, this is probably overkill.
+     *
+     * @param RemoteWebElement $element      The element to try clicking.
+     * @param string           $elementXpath Path to the element to try re-referencing it.
+     * @param int              $numAttempts  How many times to try clicking.
+     * @param int              $wait         How long to wait between clicks in milliseconds.
+     *
+     * @return AbstractBaseFeatureTest
+     */
+    protected function tryClicking(RemoteWebElement $element, $elementXpath = null, $numAttempts = 5, $wait = 10)
+    {
+        $attempts = 0;
+
+        while ($attempts < $numAttempts) {
+            $attempts ++;
+
+            try {
+                $element->click();
+
+                return $this;
+            } catch (StaleElementReferenceException $exception) {
+                if ($elementXpath) {
+                    $this->output(
+                        "Re-referencing element due to stale element reference...",
+                        "error"
+                    );
+
+                    $element = $this->getElementByXpath($elementXpath, "elementToBeClickable");
+                }
+
+                $remaining = $numAttempts - $attempts;
+
+                $this->output(
+                    "Caught StaleElementReferenceException; waiting {$wait}ms then clicking again ({$remaining} tries remaining)...",
+                    "error"
+                );
+
+                $this->webDriver->manage()->timeouts()->implicitlyWait($wait);
+            }
+        }
+
+        // Looks like we made it here, so we should let the exception happen.
+        throw $exception;
+
+        return $this;
+    }
+}
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.