├── README.md ├── SampleTest.php ├── WebDriver.php ├── WebDriver ├── Driver.php ├── ElementNotVisibleException.php ├── FirefoxProfile.php ├── MockDriver.php ├── MockElement.php ├── NoSuchElementException.php ├── OverParallelLimitException.php ├── StaleElementReferenceException.php └── WebElement.php ├── WebDriverColorTest.php ├── WebDriverSelectorTest.php └── WebDriverXPathTest.php /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | These are the PHP bindings for the WebDriver API in Selenium 2. It's designed to work with PHPUnit and includes some built-ins for running tests at Sauce Labs. 4 | 5 | For more information, see: 6 | 7 | * [Selenium](http://code.google.com/p/selenium/) 8 | * [PHPUnit](https://github.com/sebastianbergmann/phpunit/) 9 | * [Sauce Labs](https://saucelabs.com/) 10 | 11 | # Usage 12 | 13 | See the included SampleTest.php. Start up the Selenium 2 standalone server (http://code.google.com/p/selenium/downloads/list) and run the test with: 14 | 15 | phpunit SampleTest.php 16 | 17 | Make sure phpunit is in your path! 18 | 19 | # Tests 20 | 21 | What's code without tests? Run the tests with: 22 | 23 | phpunit WebDriverSelectorTest.php 24 | phpunit WebDriverXPathTest.php 25 | phpunit WebDriverColorTest.php 26 | -------------------------------------------------------------------------------- /SampleTest.php: -------------------------------------------------------------------------------- 1 | set_preference("capability.policy.default.HTMLDocument.compatMode", "allAccess"); 20 | 21 | // Choose one of the following 22 | 23 | // For tests running at Sauce Labs 24 | // $this->driver = WebDriver_Driver::InitAtSauce( 25 | // "my-sauce-username", 26 | // "my-sauce-api-key", 27 | // "WINDOWS", 28 | // "firefox", 29 | // "10", 30 | // array( 31 | // 'firefox_profile' => $fp->get_profile() 32 | // )); 33 | // $sauce_job_name = get_class($this); 34 | // $this->driver->set_sauce_context("name", $sauce_job_name); 35 | 36 | // For a mock driver (for debugging) 37 | // $this->driver = new WebDriver_MockDriver(); 38 | // define('kFestDebug', true); 39 | 40 | // For a local driver 41 | $this->driver = WebDriver_Driver::InitAtLocal("4444", "firefox"); 42 | } 43 | 44 | // Forward calls to main driver 45 | public function __call($name, $arguments) { 46 | if (method_exists($this->driver, $name)) { 47 | return call_user_func_array(array($this->driver, $name), $arguments); 48 | } else { 49 | throw new Exception("Tried to call nonexistent method $name with arguments:\n" . print_r($arguments, true)); 50 | } 51 | } 52 | 53 | public function test() { 54 | $this->set_implicit_wait(5000); 55 | $this->load("http://seleniumhq.org/"); 56 | $this->assert_title("Selenium - Web Browser Automation"); 57 | $this->get_element("css=h2")->assert_text("What is Selenium?"); 58 | 59 | $this->get_element("id=q")->send_keys("webdriver"); 60 | $this->get_element("id=submit")->click(); 61 | 62 | $first_result = $this->get_element("css=a.gs-title")->get_text(); 63 | } 64 | 65 | public function tearDown() { 66 | if ($this->driver) { 67 | if ($this->hasFailed()) { 68 | $this->driver->set_sauce_context("passed", false); 69 | } else { 70 | $this->driver->set_sauce_context("passed", true); 71 | } 72 | $this->driver->quit(); 73 | } 74 | parent::tearDown(); 75 | } 76 | } -------------------------------------------------------------------------------- /WebDriver.php: -------------------------------------------------------------------------------- 1 | send_keys(WebDriver::ReturnKey()); 13 | private static $keys = array( 14 | 'NullKey' => "\uE000", 15 | 'CancelKey' => "\uE001", 16 | 'HelpKey' => "\uE002", 17 | 'BackspaceKey' => "\uE003", 18 | 'TabKey' => "\uE004", 19 | 'ClearKey' => "\uE005", 20 | 'ReturnKey' => "\uE006", 21 | 'EnterKey' => "\uE007", 22 | 'ShiftKey' => "\uE008", 23 | 'ControlKey' => "\uE009", 24 | 'AltKey' => "\uE00A", 25 | 'PauseKey' => "\uE00B", 26 | 'EscapeKey' => "\uE00C", 27 | 'SpaceKey' => "\uE00D", 28 | 'PageUpKey' => "\uE00E", 29 | 'PageDownKey' => "\uE00F", 30 | 'EndKey' => "\uE010", 31 | 'HomeKey' => "\uE011", 32 | 'LeftArrowKey' => "\uE012", 33 | 'UpArrowKey' => "\uE013", 34 | 'RightArrowKey' => "\uE014", 35 | 'DownArrowKey' => "\uE015", 36 | 'InsertKey' => "\uE016", 37 | 'DeleteKey' => "\uE017", 38 | 'SemicolonKey' => "\uE018", 39 | 'EqualsKey' => "\uE019", 40 | 'Numpad0Key' => "\uE01A", 41 | 'Numpad1Key' => "\uE01B", 42 | 'Numpad2Key' => "\uE01C", 43 | 'Numpad3Key' => "\uE01D", 44 | 'Numpad4Key' => "\uE01E", 45 | 'Numpad5Key' => "\uE01F", 46 | 'Numpad6Key' => "\uE020", 47 | 'Numpad7Key' => "\uE021", 48 | 'Numpad8Key' => "\uE022", 49 | 'Numpad9Key' => "\uE023", 50 | 'MultiplyKey' => "\uE024", 51 | 'AddKey' => "\uE025", 52 | 'SeparatorKey' => "\uE026", 53 | 'SubtractKey' => "\uE027", 54 | 'DecimalKey' => "\uE028", 55 | 'DivideKey' => "\uE029", 56 | 'F1Key' => "\uE031", 57 | 'F2Key' => "\uE032", 58 | 'F3Key' => "\uE033", 59 | 'F4Key' => "\uE034", 60 | 'F5Key' => "\uE035", 61 | 'F6Key' => "\uE036", 62 | 'F7Key' => "\uE037", 63 | 'F8Key' => "\uE038", 64 | 'F9Key' => "\uE039", 65 | 'F10Key' => "\uE03A", 66 | 'F11Key' => "\uE03B", 67 | 'F12Key' => "\uE03C", 68 | 'CommandKey' => "\uE03D", 69 | 'MetaKey' => "\uE03D", 70 | ); 71 | 72 | public static function __callStatic($name, $arguments) { 73 | if (isset(self::$keys[$name])) { 74 | return json_decode('"' . self::$keys[$name] . '"'); 75 | } else { 76 | throw new Exception("Can't type key $name"); 77 | } 78 | } 79 | 80 | public static function WaitUntil($callback, $parameters, $expected) { 81 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 82 | do { 83 | $actual = call_user_func_array($callback, $parameters); 84 | } while (time() < $end_time && $expected !== $actual); 85 | return $actual; 86 | } 87 | 88 | public static function Curl($http_type, $full_url, $payload = null, $escape_payload = true, $cookies = array()) { 89 | $curl = curl_init($full_url); 90 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_type); 91 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); 92 | curl_setopt($curl, CURLINFO_HEADER_OUT, TRUE); 93 | curl_setopt($curl, CURLOPT_HEADER, TRUE); 94 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, WebDriver::$CurlConnectTimeoutSec); 95 | curl_setopt($curl, CURLOPT_TIMEOUT, WebDriver::$CurlTimeoutSec); 96 | if (($http_type === "POST" || $http_type === "PUT") && $payload !== null) { 97 | if ($escape_payload && (is_array($payload) || is_object($payload))) { 98 | $payload = http_build_query($payload); 99 | } 100 | curl_setopt($curl, CURLOPT_POSTFIELDS, $payload); 101 | } 102 | $headers = array('Expect:', 'Accept: application/json'); 103 | if ($payload !== null && is_string($payload) && json_decode($payload) !== null) { 104 | $headers[] = 'Content-Type: application/json; charset=utf-8'; 105 | } 106 | if (is_string($payload)) { 107 | $headers[] = 'Content-Length: ' . strlen($payload); 108 | } 109 | curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 110 | if (!empty($cookies)) { 111 | $cookie_string = http_build_query($cookies, '', '; '); 112 | curl_setopt($curl, CURLOPT_COOKIE, $cookie_string); 113 | } 114 | $full_response = curl_exec($curl); 115 | $request_header = curl_getinfo($curl, CURLINFO_HEADER_OUT); 116 | WebDriver::LogDebug($request_header); 117 | WebDriver::LogDebug($payload); 118 | WebDriver::LogDebug("-"); 119 | WebDriver::LogDebug($full_response); 120 | WebDriver::LogDebug("====="); 121 | $error = curl_error($curl); 122 | curl_close($curl); 123 | PHPUnit_Framework_Assert::assertEquals("", $error, "Curl error: $error\nMethod: $http_type\nURL: $full_url\n" . print_r($payload, true)); 124 | $response_parts = explode("\r\n\r\n", $full_response, 2); 125 | $response['header'] = $response_parts[0]; 126 | if (!empty($response_parts[1])) { 127 | $response['body'] = $response_parts[1]; 128 | } 129 | return $response; 130 | } 131 | 132 | public static function ParseLocator($locator) { 133 | $se1_to_se2 = array( 134 | "identifier" => "id", 135 | "id" => "id", 136 | "name" => "name", 137 | "xpath" => "xpath", 138 | "link" => "link text", 139 | "css" => "css selector", 140 | // The dom selector in Se1 isn't in Se2 141 | // Se2 has 4 new selectors 142 | "partial link text", 143 | "tag name", 144 | "class", 145 | "class name" 146 | ); 147 | 148 | $locator_parts = explode("=", $locator, 2); 149 | if (array_key_exists($locator_parts[0], $se1_to_se2) && isset($locator_parts[1]) && strlen($locator_parts[1]) > 0) { // Explicit Se1 selector 150 | $strategy = $se1_to_se2[$locator_parts[0]]; 151 | $value = $locator_parts[1]; 152 | } else if (in_array($locator_parts[0], $se1_to_se2) && isset($locator_parts[1]) && strlen($locator_parts[1]) > 0) { // Explicit Se2 selector 153 | $strategy = $locator_parts[0]; 154 | $value = $locator_parts[1]; 155 | } else { // Guess the selector based on Se1 156 | if (substr($locator, 0, 2) === "//") { 157 | $strategy = "xpath"; 158 | $value = $locator; 159 | } else if (substr($locator, 0, 9) === "document." || substr($locator, 0, 4) === "dom=") { 160 | throw new Exception("DOM selectors aren't supported in WebDriver: $locator"); 161 | } else { // Fall back to id 162 | $strategy = "id"; 163 | $value = $locator; 164 | } 165 | } 166 | return array("using" => $strategy, "value" => $value); 167 | } 168 | 169 | public static function QuoteXPath($value) { 170 | $contains_single_quote = strpos($value, "'") !== false; 171 | $contains_double_quote = strpos($value, '"') !== false; 172 | if (!$contains_single_quote) { 173 | return "'" . $value . "'"; 174 | } else if (!$contains_double_quote) { 175 | return '"' . $value . '"'; 176 | } else { 177 | $parts = preg_split("/'/", $value); 178 | return "concat('" . implode("', \"'\", '", $parts) . "')"; 179 | } 180 | } 181 | 182 | // Converts a CSS color to the form FFFFFF 183 | public static function CanonicalizeCSSColor($color) { 184 | $color = strtolower(trim($color)); 185 | $rgb = array(); 186 | $css_colors = array( 187 | 'black' => '000000', 188 | 'silver' => 'C0C0C0', 189 | 'gray' => '808080', 190 | 'white' => 'FFFFFF', 191 | 'maroon' => '800000', 192 | 'red' => 'FF0000', 193 | 'purple' => '800080', 194 | 'fuchsia' => 'FF00FF', 195 | 'green' => '008000', 196 | 'lime' => '00FF00', 197 | 'olive' => '808000', 198 | 'yellow' => 'FFFF00', 199 | 'navy' => '000080', 200 | 'blue' => '0000FF', 201 | 'teal' => '008080', 202 | 'aqua' => '00FFFF', 203 | ); 204 | if (preg_match('/^rgb\(([0-9]+),\s*([0-9]+),\s*([0-9]+)\)$/', $color, $rgb) || 205 | preg_match('/^rgba\(([0-9]+),\s*([0-9]+),\s*([0-9]+),\s*([0-9\.]+)\)$/', $color, $rgb)) { 206 | // rgb(255, 255, 255) -> ffffff 207 | // rgba(255, 255, 255, 0.5) -> ffffff 208 | if (0 <= $rgb[1] && $rgb[1] <= 255 && 0 <= $rgb[2] && $rgb[2] <= 255 && 0 <= $rgb[3] && $rgb[3] <= 255) { 209 | $six_hex = sprintf('%02X%02X%02X', $rgb[1], $rgb[2], $rgb[3]); 210 | } 211 | } else if (preg_match('/^#?([0-9a-f])([0-9a-f])([0-9a-f])$/', $color, $rgb)) { 212 | // #fff -> ffffff 213 | $six_hex = $rgb[1] . $rgb[1] . $rgb[2] . $rgb[2] . $rgb[3] . $rgb[3]; 214 | } else if (preg_match('/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/', $color, $rgb)) { 215 | // #ffffff -> ffffff 216 | $six_hex = $rgb[1] . $rgb[2] . $rgb[3]; 217 | } else if (isset($css_colors[$color])) { 218 | // white -> FFFFFF 219 | $six_hex = $css_colors[$color]; 220 | } 221 | PHPUnit_Framework_Assert::assertNotNull($six_hex, "Cannont canonicalize $color"); 222 | return strtoupper($six_hex); 223 | } 224 | 225 | public static function GetJSONValue($curl_response, $attribute = null) { 226 | PHPUnit_Framework_Assert::assertArrayHasKey('body', $curl_response, "Response had no body\n{$curl_response['header']}"); 227 | $array = json_decode(trim($curl_response['body']), true); 228 | PHPUnit_Framework_Assert::assertNotNull($array, "Body could not be decoded as JSON\n{$curl_response['body']}"); 229 | PHPUnit_Framework_Assert::assertArrayHasKey('value', $array, "JSON had no value\n" . print_r($array, true)); 230 | if ($attribute === null) { 231 | $rv = $array["value"]; 232 | } else { 233 | if (isset($array["value"][$attribute])) { 234 | $rv = $array["value"][$attribute]; 235 | } else if (is_array($array["value"])) { 236 | $rv = array(); 237 | foreach ($array["value"] as $a_value) { 238 | PHPUnit_Framework_Assert::assertArrayHasKey($attribute, $a_value, "JSON value did not have attribute $attribute\n" . print_r($array, true)); 239 | $rv[] = $a_value[$attribute]; 240 | } 241 | } 242 | PHPUnit_Framework_Assert::assertNotNull($rv, "JSON value did not have attribute $attribute\n" . print_r($array["value"], true)); 243 | } 244 | return $rv; 245 | } 246 | 247 | public static function LogDebug() { 248 | if (defined('kFestDebug') && kFestDebug) { 249 | $non_null = array_filter(func_get_args()); 250 | $strings = 0; 251 | foreach ($non_null as $argument) { 252 | if (is_string($argument)) { 253 | $strings++; 254 | } 255 | } 256 | if ($strings == sizeof($non_null)) { 257 | echo implode(" - ", $non_null) . "\n"; 258 | } else { 259 | print_r(func_get_args()); 260 | } 261 | } 262 | } 263 | } -------------------------------------------------------------------------------- /WebDriver/Driver.php: -------------------------------------------------------------------------------- 1 | array("Success", "The command executed successfully."), 11 | 6 => array("NoSuchDriver", "A session is either terminated or not started"), 12 | 7 => array("NoSuchElement", "An element could not be located on the page using the given search parameters."), 13 | 8 => array("NoSuchFrame", "A request to switch to a frame could not be satisfied because the frame could not be found."), 14 | 9 => array("UnknownCommand", "The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource."), 15 | 10 => array("StaleElementReference", "An element command failed because the referenced element is no longer attached to the DOM."), 16 | 11 => array("ElementNotVisible", "An element command could not be completed because the element is not visible on the page."), 17 | 12 => array("InvalidElementState", "An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element)."), 18 | 13 => array("UnknownError", "An unknown server-side error occurred while processing the command."), 19 | 15 => array("ElementIsNotSelectable", "An attempt was made to select an element that cannot be selected."), 20 | 17 => array("JavaScriptError", "An error occurred while executing user supplied JavaScript."), 21 | 19 => array("XPathLookupError", "An error occurred while searching for an element by XPath."), 22 | 21 => array("Timeout", "An operation did not complete before its timeout expired."), 23 | 23 => array("NoSuchWindow", "A request to switch to a different window could not be satisfied because the window could not be found."), 24 | 24 => array("InvalidCookieDomain", "An illegal attempt was made to set a cookie under a different domain than the current page."), 25 | 25 => array("UnableToSetCookie", "A request to set a cookie's value could not be satisfied."), 26 | 26 => array("UnexpectedAlertOpen", "A modal dialog was open, blocking this operation"), 27 | 27 => array("NoAlertOpenError", "An attempt was made to operate on a modal dialog when one was not open."), 28 | 28 => array("ScriptTimeout", "A script did not complete before its timeout expired."), 29 | 29 => array("InvalidElementCoordinates", "The coordinates provided to an interactions operation are invalid."), 30 | 30 => array("IMENotAvailable", "IME was not available."), 31 | 31 => array("IMEEngineActivationFailed", "An IME engine could not be started."), 32 | 32 => array("InvalidSelector", "Argument was an invalid selector (e.g. XPath/CSS)."), 33 | 33 => array("SessionNotCreatedException", "A new session could not be created."), 34 | 34 => array("MoveTargetOutOfBounds", "Target provided for a move action is out of bounds."), 35 | ); 36 | 37 | protected function __construct($server_url, $capabilities) { 38 | $this->server_url = $server_url; 39 | $this->browser = $capabilities['browserName']; 40 | 41 | $payload = array("desiredCapabilities" => $capabilities); 42 | $response = $this->execute("POST", "/session", $payload); 43 | 44 | // Parse out session id for Selenium <= 2.33.0 45 | $matches = array(); 46 | preg_match("/Location:.*\/(.*)/", $response['header'], $matches); 47 | if (count($matches) === 2) { 48 | $this->session_id = trim($matches[1]); 49 | } 50 | if (!$this->session_id) { 51 | // Starting with Selenium 2.34.0, the session id is in the body instead 52 | if (isset($response['body'])) { 53 | $capabilities = json_decode(trim($response['body']), true); 54 | if ($capabilities !== null && isset($capabilities['sessionId'])) { 55 | $this->session_id = $capabilities['sessionId']; 56 | } 57 | } 58 | } 59 | if (!$this->session_id) { 60 | // The Chrome driver returns the session id in the value array 61 | $this->session_id = WebDriver::GetJSONValue($response, "webdriver.remote.sessionid"); 62 | } 63 | PHPUnit_Framework_Assert::assertNotNull($this->session_id, "Did not get a session id from $server_url\n" . print_r($response, true)); 64 | } 65 | 66 | public static function InitAtSauce($sauce_username, $sauce_key, $os, $browser, $version = false, $additional_options = array()) { 67 | $capabilities = array_merge(array( 68 | 'javascriptEnabled' => true, 69 | 'platform' => strtoupper($os), 70 | 'browserName' => $browser, 71 | ), $additional_options); 72 | if ($version) { 73 | $capabilities["version"] = $version; 74 | } 75 | return new WebDriver_Driver("http://" . $sauce_username . ":" . $sauce_key . "@ondemand.saucelabs.com:80/wd/hub", $capabilities); 76 | } 77 | 78 | public static function InitAtBrowserStack($browserstack_username, $browserstack_value, $os, $browser, $version = false, $additional_options = array(), $starting_time = null, $attempt = 1) { 79 | if (!$starting_time) { 80 | $starting_time = time(); 81 | } 82 | try { 83 | $capabilities = array_merge(array( 84 | 'browserstack.debug' => true, 85 | 'platform' => strtoupper($os), 86 | 'browserName' => $browser 87 | ), $additional_options); 88 | if ($version) { 89 | $capabilities["version"] = $version; 90 | } 91 | return new WebDriver_Driver("http://" . $browserstack_username . ":" . $browserstack_value . "@hub.browserstack.com/wd/hub", $capabilities); 92 | } catch (WebDriver_OverParallelLimitException $e) { 93 | PHPUnit_Framework_Assert::assertTrue(time() < $starting_time + WebDriver::$BrowserStackMaxSeconds, "Reached maximum BrowserStack attempt seconds: " . WebDriver::$BrowserStackMaxSeconds); 94 | PHPUnit_Framework_Assert::assertTrue($attempt < WebDriver::$BrowserStackMaxAttempts, "Reached maximum BrowserStack attempt number: " . WebDriver::$BrowserStackMaxAttempts); 95 | sleep(WebDriver::$BrowserStackWaitSeconds); 96 | return WebDriver_Driver::InitAtBrowserStack($browserstack_username, $browserstack_value, $os, $browser, $version, $additional_options, $starting_time, $attempt + 1); 97 | } 98 | } 99 | 100 | public static function InitAtTestingBot($testingbot_apikey, $testbot_secret, $os, $browser, $version = false, $additional_options = array()) { 101 | $capabilities = array_merge(array( 102 | 'platform' => strtoupper($os), 103 | 'browserName' => $browser 104 | ), $additional_options); 105 | if ($version) { 106 | $capabilities["version"] = $version; 107 | } 108 | return new WebDriver_Driver("http://" . $testingbot_apikey . ":" . $testbot_secret . "@hub.testingbot.com:4444/wd/hub", $capabilities); 109 | } 110 | 111 | public static function InitAtHost($host, $port, $browser, $additional_options = array()) { 112 | $capabilities = array_merge(array( 113 | 'javascriptEnabled' => true, 114 | 'browserName' => $browser, 115 | ), $additional_options); 116 | if (strcasecmp($browser, "iphone") == 0 || strcasecmp($browser, "android") == 0) { 117 | return new WebDriver_Driver("http://$host:$port/hub", $capabilities); 118 | } else { 119 | return new WebDriver_Driver("http://$host:$port/wd/hub", $capabilities); 120 | } 121 | } 122 | 123 | public static function InitAtLocal($port, $browser, $additional_options = array()) { 124 | return self::InitAtHost('localhost', $port, $browser, $additional_options); 125 | } 126 | 127 | public function get_browser() { 128 | return $this->browser; 129 | } 130 | 131 | public function running_at_sauce() { 132 | return (strpos($this->server_url, "saucelabs.com") !== false); 133 | } 134 | 135 | public function running_at_browserstack() { 136 | return (strpos($this->server_url, "browserstack.com") !== false); 137 | } 138 | 139 | public function running_at_testingbot() { 140 | return (strpos($this->server_url, "testingbot.com") !== false); 141 | } 142 | 143 | public function sauce_url() { 144 | if ($this->running_at_sauce()) { 145 | return "https://saucelabs.com/jobs/{$this->session_id}"; 146 | } else { 147 | return false; 148 | } 149 | } 150 | 151 | public function browserstack_info() { 152 | if ($this->running_at_browserstack()) { 153 | $url_parts = parse_url($this->server_url); 154 | $response = WebDriver::Curl("GET", "https://" . $url_parts['user'] . ":" . $url_parts['pass'] . "@www.browserstack.com/automate/sessions/" . $this->session_id . ".json"); 155 | $result = json_decode(trim($response['body']), true); 156 | return $result['automation_session']; 157 | } else { 158 | return false; 159 | } 160 | } 161 | 162 | public function execute($http_type, $relative_url, $payload = null) { 163 | if ($payload !== null) { 164 | $payload = json_encode($payload); 165 | } 166 | $relative_url = str_replace(':sessionId', $this->session_id, $relative_url); 167 | $full_url = $this->server_url . $relative_url; 168 | $response = WebDriver::Curl($http_type, $full_url, $payload); 169 | if (isset($response['body'])) { 170 | $command_info = $http_type . " - " . $full_url . " - " . print_r($payload, true); 171 | $response_json = json_decode(trim($response['body']), true); 172 | if (!is_null($response_json)) { 173 | $response_status_code = $response_json["status"]; 174 | PHPUnit_Framework_Assert::assertArrayHasKey($response_status_code, self::$status_codes, "Unknown status code $response_status_code returned from server.\n{$response['body']}"); 175 | $response_info = $response_status_code . " - " . self::$status_codes[$response_status_code][0] . " - " . self::$status_codes[$response_status_code][1]; 176 | $additional_info = isset($response_json['value']['message']) ? "Message: " . $response_json['value']['message'] : "Response: " . $response['body']; 177 | if ($response_status_code == 7) { 178 | throw new WebDriver_NoSuchElementException("Could not find element: " . print_r($payload, true)); 179 | } 180 | if ($response_status_code == 10) { 181 | throw new WebDriver_StaleElementReferenceException(); 182 | } 183 | if ($response_status_code == 11) { 184 | throw new WebDriver_ElementNotVisibleException($command_info); 185 | } 186 | if ($response_status_code == 13 && strpos($response_json['value']['message'], 'Please upgrade to add more parallel sessions') !== false) { 187 | throw new WebDriver_OverParallelLimitException(); 188 | } 189 | PHPUnit_Framework_Assert::assertEquals(0, $response_status_code, "Unsuccessful WebDriver command: $response_info\nCommand: $command_info\n$additional_info"); 190 | } 191 | } 192 | return $response; 193 | } 194 | 195 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId 196 | public function quit() { 197 | $this->execute("DELETE", "/session/:sessionId"); 198 | } 199 | 200 | /******************************************************************** 201 | * Getters 202 | */ 203 | 204 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/status 205 | public function get_server_status() { 206 | $response = $this->execute("GET", "/status"); 207 | return WebDriver::GetJSONValue($response); 208 | } 209 | 210 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/sessions 211 | public function get_all_sessions() { 212 | $response = $this->execute("GET", "/sessions"); 213 | return WebDriver::GetJSONValue($response); 214 | } 215 | 216 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId 217 | public function get_capabilities() { 218 | $response = $this->execute("GET", "/session/:sessionId"); 219 | return WebDriver::GetJSONValue($response); 220 | } 221 | 222 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/url 223 | public function get_url() { 224 | $response = $this->execute("GET", "/session/:sessionId/url"); 225 | return WebDriver::GetJSONValue($response); 226 | } 227 | 228 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/title 229 | public function get_title() { 230 | $response = $this->execute("GET", "/session/:sessionId/title"); 231 | return WebDriver::GetJSONValue($response); 232 | } 233 | 234 | public function is_page_loaded() { 235 | $state = $this->execute_js_sync("return document.readyState"); 236 | return $state == "complete"; 237 | } 238 | 239 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/source 240 | public function get_source() { 241 | $response = $this->execute("GET", "/session/:sessionId/source"); 242 | return WebDriver::GetJSONValue($response); 243 | } 244 | 245 | public function get_text() { 246 | return $this->get_element("tag name=body")->get_text(); 247 | } 248 | 249 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/screenshot 250 | public function get_screenshot() { 251 | $response = $this->execute("GET", "/session/:sessionId/screenshot"); 252 | $base64_encoded_png = WebDriver::GetJSONValue($response); 253 | return base64_decode($base64_encoded_png); 254 | } 255 | 256 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/ime/available_engines 257 | public function get_all_ime_engines() { 258 | $response = $this->execute("GET", "/session/:sessionId/ime/available_engines"); 259 | return WebDriver::GetJSONValue($response); 260 | } 261 | 262 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/ime/active_engine 263 | public function get_ime_engine() { 264 | $response = $this->execute("GET", "/session/:sessionId/ime/active_engine"); 265 | return WebDriver::GetJSONValue($response); 266 | } 267 | 268 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/ime/activated 269 | public function is_ime_active() { 270 | $response = $this->execute("GET", "/session/:sessionId/ime/activated"); 271 | return WebDriver::GetJSONValue($response); 272 | } 273 | 274 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element 275 | public function get_element($locator) { 276 | $payload = WebDriver::ParseLocator($locator); 277 | $response = $this->execute("POST", "/session/:sessionId/element", $payload); 278 | $element_id = WebDriver::GetJSONValue($response, "ELEMENT"); 279 | return new WebDriver_WebElement($this, $element_id, $locator); 280 | } 281 | 282 | // WebDriver can do implicit waits for AJAX elements, but sometimes you need explicit reloads 283 | // Note: is_element_present() will use the wait time, if any, that you've set with set_implicit_wait() 284 | public function get_element_reload($locator, $max_wait_minutes = 2) { 285 | $end_time = time() + $max_wait_minutes * 60; 286 | do { 287 | $this->reload(); 288 | $present = $this->is_element_present($locator); 289 | } while (time() < $end_time && !$present); 290 | return $this->get_element($locator); 291 | } 292 | 293 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/elements 294 | public function get_all_elements($locator) { 295 | $payload = WebDriver::ParseLocator($locator); 296 | $response = $this->execute("POST", "/session/:sessionId/elements", $payload); 297 | $element_ids = WebDriver::GetJSONValue($response, "ELEMENT"); 298 | $elements = array(); 299 | foreach ($element_ids as $element_id) { 300 | $elements[] = new WebDriver_WebElement($this, $element_id, $locator); 301 | } 302 | return $elements; 303 | } 304 | 305 | public function get_element_count($locator) { 306 | return count($this->get_all_elements($locator)); 307 | } 308 | 309 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/active 310 | public function get_active_element() { 311 | $response = $this->execute("POST", "/session/:sessionId/element/active"); 312 | $element_id = WebDriver::GetJSONValue($response, "ELEMENT"); 313 | return new WebDriver_WebElement($this, $element_id, "active=true"); 314 | } 315 | 316 | public function is_element_present($locator) { 317 | try { 318 | $element = $this->get_element($locator); 319 | if ($this->browser !== 'android' && $this->browser !== 'chrome') { // The android driver fails at this & chrome driver 2.0 does not implement this 320 | $element->describe(); // Under certain conditions get_element returns cached information. This tests if the element is actually there. 321 | } 322 | $is_element_present = true; 323 | } catch (WebDriver_NoSuchElementException $e) { 324 | $is_element_present = false; 325 | } 326 | return $is_element_present; 327 | } 328 | 329 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window_handle 330 | public function get_window_handle() { 331 | $response = $this->execute("GET", "/session/:sessionId/window_handle"); 332 | return WebDriver::GetJSONValue($response); 333 | } 334 | 335 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window_handles 336 | public function get_all_window_handles() { 337 | $response = $this->execute("GET", "/session/:sessionId/window_handles"); 338 | return WebDriver::GetJSONValue($response); 339 | } 340 | 341 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window/:windowHandle/size 342 | public function get_window_size($window_handle = "current") { 343 | $response = $this->execute("GET", "/session/:sessionId/window/{$window_handle}/size"); 344 | return WebDriver::GetJSONValue($response); 345 | } 346 | 347 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window/:windowHandle/position 348 | public function get_window_position($window_handle = "current") { 349 | $response = $this->execute("GET", "/session/:sessionId/window/{$window_handle}/position"); 350 | return WebDriver::GetJSONValue($response); 351 | } 352 | 353 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/cookie 354 | public function get_all_cookies() { 355 | $response = $this->execute("GET", "/session/:sessionId/cookie"); 356 | return WebDriver::GetJSONValue($response); 357 | } 358 | 359 | public function get_cookie($name, $property = null) { 360 | $all_cookies = $this->get_all_cookies(); 361 | foreach ($all_cookies as $cookie) { 362 | if ($cookie['name'] == $name) { 363 | if (is_null($property)) { 364 | return $cookie; 365 | } else { 366 | return $cookie[$property]; 367 | } 368 | } 369 | } 370 | } 371 | 372 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/orientation 373 | private function get_orientation() { 374 | $response = $this->execute("GET", "/session/:sessionId/orientation"); 375 | return WebDriver::GetJSONValue($response); 376 | } 377 | public function is_landscape() { return $this->get_orientation() == "LANDSCAPE"; } 378 | public function is_portrait() { return $this->get_orientation() == "PORTRAIT"; } 379 | 380 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/alert_text 381 | public function get_alert_text() { 382 | $response = $this->execute("GET", "/session/:sessionId/alert_text"); 383 | return WebDriver::GetJSONValue($response); 384 | } 385 | 386 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/log 387 | public function get_log($type) { 388 | $payload = array("type" => $type); 389 | $response = $this->execute("POST", "/session/:sessionId/log", $payload); 390 | return WebDriver::GetJSONValue($response); 391 | } 392 | 393 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/log/types 394 | public function get_log_types() { 395 | $response = $this->execute("GET", "/session/:sessionId/log/types"); 396 | return WebDriver::GetJSONValue($response); 397 | } 398 | 399 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/application_cache/status 400 | public function get_cache_status_code() { 401 | $response = $this->execute("GET", "/session/:sessionId/application_cache/status"); 402 | return WebDriver::GetJSONValue($response); 403 | } 404 | 405 | public function get_cache_status_string() { 406 | $status_code = $this->get_cache_status_code(); 407 | $status_strings = array( 408 | 0 => 'UNCACHED', 409 | 1 => 'IDLE', 410 | 2 => 'CHECKING', 411 | 3 => 'DOWNLOADING', 412 | 4 => 'UPDATE_READY', 413 | 5 => 'OBSOLETE', 414 | ); 415 | return $status_strings[$status_code]; 416 | } 417 | 418 | // See http://testingbot.com/support/api#singletest 419 | public function get_testingbot_info() { 420 | $response = WebDriver::Curl("GET", "https://" . kTestingBotAPIKey . "@api.testingbot.com/v1/tests/" . $this->session_id); 421 | return json_decode(trim($response['body']), true); 422 | } 423 | 424 | /******************************************************************** 425 | * Setters 426 | */ 427 | 428 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/timeouts 429 | public function set_timeout($timeout_type, $milliseconds) { 430 | $acceptable_timeout_types = array("script", "implicit", "page load"); 431 | PHPUnit_Framework_Assert::assertTrue(in_array($timeout_type, $acceptable_timeout_types), "First argument must be one of: " . implode(", ", $acceptable_timeout_types)); 432 | $payload = array("type" => $timeout_type, "ms" => $milliseconds); 433 | $this->execute("POST", "/session/:sessionId/timeouts", $payload); 434 | } 435 | 436 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/timeouts/async_script 437 | public function set_async_timeout($milliseconds) { 438 | $payload = array("ms" => $milliseconds); 439 | $this->execute("POST", "/session/:sessionId/timeouts/async_script", $payload); 440 | } 441 | 442 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/timeouts/implicit_wait 443 | public function set_implicit_wait($milliseconds) { 444 | WebDriver::$ImplicitWaitMS = $milliseconds; 445 | $payload = array("ms" => $milliseconds); 446 | $this->execute("POST", "/session/:sessionId/timeouts/implicit_wait", $payload); 447 | } 448 | 449 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/url 450 | public function load($url) { 451 | $payload = array("url" => $url); 452 | $this->execute("POST", "/session/:sessionId/url", $payload); 453 | } 454 | 455 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/forward 456 | public function go_forward() { 457 | $this->execute("POST", "/session/:sessionId/forward"); 458 | } 459 | 460 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/back 461 | public function go_back() { 462 | $this->execute("POST", "/session/:sessionId/back"); 463 | } 464 | 465 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/refresh 466 | public function reload() { 467 | $this->execute("POST", "/session/:sessionId/refresh"); 468 | } 469 | 470 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window 471 | // IE appends the anchor tag to the window title, but only when it's done loading 472 | // Example: $driver->select_window("My Cool Page", "#chapter7") finds the window called "My Cool Page#chapter7" or "My Cool Page" in IE, and "My Cool Page" in all other browsers 473 | public function select_window($window_title, $ie_hash = '') { 474 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 475 | $all_window_handles = $this->get_all_window_handles(); 476 | $all_titles = array(); 477 | $found_window = false; 478 | do { 479 | for ($i = 0; $i < count($all_window_handles); $i++) { 480 | $payload = array("name" => $all_window_handles[$i]); 481 | $this->execute("POST", "/session/:sessionId/window", $payload); 482 | $current_title = $this->get_title(); 483 | $all_titles[$i] = $current_title; 484 | if ($current_title == $window_title || ($this->browser == 'internet explorer' && $current_title == $window_title . $ie_hash)) { 485 | $found_window = true; 486 | break; 487 | } 488 | } 489 | } while (time() < $end_time && !$found_window); 490 | PHPUnit_Framework_Assert::assertTrue($found_window, "Could not find window with title <$window_title> and optional hash <$ie_hash>. Found " . count($all_titles) . " windows: " . implode("; ", $all_titles)); 491 | } 492 | 493 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window 494 | public function close_window() { 495 | $this->execute("DELETE", "/session/:sessionId/window"); 496 | } 497 | 498 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window/:windowHandle/size 499 | public function set_window_size($new_width, $new_height, $window_handle = "current") { 500 | $payload = array( 501 | "width" => $new_width, 502 | "height" => $new_height 503 | ); 504 | $this->execute("POST", "/session/:sessionId/window/{$window_handle}/size", $payload); 505 | } 506 | 507 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window/:windowHandle/position 508 | public function set_window_position($new_x, $new_y, $window_handle = "current") { 509 | $payload = array( 510 | "x" => $new_x, 511 | "y" => $new_y 512 | ); 513 | $this->execute("POST", "/session/:sessionId/window/{$window_handle}/position", $payload); 514 | } 515 | 516 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/window/:windowHandle/maximize 517 | public function maximize_window($window_handle = "current") { 518 | $this->execute("POST", "/session/:sessionId/window/{$window_handle}/maximize"); 519 | } 520 | 521 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/ime/deactivate 522 | public function deactivate_ime() { 523 | $this->execute("POST", "/session/:sessionId/ime/deactivate"); 524 | } 525 | 526 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/ime/activate 527 | public function activate_ime() { 528 | $this->execute("POST", "/session/:sessionId/ime/activate"); 529 | } 530 | 531 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/frame 532 | public function select_frame($identifier = null) { 533 | if ($identifier !== null) { 534 | $this->get_element($identifier); // POST /session/:sessionId/frame does not use implicit wait but POST /session/:sessionId/element does 535 | } 536 | $payload = array("id" => $identifier); 537 | $this->execute("POST", "/session/:sessionId/frame", $payload); 538 | } 539 | 540 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/cookie 541 | public function set_cookie($name, $value, $path = null, $domain = null, $secure = false, $expiry = null) { 542 | $payload = array( 543 | 'cookie' => array( 544 | 'name' => $name, 545 | 'value' => $value, 546 | 'secure' => $secure, // The documentation says this is optional, but selenium server 2.0b1 throws a NullPointerException if it's not provided 547 | ) 548 | ); 549 | if (!is_null($path)) { 550 | $payload['cookie']['path'] = $path; 551 | } 552 | if (!is_null($domain)) { 553 | $payload['cookie']['domain'] = $domain; 554 | } 555 | if (!is_null($expiry)) { 556 | $payload['cookie']['expiry'] = $expiry; 557 | } 558 | $this->execute("POST", "/session/:sessionId/cookie", $payload); 559 | } 560 | 561 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/cookie 562 | public function delete_all_cookies() { 563 | $this->execute("DELETE", "/session/:sessionId/cookie"); 564 | } 565 | 566 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/cookie/:name 567 | public function delete_cookie($name) { 568 | $this->execute("DELETE", "/session/:sessionId/cookie/" . $name); 569 | } 570 | 571 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/execute 572 | public function execute_js_sync($javascript, $arguments = array()) { 573 | $payload = array( 574 | "script" => $javascript, 575 | "args" => $arguments, 576 | ); 577 | $response = $this->execute("POST", "/session/:sessionId/execute", $payload); 578 | return WebDriver::GetJSONValue($response); 579 | } 580 | 581 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/execute_async 582 | public function execute_js_async($javascript, $arguments = array()) { 583 | $payload = array( 584 | "script" => $javascript, 585 | "args" => $arguments, 586 | ); 587 | $response = $this->execute("POST", "/session/:sessionId/execute_async", $payload); 588 | return WebDriver::GetJSONValue($response); 589 | } 590 | 591 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/keys 592 | public function send_keys($keys) { 593 | $payload = array("value" => preg_split('//u', $keys, -1, PREG_SPLIT_NO_EMPTY)); 594 | $this->execute("POST", "/session/:sessionId/keys", $payload); 595 | } 596 | 597 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/orientation 598 | private function set_orientation($new_orientation) { 599 | $payload = array("orientation", $new_orientation); 600 | $this->execute("POST", "/session/:sessionId/orientation", $payload); 601 | } 602 | public function rotate_landscape() { $this->set_orientation("LANDSCAPE"); } 603 | public function rotate_portrait() { $this->set_orientation("PORTRAIT"); } 604 | 605 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/moveto 606 | public function move_cursor($right, $down) { 607 | $payload = array( 608 | "xoffset" => $right, 609 | "yoffset" => $down 610 | ); 611 | $this->execute("POST", "/session/:sessionId/moveto", $payload); 612 | } 613 | 614 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/click 615 | private function click_mouse($button) { 616 | $payload = array("button" => $button); 617 | $this->execute("POST", "/session/:sessionId/click", $payload); 618 | } 619 | public function click() { $this->click_mouse(0); } 620 | public function middle_click() { $this->click_mouse(1); } 621 | public function right_click() { $this->click_mouse(2); } 622 | 623 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/buttondown 624 | public function click_and_hold() { 625 | $this->execute("POST", "/session/:sessionId/buttondown"); 626 | } 627 | 628 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/buttonup 629 | public function release_click() { 630 | $this->execute("POST", "/session/:sessionId/buttonup"); 631 | } 632 | 633 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/doubleclick 634 | public function double_click() { 635 | $this->execute("POST", "/session/:sessionId/doubleclick"); 636 | } 637 | 638 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/alert_text 639 | public function type_alert($text) { 640 | $payload = array("text" => $text); 641 | $this->execute("POST", "/session/:sessionId/alert_text", $payload); 642 | } 643 | 644 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/accept_alert 645 | public function accept_alert() { 646 | $this->execute("POST", "/session/:sessionId/accept_alert"); 647 | } 648 | 649 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/dismiss_alert 650 | public function dismiss_alert() { 651 | $this->execute("POST", "/session/:sessionId/dismiss_alert"); 652 | } 653 | 654 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/touch/click 655 | public function single_tap($element_id) { 656 | $payload = array("element" => $element_id); 657 | $this->execute("POST", "/session/:sessionId/touch/click", $payload); 658 | } 659 | 660 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/touch/doubleclick 661 | public function double_tap($element_id) { 662 | $payload = array("element" => $element_id); 663 | $this->execute("POST", "/session/:sessionId/touch/doubleclick", $payload); 664 | } 665 | 666 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/touch/longclick 667 | public function long_tap($element_id) { 668 | $payload = array("element" => $element_id); 669 | $this->execute("POST", "/session/:sessionId/touch/longclick", $payload); 670 | } 671 | 672 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/touch/down 673 | public function touch_down($x_coordinate, $y_coordinate) { 674 | $payload = array( 675 | "x" => $x_coordinate, 676 | "y" => $y_coordinate, 677 | ); 678 | $this->execute("POST", "/session/:sessionId/touch/down", $payload); 679 | } 680 | 681 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/touch/up 682 | public function touch_up($x_coordinate, $y_coordinate) { 683 | $payload = array( 684 | "x" => $x_coordinate, 685 | "y" => $y_coordinate, 686 | ); 687 | $this->execute("POST", "/session/:sessionId/touch/up", $payload); 688 | } 689 | 690 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/touch/move 691 | public function touch_move($x_coordinate, $y_coordinate) { 692 | $payload = array( 693 | "x" => $x_coordinate, 694 | "y" => $y_coordinate, 695 | ); 696 | $this->execute("POST", "/session/:sessionId/touch/move", $payload); 697 | } 698 | 699 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/touch/scroll 700 | public function touch_scroll_at($start_element_id, $pixels_offset_x, $pixels_offset_y) { 701 | $payload = array( 702 | "element" => $start_element_id, 703 | "xOffset" => $pixels_offset_x, 704 | "yOffset" => $pixels_offset_y, 705 | ); 706 | $this->execute("POST", "/session/:sessionId/touch/scroll", $payload); 707 | } 708 | 709 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/touch/scroll 710 | public function touch_scroll($pixels_offset_x, $pixels_offset_y) { 711 | $payload = array( 712 | "xOffset" => $pixels_offset_x, 713 | "yOffset" => $pixels_offset_y, 714 | ); 715 | $this->execute("POST", "/session/:sessionId/touch/scroll", $payload); 716 | } 717 | 718 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#session/:sessionId/touch/flick 719 | public function touch_flick_at($start_element_id, $pixels_offset_x, $pixels_offset_y, $pixels_per_second) { 720 | $payload = array( 721 | "element" => $start_element_id, 722 | "xOffset" => $pixels_offset_x, 723 | "yOffset" => $pixels_offset_y, 724 | "speed" => $pixels_per_second, 725 | ); 726 | $this->execute("POST", "/session/:sessionId/touch/flick", $payload); 727 | } 728 | 729 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#session/:sessionId/touch/flick 730 | public function touch_flick($pixels_per_second_x, $pixels_per_second_y) { 731 | $payload = array( 732 | "xSpeed" => $pixels_per_second_x, 733 | "ySpeed" => $pixels_per_second_y, 734 | ); 735 | $this->execute("POST", "/session/:sessionId/touch/flick", $payload); 736 | } 737 | 738 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/location 739 | public function get_geographical_location() { 740 | $response = $this->execute("GET", "/session/:sessionId/location"); 741 | return WebDriver::GetJSONValue($response); 742 | } 743 | 744 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/location 745 | public function set_geographical_location($new_latitude, $new_longitude, $new_altitude) { 746 | $payload = array( 747 | 'latitude' => $new_latitude, 748 | 'longitude' => $new_longitude, 749 | 'altitude' => $new_altitude 750 | ); 751 | $this->execute("POST", "/session/:sessionId/location", $payload); 752 | } 753 | 754 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/local_storage 755 | public function get_all_local_storage_keys() { 756 | $response = $this->execute("GET", "/session/:sessionId/local_storage"); 757 | return WebDriver::GetJSONValue($response); 758 | } 759 | 760 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/local_storage 761 | public function set_local_storage_item($key, $new_value) { 762 | $payload = array( 763 | 'key' => $key, 764 | 'value' => $new_value 765 | ); 766 | $this->execute("POST", "/session/:sessionId/local_storage", $payload); 767 | } 768 | 769 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/local_storage 770 | public function delete_all_local_storage() { 771 | $this->execute("DELETE", "/session/:sessionId/local_storage"); 772 | } 773 | 774 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/local_storage/key/:key 775 | public function get_local_storage_item($key) { 776 | $response = $this->execute("GET", "/session/:sessionId/local_storage/key/{$key}"); 777 | return WebDriver::GetJSONValue($response); 778 | } 779 | 780 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/local_storage/key/:key 781 | public function delete_local_storage_item($key) { 782 | $this->execute("DELETE", "/session/:sessionId/local_storage/key/{$key}"); 783 | } 784 | 785 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/local_storage/size 786 | public function get_local_storage_count() { 787 | $response = $this->execute("GET", "/session/:sessionId/local_storage/size"); 788 | return WebDriver::GetJSONValue($response); 789 | } 790 | 791 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/session_storage 792 | public function get_all_session_storage_keys() { 793 | $response = $this->execute("GET", "/session/:sessionId/session_storage"); 794 | return WebDriver::GetJSONValue($response); 795 | } 796 | 797 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/session_storage 798 | public function set_session_storage_item($key, $new_value) { 799 | $payload = array( 800 | 'key' => $key, 801 | 'value' => $new_value 802 | ); 803 | $this->execute("POST", "/session/:sessionId/session_storage", $payload); 804 | } 805 | 806 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/session_storage 807 | public function delete_all_session_storage() { 808 | $this->execute("DELETE", "/session/:sessionId/session_storage"); 809 | } 810 | 811 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/session_storage/key/:key 812 | public function get_session_storage_item($key) { 813 | $response = $this->execute("GET", "/session/:sessionId/session_storage/key/{$key}"); 814 | return WebDriver::GetJSONValue($response); 815 | } 816 | 817 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/session_storage/key/:key 818 | public function delete_session_storage_item($key) { 819 | $this->execute("DELETE", "/session/:sessionId/session_storage/key/{$key}"); 820 | } 821 | 822 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/session_storage/size 823 | public function get_session_storage_count() { 824 | $response = $this->execute("GET", "/session/:sessionId/session_storage/size"); 825 | return WebDriver::GetJSONValue($response); 826 | } 827 | 828 | // See https://saucelabs.com/docs/sauce-ondemand#alternative-annotation-methods 829 | public function set_sauce_context($field, $value) { 830 | if ($this->running_at_sauce()) { 831 | $payload = json_encode(array($field => $value)); 832 | $url_parts = parse_url($this->server_url); 833 | WebDriver::Curl("PUT", "http://" . $url_parts['user'] . ":" . $url_parts['pass'] . "@saucelabs.com/rest/v1/" . $url_parts['user'] . "/jobs/" . $this->session_id, $payload); 834 | } 835 | } 836 | 837 | // See https://www.browserstack.com/automate/rest-api#rest-api-sessions 838 | public function set_browserstack_status($status, $reason = "") { 839 | if (!in_array($status, array("completed", "error"))) { 840 | throw new Exception("Status must be 'completed' or 'error', not '$status'"); 841 | } 842 | if($this->running_at_browserstack()) { 843 | $payload = json_encode(array( 844 | 'status' => $status, 845 | 'reason' => $reason 846 | )); 847 | $url_parts = parse_url($this->server_url); 848 | WebDriver::Curl("PUT", "https://" . $url_parts['user'] . ":" . $url_parts['pass'] . "@www.browserstack.com/automate/sessions/" . $this->session_id . ".json", $payload); 849 | } 850 | } 851 | 852 | // See http://testingbot.com/support/api 853 | public function set_testingbot_info($field, $value) { 854 | if ($this->running_at_testingbot()) { 855 | $payload = "test[$field]=$value"; 856 | $url_parts = parse_url($this->server_url); 857 | WebDriver::Curl("PUT", "https://" . $url_parts['user'] . "api.testingbot.com/v2/tests/" . $this->session_id, $payload); 858 | } 859 | } 860 | 861 | /******************************************************************** 862 | * Asserters 863 | */ 864 | 865 | public function assert_url($expected_url) { 866 | $url = WebDriver::WaitUntil(array($this, 'get_url'), array(), $expected_url); 867 | PHPUnit_Framework_Assert::assertEquals($expected_url, $url, "Failed asserting that URL is <$expected_url>."); 868 | } 869 | 870 | // IE appends the anchor tag to the window title, but only when it's done loading 871 | // Example: $driver->assert_title("My Cool Page", "#chapter7") asserts that the page title is "My Cool Page#chapter7" in IE, and "My Cool Page" in all other browsers 872 | // WebDriver does not wait for the page to finish loading before returning the title, so we check repeatedly 873 | public function assert_title($expected_title, $ie_hash = '') { 874 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 875 | do { 876 | $actual_title = $this->get_title(); 877 | $title_matched = ($this->browser == 'internet explorer' && $actual_title == $expected_title . $ie_hash) || ($actual_title == $expected_title); 878 | } while (time() < $end_time && !$title_matched); 879 | PHPUnit_Framework_Assert::assertTrue($title_matched, "Failed asserting that <$actual_title> is <$expected_title> with optional hash <$ie_hash>."); 880 | } 881 | 882 | public function assert_page_loaded() { 883 | $loaded = WebDriver::WaitUntil(array($this, 'is_page_loaded'), array(), true); 884 | PHPUnit_Framework_Assert::assertTrue($loaded, "Failed asserting that page loaded"); 885 | } 886 | 887 | public function assert_element_present($element_locator) { 888 | PHPUnit_Framework_Assert::assertTrue($this->is_element_present($element_locator), "Failed asserting that <$element_locator> is present"); 889 | } 890 | 891 | public function assert_element_not_present($element_locator) { 892 | $implicit_wait = WebDriver::$ImplicitWaitMS; 893 | $this->set_implicit_wait(0); 894 | PHPUnit_Framework_Assert::assertFalse($this->is_element_present($element_locator), "Failed asserting that <$element_locator> is not present"); 895 | $this->set_implicit_wait($implicit_wait); 896 | } 897 | 898 | public function assert_element_count($locator, $expected_count) { 899 | if ($expected_count == 0) { 900 | $this->assert_element_not_present($locator); 901 | } else { 902 | $count = WebDriver::WaitUntil(array($this, 'get_element_count'), array($locator), $expected_count); 903 | PHPUnit_Framework_Assert::assertEquals($expected_count, $count, "Failed asserting that <$locator> appears $expected_count times."); 904 | } 905 | } 906 | 907 | public function assert_string_present($expected_string) { 908 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 909 | do { 910 | $page_text = $this->get_text(); 911 | } while (time() < $end_time && strstr($page_text, $expected_string) === false); 912 | PHPUnit_Framework_Assert::assertContains($expected_string, $page_text, "Failed asserting that page text contains <$expected_string>.\n$page_text"); 913 | } 914 | 915 | public function assert_string_not_present($expected_missing_string) { 916 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 917 | do { 918 | $page_text = $this->get_text(); 919 | } while (time() < $end_time && strstr($page_text, $expected_missing_string) !== false); 920 | PHPUnit_Framework_Assert::assertNotContains($expected_missing_string, $page_text, "Failed asserting that page text does not contain <$expected_missing_string>.\n$page_text"); 921 | } 922 | 923 | public function assert_cookie_value($name, $expected_value) { 924 | $actual_value = WebDriver::WaitUntil(array($this, 'get_cookie'), array($name, 'value'), $expected_value); 925 | PHPUnit_Framework_Assert::assertEquals($expected_value, $actual_value, "Failed asserting that cookie <$name> has value <$expected_value>."); 926 | } 927 | 928 | public function assert_alert_text($expected_text) { 929 | $text = WebDriver::WaitUntil(array($this, 'get_alert_text'), array(), $expected_text); 930 | PHPUnit_Framework_Assert::assertEquals($expected_text, $text, "Failed asserting that alert text is <$expected_text>."); 931 | } 932 | } 933 | -------------------------------------------------------------------------------- /WebDriver/ElementNotVisibleException.php: -------------------------------------------------------------------------------- 1 | command = $command; 8 | $locator_description = strlen($locator) > 0 ? '<' . $locator . '>' : 'this element'; 9 | parent::__construct("Could not interact with {$locator_description} because it is not visible\nCommand: $command"); 10 | } 11 | 12 | public function get_command() { 13 | return $this->command; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /WebDriver/FirefoxProfile.php: -------------------------------------------------------------------------------- 1 | preferences[$key] = $value; 8 | } 9 | 10 | public function get_profile() { 11 | $tmp_filename = tempnam(sys_get_temp_dir(), "webdriver_firefox_profile_"); 12 | 13 | $zip = new ZipArchive(); 14 | $zip->open($tmp_filename, ZIPARCHIVE::CREATE); 15 | $zip->addFromString("prefs.js", $this->get_preferences_file()); 16 | $zip->close(); 17 | 18 | $base64 = base64_encode(file_get_contents($tmp_filename)); 19 | unlink($tmp_filename); 20 | 21 | return $base64; 22 | } 23 | 24 | public function __toString() { 25 | return $this->get_profile; 26 | } 27 | 28 | protected function get_preferences_file() { 29 | $file = ""; 30 | foreach ($this->preferences as $key => $value) { 31 | $file .= "user_pref(\"{$key}\", \"{$value}\");\n"; 32 | } 33 | return $file; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /WebDriver/MockDriver.php: -------------------------------------------------------------------------------- 1 | server_url = "http://localhost/wd/hub"; 8 | $this->session_id = 0; 9 | $this->next_element_id = 0; 10 | } 11 | 12 | public function execute($http_type, $relative_url, $payload = null) { 13 | if ($payload !== null) { 14 | $payload = json_encode($payload); 15 | } 16 | $relative_url = str_replace(':sessionId', $this->session_id, $relative_url); 17 | $full_url = $this->server_url . $relative_url; 18 | $response = WebDriver_MockDriver::MockCurl($http_type, $full_url, $payload); 19 | return $response; 20 | } 21 | 22 | public static function MockCurl($http_type, $full_url, $payload = null, $escape_payload = true) { 23 | if (($http_type === "POST" || $http_type === "PUT") && $payload !== null) { 24 | if ($escape_payload && (is_array($payload) || is_object($payload))) { 25 | $payload = http_build_query($payload); 26 | } 27 | } 28 | WebDriver::LogDebug("====="); 29 | WebDriver::LogDebug($http_type, $full_url, $payload); 30 | $response['header'] = ""; 31 | $response['body'] = json_encode(array("value" => "")); 32 | return $response; 33 | } 34 | 35 | public function get_element($locator) { 36 | $payload = WebDriver::ParseLocator($locator); 37 | $response = $this->execute("POST", "/session/:sessionId/element", $payload); 38 | return new WebDriver_MockElement($this, $this->next_element_id++, $locator); 39 | } 40 | 41 | public function assert_url($expected_url) { return true; } 42 | public function assert_title($expected_title, $ie_hash = '') { return true; } 43 | public function assert_element_present($element_locator) { return true; } 44 | public function assert_element_not_present($element_locator) { return true; } 45 | public function assert_string_present($expected_string) { return true; } 46 | public function assert_string_not_present($expected_missing_string) { return true; } 47 | } 48 | -------------------------------------------------------------------------------- /WebDriver/MockElement.php: -------------------------------------------------------------------------------- 1 | driver = $driver; 10 | $this->element_id = $element_id; 11 | $this->locator = $locator; 12 | } 13 | 14 | public function execute($http_type, $relative_url, $payload = null) { 15 | try { 16 | return $this->driver->execute($http_type, "/session/:sessionId/element/" . $this->element_id . $relative_url, $payload); 17 | } catch (WebDriver_StaleElementReferenceException $e) { 18 | $element_in_new_dom = $this->driver->get_element($this->locator); 19 | return $element_in_new_dom->execute($http_type, $relative_url, $payload); 20 | } catch (WebDriver_ElementNotVisibleException $e) { 21 | throw new WebDriver_ElementNotVisibleException($e->get_command(), $this->locator); 22 | } 23 | } 24 | 25 | public function get_driver() { 26 | return $this->driver; 27 | } 28 | 29 | public function get_element_id() { 30 | return $this->element_id; 31 | } 32 | 33 | public function get_locator() { 34 | return $this->locator; 35 | } 36 | 37 | /******************************************************************** 38 | * Getters 39 | */ 40 | 41 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id 42 | public function describe() { 43 | $response = $this->execute("GET", ""); 44 | return WebDriver::GetJSONValue($response); 45 | } 46 | 47 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/text 48 | public function get_text() { 49 | $response = $this->execute("GET", "/text"); 50 | return trim(WebDriver::GetJSONValue($response)); 51 | } 52 | 53 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value 54 | public function get_value() { 55 | $response = $this->execute("GET", "/value"); 56 | return WebDriver::GetJSONValue($response); 57 | } 58 | 59 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/displayed 60 | public function is_visible() { 61 | $response = $this->execute("GET", "/displayed"); 62 | return WebDriver::GetJSONValue($response); 63 | } 64 | 65 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/enabled 66 | public function is_enabled() { 67 | $response = $this->execute("GET", "/enabled"); 68 | return WebDriver::GetJSONValue($response); 69 | } 70 | 71 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/selected 72 | public function is_selected() { 73 | $response = $this->execute("GET", "/selected"); 74 | return WebDriver::GetJSONValue($response); 75 | } 76 | 77 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/element 78 | public function get_next_element($locator) { 79 | $payload = WebDriver::ParseLocator($locator); 80 | $response = $this->execute("POST", "/element", $payload); 81 | $next_element_id = WebDriver::GetJSONValue($response, "ELEMENT"); 82 | return new WebDriver_WebElement($this->driver, $next_element_id, $locator); 83 | } 84 | 85 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/elements 86 | public function get_all_next_elements($locator) { 87 | $payload = WebDriver::ParseLocator($locator); 88 | $response = $this->execute("POST", "/elements", $payload); 89 | $all_element_ids = WebDriver::GetJSONValue($response, "ELEMENT"); 90 | $all_elements = array(); 91 | foreach ($all_element_ids as $element_id) { 92 | $all_elements[] = new WebDriver_WebElement($this->driver, $element_id, $locator); 93 | } 94 | return $all_elements; 95 | } 96 | 97 | public function get_next_element_count($locator) { 98 | return count($this->get_all_next_elements($locator)); 99 | } 100 | 101 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/name 102 | public function get_tag_name() { 103 | $response = $this->execute("GET", "/name"); 104 | return WebDriver::GetJSONValue($response); 105 | } 106 | 107 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/attribute/:name 108 | public function get_attribute_value($attribute_name) { 109 | $response = $this->execute("GET", "/attribute/" . $attribute_name); 110 | return WebDriver::GetJSONValue($response); 111 | } 112 | 113 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/equals/:other 114 | public function is_same_element_as($other_element_id) { 115 | $response = $this->execute("GET", "/equals/" . $other_element_id); 116 | return WebDriver::GetJSONValue($response); 117 | } 118 | 119 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/location 120 | public function get_location() { 121 | $response = $this->execute("GET", "/location"); 122 | return WebDriver::GetJSONValue($response); 123 | } 124 | 125 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/location_in_view 126 | public function get_location_in_view() { 127 | $response = $this->execute("GET", "/location_in_view"); 128 | return WebDriver::GetJSONValue($response); 129 | } 130 | 131 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/size 132 | public function get_size() { 133 | $response = $this->execute("GET", "/size"); 134 | return WebDriver::GetJSONValue($response); 135 | } 136 | 137 | // See http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/css/:propertyName 138 | public function get_css_value($property_name) { 139 | $response = $this->execute("GET", "/css/" . $property_name); 140 | return WebDriver::GetJSONValue($response); 141 | } 142 | 143 | public function contains_element($locator) { 144 | try { 145 | $this->get_next_element($locator); 146 | $is_element_present = true; 147 | } catch (Exception $e) { 148 | $is_element_present = false; 149 | } 150 | return $is_element_present; 151 | } 152 | 153 | /******************************************************************** 154 | * Getters for elements 253 | */ 254 | 255 | public function select_label($label) { 256 | $this->get_next_element("//option[text()=" . WebDriver::QuoteXPath($label) . "]")->select(); 257 | } 258 | 259 | public function select_value($value) { 260 | $this->get_next_element("//option[@value=" . WebDriver::QuoteXPath($value) . "]")->select(); 261 | } 262 | 263 | // 1-based index 264 | public function select_index($index) { 265 | $this->get_next_element("//option[" . $index . "]")->select(); 266 | } 267 | 268 | public function select_random() { 269 | $all_elements = $this->get_options(); 270 | $new_index = rand(1, count($all_elements)); 271 | $this->select_index($new_index); 272 | } 273 | 274 | /******************************************************************** 275 | * Asserters 276 | */ 277 | 278 | public function assert_visible() { 279 | $visible = WebDriver::WaitUntil(array($this, 'is_visible'), array(), true); 280 | PHPUnit_Framework_Assert::assertTrue($visible, "Failed asserting that <{$this->locator}> is visible."); 281 | } 282 | 283 | public function assert_hidden() { 284 | $visible = WebDriver::WaitUntil(array($this, 'is_visible'), array(), false); 285 | PHPUnit_Framework_Assert::assertFalse($visible, "Failed asserting that <{$this->locator}> is hidden."); 286 | } 287 | 288 | public function assert_enabled() { 289 | $enabled = WebDriver::WaitUntil(array($this, 'is_enabled'), array(), true); 290 | PHPUnit_Framework_Assert::assertTrue($enabled, "Failed asserting that <{$this->locator}> is enabled."); 291 | } 292 | 293 | public function assert_disabled() { 294 | $enabled = WebDriver::WaitUntil(array($this, 'is_enabled'), array(), false); 295 | PHPUnit_Framework_Assert::assertFalse($enabled, "Failed asserting that <{$this->locator}> is disabled."); 296 | } 297 | 298 | public function assert_selected() { 299 | $selected = WebDriver::WaitUntil(array($this, 'is_selected'), array(), true); 300 | PHPUnit_Framework_Assert::assertTrue($selected, "Failed asserting that <{$this->locator}> is selected."); 301 | } 302 | 303 | public function assert_not_selected() { 304 | $selected = WebDriver::WaitUntil(array($this, 'is_selected'), array(), false); 305 | PHPUnit_Framework_Assert::assertFalse($selected, "Failed asserting that <{$this->locator}> is not selected."); 306 | } 307 | 308 | public function assert_contains_element($child_locator) { 309 | $contains = WebDriver::WaitUntil(array($this, 'contains_element'), array($child_locator), true); 310 | PHPUnit_Framework_Assert::assertTrue($contains, "Failed asserting that <{$this->locator}> contains <$child_locator>."); 311 | } 312 | 313 | public function assert_does_not_contain_element($child_locator) { 314 | $contains = WebDriver::WaitUntil(array($this, 'contains_element'), array($child_locator), false); 315 | PHPUnit_Framework_Assert::assertFalse($contains, "Failed asserting that <{$this->locator}> does not contain <$child_locator>."); 316 | } 317 | 318 | public function assert_next_element_count($locator, $expected_count) { 319 | $count = WebDriver::WaitUntil(array($this, 'get_next_element_count'), array($locator), $expected_count); 320 | PHPUnit_Framework_Assert::assertEquals($expected_count, $count, "Failed asserting that <{$this->locator}> is followed by <$expected_count> instances of <$locator>"); 321 | } 322 | 323 | public function assert_text($expected_text) { 324 | $text = WebDriver::WaitUntil(array($this, 'get_text'), array(), $expected_text); 325 | PHPUnit_Framework_Assert::assertEquals($expected_text, $text, "Failed asserting that <{$this->locator}>'s text is <$expected_text>."); 326 | } 327 | 328 | // Reload the page until this element's text matches what's expected 329 | public function assert_text_reload($expected_text) { 330 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 331 | do { 332 | $this->driver->reload(); 333 | $element_in_new_dom = $this->driver->get_element($this->locator); 334 | $actual_text = $element_in_new_dom->get_text(); 335 | } while (time() < $end_time && $actual_text != $expected_text); 336 | PHPUnit_Framework_Assert::assertEquals($expected_text, $actual_text, "Failed asserting that <{$this->locator}>'s text is <$expected_text>."); 337 | } 338 | 339 | public function assert_text_contains($expected_needle) { 340 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 341 | do { 342 | $actual_haystack = $this->get_text(); 343 | } while (time() < $end_time && strstr($actual_haystack, $expected_needle) === false); 344 | PHPUnit_Framework_Assert::assertContains($expected_needle, $actual_haystack, "Failed asserting that <{$this->locator}>'s text contains <$expected_needle>.\n$actual_haystack"); 345 | } 346 | 347 | public function assert_text_does_not_contain($expected_missing_needle) { 348 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 349 | do { 350 | $actual_haystack = $this->get_text(); 351 | } while (time() < $end_time && strstr($actual_haystack, $expected_missing_needle) !== false); 352 | PHPUnit_Framework_Assert::assertNotContains($expected_missing_needle, $actual_haystack, "Failed asserting that <{$this->locator}>'s text does not contain <$expected_missing_needle>."); 353 | } 354 | 355 | public function assert_value($expected_value) { 356 | $value = WebDriver::WaitUntil(array($this, 'get_value'), array(), $expected_value); 357 | PHPUnit_Framework_Assert::assertEquals($expected_value, $value, "Failed asserting that <{$this->locator}>'s value is <$expected_value>."); 358 | } 359 | 360 | public function assert_attribute_value($attribute_name, $expected_value) { 361 | $value = WebDriver::WaitUntil(array($this, 'get_attribute_value'), array($attribute_name), $expected_value); 362 | PHPUnit_Framework_Assert::assertEquals($expected_value, $value, "Failed asserting that <{$this->locator}>'s attribute <{$attribute_name}> is <$expected_value>."); 363 | } 364 | 365 | public function assert_attribute_contains($attribute_name, $expected_needle) { 366 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 367 | do { 368 | $actual_haystack = $this->get_attribute_value($attribute_name); 369 | } while (time() < $end_time && strstr($actual_haystack, $expected_needle) === false); 370 | PHPUnit_Framework_Assert::assertContains($expected_needle, $actual_haystack, "Failed asserting that <{$this->locator}>'s attribute <{$attribute_name}> contains <{$expected_needle}>."); 371 | } 372 | 373 | public function assert_attribute_does_not_contain($attribute_name, $expected_missing_needle) { 374 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 375 | do { 376 | $actual_haystack = $this->get_attribute_value($attribute_name); 377 | } while (time() < $end_time && strstr($actual_haystack, $expected_missing_needle) !== false); 378 | PHPUnit_Framework_Assert::assertNotContains($expected_missing_needle, $actual_haystack, "Failed asserting that <{$this->locator}>'s attribute <{$attribute_name}> does not contain <{$expected_missing_needle}>."); 379 | } 380 | 381 | // Will pass for "equivalent" CSS colors such as "#FFFFFF" and "white". Pass $canonicalize_colors = false to disable. 382 | public function assert_css_value($property_name, $expected_value, $canonicalize_colors = true) { 383 | if (strpos($property_name, 'color') !== false && $canonicalize_colors) { 384 | $canonical_expected = WebDriver::CanonicalizeCSSColor($expected_value); 385 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 386 | do { 387 | $actual_value = $this->get_css_value($property_name); 388 | $canonical_actual = WebDriver::CanonicalizeCSSColor($actual_value); 389 | } while (time() < $end_time && $canonical_actual != $canonical_expected); 390 | PHPUnit_Framework_Assert::assertEquals($canonical_expected, $canonical_actual, "Failed asserting that <{$this->locator}>'s <{$property_name}> is <$canonical_expected> after canonicalization.\nExpected: $expected_value -> $canonical_expected\nActual: $actual_value -> $canonical_actual"); 391 | } else { 392 | $value = WebDriver::WaitUntil(array($this, 'get_css_value'), array($property_name), $expected_value); 393 | PHPUnit_Framework_Assert::assertEquals($expected_value, $value, "Failed asserting that <{$this->locator}>'s <{$property_name}> is <$expected_value>."); 394 | } 395 | } 396 | 397 | public function assert_css_value_contains($property_name, $expected_needle) { 398 | $end_time = time() + WebDriver::$ImplicitWaitMS/1000; 399 | do { 400 | $actual_haystack = $this->get_css_value($property_name); 401 | } while (time() < $end_time && strstr($actual_haystack, $expected_needle) === false); 402 | PHPUnit_Framework_Assert::assertContains($expected_needle, $actual_haystack, "Failed asserting that <{$this->locator}>'s <{$property_name}> contains <$expected_needle>.\n$actual_haystack"); 403 | } 404 | 405 | /******************************************************************** 406 | * Asserters for