1<?php 2 3namespace Facebook\WebDriver; 4 5use Facebook\WebDriver\Exception\NoSuchAlertException; 6use Facebook\WebDriver\Exception\NoSuchElementException; 7use Facebook\WebDriver\Exception\NoSuchFrameException; 8use Facebook\WebDriver\Exception\StaleElementReferenceException; 9 10/** 11 * Canned ExpectedConditions which are generally useful within webdriver tests. 12 * 13 * @see WebDriverWait 14 */ 15class WebDriverExpectedCondition 16{ 17 /** 18 * A callable function to be executed by WebDriverWait. It should return 19 * a truthy value, mostly boolean or a WebDriverElement, on success. 20 * @var callable 21 */ 22 private $apply; 23 24 protected function __construct(callable $apply) 25 { 26 $this->apply = $apply; 27 } 28 29 /** 30 * @return callable A callable function to be executed by WebDriverWait 31 */ 32 public function getApply() 33 { 34 return $this->apply; 35 } 36 37 /** 38 * An expectation for checking the title of a page. 39 * 40 * @param string $title The expected title, which must be an exact match. 41 * @return static Condition returns whether current page title equals given string. 42 */ 43 public static function titleIs($title) 44 { 45 return new static( 46 function (WebDriver $driver) use ($title) { 47 return $title === $driver->getTitle(); 48 } 49 ); 50 } 51 52 /** 53 * An expectation for checking substring of a page Title. 54 * 55 * @param string $title The expected substring of Title. 56 * @return static Condition returns whether current page title contains given string. 57 */ 58 public static function titleContains($title) 59 { 60 return new static( 61 function (WebDriver $driver) use ($title) { 62 return mb_strpos($driver->getTitle(), $title) !== false; 63 } 64 ); 65 } 66 67 /** 68 * An expectation for checking current page title matches the given regular expression. 69 * 70 * @param string $titleRegexp The regular expression to test against. 71 * @return static Condition returns whether current page title matches the regular expression. 72 */ 73 public static function titleMatches($titleRegexp) 74 { 75 return new static( 76 function (WebDriver $driver) use ($titleRegexp) { 77 return (bool) preg_match($titleRegexp, $driver->getTitle()); 78 } 79 ); 80 } 81 82 /** 83 * An expectation for checking the URL of a page. 84 * 85 * @param string $url The expected URL, which must be an exact match. 86 * @return static Condition returns whether current URL equals given one. 87 */ 88 public static function urlIs($url) 89 { 90 return new static( 91 function (WebDriver $driver) use ($url) { 92 return $url === $driver->getCurrentURL(); 93 } 94 ); 95 } 96 97 /** 98 * An expectation for checking substring of the URL of a page. 99 * 100 * @param string $url The expected substring of the URL 101 * @return static Condition returns whether current URL contains given string. 102 */ 103 public static function urlContains($url) 104 { 105 return new static( 106 function (WebDriver $driver) use ($url) { 107 return mb_strpos($driver->getCurrentURL(), $url) !== false; 108 } 109 ); 110 } 111 112 /** 113 * An expectation for checking current page URL matches the given regular expression. 114 * 115 * @param string $urlRegexp The regular expression to test against. 116 * @return static Condition returns whether current URL matches the regular expression. 117 */ 118 public static function urlMatches($urlRegexp) 119 { 120 return new static( 121 function (WebDriver $driver) use ($urlRegexp) { 122 return (bool) preg_match($urlRegexp, $driver->getCurrentURL()); 123 } 124 ); 125 } 126 127 /** 128 * An expectation for checking that an element is present on the DOM of a page. 129 * This does not necessarily mean that the element is visible. 130 * 131 * @param WebDriverBy $by The locator used to find the element. 132 * @return static Condition returns the WebDriverElement which is located. 133 */ 134 public static function presenceOfElementLocated(WebDriverBy $by) 135 { 136 return new static( 137 function (WebDriver $driver) use ($by) { 138 try { 139 return $driver->findElement($by); 140 } catch (NoSuchElementException $e) { 141 return false; 142 } 143 } 144 ); 145 } 146 147 /** 148 * An expectation for checking that there is at least one element present on a web page. 149 * 150 * @param WebDriverBy $by The locator used to find the element. 151 * @return static Condition return an array of WebDriverElement once they are located. 152 */ 153 public static function presenceOfAllElementsLocatedBy(WebDriverBy $by) 154 { 155 return new static( 156 function (WebDriver $driver) use ($by) { 157 $elements = $driver->findElements($by); 158 159 return count($elements) > 0 ? $elements : null; 160 } 161 ); 162 } 163 164 /** 165 * An expectation for checking that an element is present on the DOM of a page and visible. 166 * Visibility means that the element is not only displayed but also has a height and width that is greater than 0. 167 * 168 * @param WebDriverBy $by The locator used to find the element. 169 * @return static Condition returns the WebDriverElement which is located and visible. 170 */ 171 public static function visibilityOfElementLocated(WebDriverBy $by) 172 { 173 return new static( 174 function (WebDriver $driver) use ($by) { 175 try { 176 $element = $driver->findElement($by); 177 178 return $element->isDisplayed() ? $element : null; 179 } catch (StaleElementReferenceException $e) { 180 return null; 181 } 182 } 183 ); 184 } 185 186 /** 187 * An expectation for checking than at least one element in an array of elements is present on the 188 * DOM of a page and visible. 189 * Visibility means that the element is not only displayed but also has a height and width that is greater than 0. 190 * 191 * @param WebDriverBy $by The located used to find the element. 192 * @return static Condition returns the array of WebDriverElement that are located and visible. 193 */ 194 public static function visibilityOfAnyElementLocated(WebDriverBy $by) 195 { 196 return new static( 197 function (WebDriver $driver) use ($by) { 198 $elements = $driver->findElements($by); 199 $visibleElements = []; 200 201 foreach ($elements as $element) { 202 try { 203 if ($element->isDisplayed()) { 204 $visibleElements[] = $element; 205 } 206 } catch (StaleElementReferenceException $e) { 207 } 208 } 209 210 return count($visibleElements) > 0 ? $visibleElements : null; 211 } 212 ); 213 } 214 215 /** 216 * An expectation for checking that an element, known to be present on the DOM of a page, is visible. 217 * Visibility means that the element is not only displayed but also has a height and width that is greater than 0. 218 * 219 * @param WebDriverElement $element The element to be checked. 220 * @return static Condition returns the same WebDriverElement once it is visible. 221 */ 222 public static function visibilityOf(WebDriverElement $element) 223 { 224 return new static( 225 function () use ($element) { 226 return $element->isDisplayed() ? $element : null; 227 } 228 ); 229 } 230 231 /** 232 * An expectation for checking if the given text is present in the specified element. 233 * To check exact text match use elementTextIs() condition. 234 * 235 * @codeCoverageIgnore 236 * @deprecated Use WebDriverExpectedCondition::elementTextContains() instead 237 * @param WebDriverBy $by The locator used to find the element. 238 * @param string $text The text to be presented in the element. 239 * @return static Condition returns whether the text is present in the element. 240 */ 241 public static function textToBePresentInElement(WebDriverBy $by, $text) 242 { 243 return self::elementTextContains($by, $text); 244 } 245 246 /** 247 * An expectation for checking if the given text is present in the specified element. 248 * To check exact text match use elementTextIs() condition. 249 * 250 * @param WebDriverBy $by The locator used to find the element. 251 * @param string $text The text to be presented in the element. 252 * @return static Condition returns whether the partial text is present in the element. 253 */ 254 public static function elementTextContains(WebDriverBy $by, $text) 255 { 256 return new static( 257 function (WebDriver $driver) use ($by, $text) { 258 try { 259 $element_text = $driver->findElement($by)->getText(); 260 261 return mb_strpos($element_text, $text) !== false; 262 } catch (StaleElementReferenceException $e) { 263 return null; 264 } 265 } 266 ); 267 } 268 269 /** 270 * An expectation for checking if the given text exactly equals the text in specified element. 271 * To check only partial substring of the text use elementTextContains() condition. 272 * 273 * @param WebDriverBy $by The locator used to find the element. 274 * @param string $text The expected text of the element. 275 * @return static Condition returns whether the element has text value equal to given one. 276 */ 277 public static function elementTextIs(WebDriverBy $by, $text) 278 { 279 return new static( 280 function (WebDriver $driver) use ($by, $text) { 281 try { 282 return $driver->findElement($by)->getText() == $text; 283 } catch (StaleElementReferenceException $e) { 284 return null; 285 } 286 } 287 ); 288 } 289 290 /** 291 * An expectation for checking if the given regular expression matches the text in specified element. 292 * 293 * @param WebDriverBy $by The locator used to find the element. 294 * @param string $regexp The regular expression to test against. 295 * @return static Condition returns whether the element has text value equal to given one. 296 */ 297 public static function elementTextMatches(WebDriverBy $by, $regexp) 298 { 299 return new static( 300 function (WebDriver $driver) use ($by, $regexp) { 301 try { 302 return (bool) preg_match($regexp, $driver->findElement($by)->getText()); 303 } catch (StaleElementReferenceException $e) { 304 return null; 305 } 306 } 307 ); 308 } 309 310 /** 311 * An expectation for checking if the given text is present in the specified elements value attribute. 312 * 313 * @codeCoverageIgnore 314 * @deprecated Use WebDriverExpectedCondition::elementValueContains() instead 315 * @param WebDriverBy $by The locator used to find the element. 316 * @param string $text The text to be presented in the element value. 317 * @return static Condition returns whether the text is present in value attribute. 318 */ 319 public static function textToBePresentInElementValue(WebDriverBy $by, $text) 320 { 321 return self::elementValueContains($by, $text); 322 } 323 324 /** 325 * An expectation for checking if the given text is present in the specified elements value attribute. 326 * 327 * @param WebDriverBy $by The locator used to find the element. 328 * @param string $text The text to be presented in the element value. 329 * @return static Condition returns whether the text is present in value attribute. 330 */ 331 public static function elementValueContains(WebDriverBy $by, $text) 332 { 333 return new static( 334 function (WebDriver $driver) use ($by, $text) { 335 try { 336 $element_text = $driver->findElement($by)->getAttribute('value'); 337 338 return mb_strpos($element_text, $text) !== false; 339 } catch (StaleElementReferenceException $e) { 340 return null; 341 } 342 } 343 ); 344 } 345 346 /** 347 * Expectation for checking if iFrame exists. If iFrame exists switches driver's focus to the iFrame. 348 * 349 * @param string $frame_locator The locator used to find the iFrame 350 * expected to be either the id or name value of the i/frame 351 * @return static Condition returns object focused on new frame when frame is found, false otherwise. 352 */ 353 public static function frameToBeAvailableAndSwitchToIt($frame_locator) 354 { 355 return new static( 356 function (WebDriver $driver) use ($frame_locator) { 357 try { 358 return $driver->switchTo()->frame($frame_locator); 359 } catch (NoSuchFrameException $e) { 360 return false; 361 } 362 } 363 ); 364 } 365 366 /** 367 * An expectation for checking that an element is either invisible or not present on the DOM. 368 * 369 * @param WebDriverBy $by The locator used to find the element. 370 * @return static Condition returns whether no visible element located. 371 */ 372 public static function invisibilityOfElementLocated(WebDriverBy $by) 373 { 374 return new static( 375 function (WebDriver $driver) use ($by) { 376 try { 377 return !$driver->findElement($by)->isDisplayed(); 378 } catch (NoSuchElementException $e) { 379 return true; 380 } catch (StaleElementReferenceException $e) { 381 return true; 382 } 383 } 384 ); 385 } 386 387 /** 388 * An expectation for checking that an element with text is either invisible or not present on the DOM. 389 * 390 * @param WebDriverBy $by The locator used to find the element. 391 * @param string $text The text of the element. 392 * @return static Condition returns whether the text is found in the element located. 393 */ 394 public static function invisibilityOfElementWithText(WebDriverBy $by, $text) 395 { 396 return new static( 397 function (WebDriver $driver) use ($by, $text) { 398 try { 399 return !($driver->findElement($by)->getText() === $text); 400 } catch (NoSuchElementException $e) { 401 return true; 402 } catch (StaleElementReferenceException $e) { 403 return true; 404 } 405 } 406 ); 407 } 408 409 /** 410 * An expectation for checking an element is visible and enabled such that you can click it. 411 * 412 * @param WebDriverBy $by The locator used to find the element 413 * @return static Condition return the WebDriverElement once it is located, visible and clickable. 414 */ 415 public static function elementToBeClickable(WebDriverBy $by) 416 { 417 $visibility_of_element_located = self::visibilityOfElementLocated($by); 418 419 return new static( 420 function (WebDriver $driver) use ($visibility_of_element_located) { 421 $element = call_user_func( 422 $visibility_of_element_located->getApply(), 423 $driver 424 ); 425 426 try { 427 if ($element !== null && $element->isEnabled()) { 428 return $element; 429 } 430 431 return null; 432 } catch (StaleElementReferenceException $e) { 433 return null; 434 } 435 } 436 ); 437 } 438 439 /** 440 * Wait until an element is no longer attached to the DOM. 441 * 442 * @param WebDriverElement $element The element to wait for. 443 * @return static Condition returns whether the element is still attached to the DOM. 444 */ 445 public static function stalenessOf(WebDriverElement $element) 446 { 447 return new static( 448 function () use ($element) { 449 try { 450 $element->isEnabled(); 451 452 return false; 453 } catch (StaleElementReferenceException $e) { 454 return true; 455 } 456 } 457 ); 458 } 459 460 /** 461 * Wrapper for a condition, which allows for elements to update by redrawing. 462 * 463 * This works around the problem of conditions which have two parts: find an element and then check for some 464 * condition on it. For these conditions it is possible that an element is located and then subsequently it is 465 * redrawn on the client. When this happens a StaleElementReferenceException is thrown when the second part of 466 * the condition is checked. 467 * 468 * @param WebDriverExpectedCondition $condition The condition wrapped. 469 * @return static Condition returns the return value of the getApply() of the given condition. 470 */ 471 public static function refreshed(self $condition) 472 { 473 return new static( 474 function (WebDriver $driver) use ($condition) { 475 try { 476 return call_user_func($condition->getApply(), $driver); 477 } catch (StaleElementReferenceException $e) { 478 return null; 479 } 480 } 481 ); 482 } 483 484 /** 485 * An expectation for checking if the given element is selected. 486 * 487 * @param mixed $element_or_by Either the element or the locator. 488 * @return static Condition returns whether the element is selected. 489 */ 490 public static function elementToBeSelected($element_or_by) 491 { 492 return self::elementSelectionStateToBe( 493 $element_or_by, 494 true 495 ); 496 } 497 498 /** 499 * An expectation for checking if the given element is selected. 500 * 501 * @param mixed $element_or_by Either the element or the locator. 502 * @param bool $selected The required state. 503 * @return static Condition returns whether the element is selected. 504 */ 505 public static function elementSelectionStateToBe($element_or_by, $selected) 506 { 507 if ($element_or_by instanceof WebDriverElement) { 508 return new static( 509 function () use ($element_or_by, $selected) { 510 return $element_or_by->isSelected() === $selected; 511 } 512 ); 513 } 514 515 if ($element_or_by instanceof WebDriverBy) { 516 return new static( 517 function (WebDriver $driver) use ($element_or_by, $selected) { 518 try { 519 $element = $driver->findElement($element_or_by); 520 521 return $element->isSelected() === $selected; 522 } catch (StaleElementReferenceException $e) { 523 return null; 524 } 525 } 526 ); 527 } 528 529 throw new \InvalidArgumentException('Instance of either WebDriverElement or WebDriverBy must be given'); 530 } 531 532 /** 533 * An expectation for whether an alert() box is present. 534 * 535 * @return static Condition returns WebDriverAlert if alert() is present, null otherwise. 536 */ 537 public static function alertIsPresent() 538 { 539 return new static( 540 function (WebDriver $driver) { 541 try { 542 // Unlike the Java code, we get a WebDriverAlert object regardless 543 // of whether there is an alert. Calling getText() will throw 544 // an exception if it is not really there. 545 $alert = $driver->switchTo()->alert(); 546 $alert->getText(); 547 548 return $alert; 549 } catch (NoSuchAlertException $e) { 550 return null; 551 } 552 } 553 ); 554 } 555 556 /** 557 * An expectation checking the number of opened windows. 558 * 559 * @param int $expectedNumberOfWindows 560 * @return static 561 */ 562 public static function numberOfWindowsToBe($expectedNumberOfWindows) 563 { 564 return new static( 565 function (WebDriver $driver) use ($expectedNumberOfWindows) { 566 return count($driver->getWindowHandles()) == $expectedNumberOfWindows; 567 } 568 ); 569 } 570 571 /** 572 * An expectation with the logical opposite condition of the given condition. 573 * 574 * @param WebDriverExpectedCondition $condition The condition to be negated. 575 * @return mixed The negation of the result of the given condition. 576 */ 577 public static function not(self $condition) 578 { 579 return new static( 580 function (WebDriver $driver) use ($condition) { 581 $result = call_user_func($condition->getApply(), $driver); 582 583 return !$result; 584 } 585 ); 586 } 587} 588