1<?php 2/** 3 * Base include file for SimpleTest. 4 * @package SimpleTest 5 * @subpackage WebTester 6 * @version $Id$ 7 */ 8 9/**#@+ 10 * include other SimpleTest class files 11 */ 12require_once(dirname(__FILE__) . '/test_case.php'); 13require_once(dirname(__FILE__) . '/browser.php'); 14require_once(dirname(__FILE__) . '/page.php'); 15require_once(dirname(__FILE__) . '/expectation.php'); 16/**#@-*/ 17 18/** 19 * Test for an HTML widget value match. 20 * @package SimpleTest 21 * @subpackage WebTester 22 */ 23class FieldExpectation extends SimpleExpectation { 24 private $value; 25 26 /** 27 * Sets the field value to compare against. 28 * @param mixed $value Test value to match. Can be an 29 * expectation for say pattern matching. 30 * @param string $message Optiona message override. Can use %s as 31 * a placeholder for the original message. 32 * @access public 33 */ 34 function __construct($value, $message = '%s') { 35 parent::__construct($message); 36 if (is_array($value)) { 37 sort($value); 38 } 39 $this->value = $value; 40 } 41 42 /** 43 * Tests the expectation. True if it matches 44 * a string value or an array value in any order. 45 * @param mixed $compare Comparison value. False for 46 * an unset field. 47 * @return boolean True if correct. 48 * @access public 49 */ 50 function test($compare) { 51 if ($this->value === false) { 52 return ($compare === false); 53 } 54 if ($this->isSingle($this->value)) { 55 return $this->testSingle($compare); 56 } 57 if (is_array($this->value)) { 58 return $this->testMultiple($compare); 59 } 60 return false; 61 } 62 63 /** 64 * Tests for valid field comparisons with a single option. 65 * @param mixed $value Value to type check. 66 * @return boolean True if integer, string or float. 67 * @access private 68 */ 69 protected function isSingle($value) { 70 return is_string($value) || is_integer($value) || is_float($value); 71 } 72 73 /** 74 * String comparison for simple field with a single option. 75 * @param mixed $compare String to test against. 76 * @returns boolean True if matching. 77 * @access private 78 */ 79 protected function testSingle($compare) { 80 if (is_array($compare) && count($compare) == 1) { 81 $compare = $compare[0]; 82 } 83 if (! $this->isSingle($compare)) { 84 return false; 85 } 86 return ($this->value == $compare); 87 } 88 89 /** 90 * List comparison for multivalue field. 91 * @param mixed $compare List in any order to test against. 92 * @returns boolean True if matching. 93 * @access private 94 */ 95 protected function testMultiple($compare) { 96 if (is_string($compare)) { 97 $compare = array($compare); 98 } 99 if (! is_array($compare)) { 100 return false; 101 } 102 sort($compare); 103 return ($this->value === $compare); 104 } 105 106 /** 107 * Returns a human readable test message. 108 * @param mixed $compare Comparison value. 109 * @return string Description of success 110 * or failure. 111 * @access public 112 */ 113 function testMessage($compare) { 114 $dumper = $this->getDumper(); 115 if (is_array($compare)) { 116 sort($compare); 117 } 118 if ($this->test($compare)) { 119 return "Field expectation [" . $dumper->describeValue($this->value) . "]"; 120 } else { 121 return "Field expectation [" . $dumper->describeValue($this->value) . 122 "] fails with [" . 123 $dumper->describeValue($compare) . "] " . 124 $dumper->describeDifference($this->value, $compare); 125 } 126 } 127} 128 129/** 130 * Test for a specific HTTP header within a header block. 131 * @package SimpleTest 132 * @subpackage WebTester 133 */ 134class HttpHeaderExpectation extends SimpleExpectation { 135 private $expected_header; 136 private $expected_value; 137 138 /** 139 * Sets the field and value to compare against. 140 * @param string $header Case insenstive trimmed header name. 141 * @param mixed $value Optional value to compare. If not 142 * given then any value will match. If 143 * an expectation object then that will 144 * be used instead. 145 * @param string $message Optiona message override. Can use %s as 146 * a placeholder for the original message. 147 */ 148 function __construct($header, $value = false, $message = '%s') { 149 parent::__construct($message); 150 $this->expected_header = $this->normaliseHeader($header); 151 $this->expected_value = $value; 152 } 153 154 /** 155 * Accessor for aggregated object. 156 * @return mixed Expectation set in constructor. 157 * @access protected 158 */ 159 protected function getExpectation() { 160 return $this->expected_value; 161 } 162 163 /** 164 * Removes whitespace at ends and case variations. 165 * @param string $header Name of header. 166 * @param string Trimmed and lowecased header 167 * name. 168 * @access private 169 */ 170 protected function normaliseHeader($header) { 171 return strtolower(trim($header)); 172 } 173 174 /** 175 * Tests the expectation. True if it matches 176 * a string value or an array value in any order. 177 * @param mixed $compare Raw header block to search. 178 * @return boolean True if header present. 179 * @access public 180 */ 181 function test($compare) { 182 return is_string($this->findHeader($compare)); 183 } 184 185 /** 186 * Searches the incoming result. Will extract the matching 187 * line as text. 188 * @param mixed $compare Raw header block to search. 189 * @return string Matching header line. 190 * @access protected 191 */ 192 protected function findHeader($compare) { 193 $lines = explode("\r\n", $compare); 194 foreach ($lines as $line) { 195 if ($this->testHeaderLine($line)) { 196 return $line; 197 } 198 } 199 return false; 200 } 201 202 /** 203 * Compares a single header line against the expectation. 204 * @param string $line A single line to compare. 205 * @return boolean True if matched. 206 * @access private 207 */ 208 protected function testHeaderLine($line) { 209 if (count($parsed = explode(':', $line, 2)) < 2) { 210 return false; 211 } 212 list($header, $value) = $parsed; 213 if ($this->normaliseHeader($header) != $this->expected_header) { 214 return false; 215 } 216 return $this->testHeaderValue($value, $this->expected_value); 217 } 218 219 /** 220 * Tests the value part of the header. 221 * @param string $value Value to test. 222 * @param mixed $expected Value to test against. 223 * @return boolean True if matched. 224 * @access protected 225 */ 226 protected function testHeaderValue($value, $expected) { 227 if ($expected === false) { 228 return true; 229 } 230 if (SimpleExpectation::isExpectation($expected)) { 231 return $expected->test(trim($value)); 232 } 233 return (trim($value) == trim($expected)); 234 } 235 236 /** 237 * Returns a human readable test message. 238 * @param mixed $compare Raw header block to search. 239 * @return string Description of success 240 * or failure. 241 * @access public 242 */ 243 function testMessage($compare) { 244 if (SimpleExpectation::isExpectation($this->expected_value)) { 245 $message = $this->expected_value->overlayMessage($compare, $this->getDumper()); 246 } else { 247 $message = $this->expected_header . 248 ($this->expected_value ? ': ' . $this->expected_value : ''); 249 } 250 if (is_string($line = $this->findHeader($compare))) { 251 return "Searching for header [$message] found [$line]"; 252 } else { 253 return "Failed to find header [$message]"; 254 } 255 } 256} 257 258/** 259 * Test for a specific HTTP header within a header block that 260 * should not be found. 261 * @package SimpleTest 262 * @subpackage WebTester 263 */ 264class NoHttpHeaderExpectation extends HttpHeaderExpectation { 265 private $expected_header; 266 private $expected_value; 267 268 /** 269 * Sets the field and value to compare against. 270 * @param string $unwanted Case insenstive trimmed header name. 271 * @param string $message Optiona message override. Can use %s as 272 * a placeholder for the original message. 273 */ 274 function __construct($unwanted, $message = '%s') { 275 parent::__construct($unwanted, false, $message); 276 } 277 278 /** 279 * Tests that the unwanted header is not found. 280 * @param mixed $compare Raw header block to search. 281 * @return boolean True if header present. 282 * @access public 283 */ 284 function test($compare) { 285 return ($this->findHeader($compare) === false); 286 } 287 288 /** 289 * Returns a human readable test message. 290 * @param mixed $compare Raw header block to search. 291 * @return string Description of success 292 * or failure. 293 * @access public 294 */ 295 function testMessage($compare) { 296 $expectation = $this->getExpectation(); 297 if (is_string($line = $this->findHeader($compare))) { 298 return "Found unwanted header [$expectation] with [$line]"; 299 } else { 300 return "Did not find unwanted header [$expectation]"; 301 } 302 } 303} 304 305/** 306 * Test for a text substring. 307 * @package SimpleTest 308 * @subpackage UnitTester 309 */ 310class TextExpectation extends SimpleExpectation { 311 private $substring; 312 313 /** 314 * Sets the value to compare against. 315 * @param string $substring Text to search for. 316 * @param string $message Customised message on failure. 317 * @access public 318 */ 319 function __construct($substring, $message = '%s') { 320 parent::__construct($message); 321 $this->substring = $substring; 322 } 323 324 /** 325 * Accessor for the substring. 326 * @return string Text to match. 327 * @access protected 328 */ 329 protected function getSubstring() { 330 return $this->substring; 331 } 332 333 /** 334 * Tests the expectation. True if the text contains the 335 * substring. 336 * @param string $compare Comparison value. 337 * @return boolean True if correct. 338 * @access public 339 */ 340 function test($compare) { 341 return (strpos($compare, $this->substring) !== false); 342 } 343 344 /** 345 * Returns a human readable test message. 346 * @param mixed $compare Comparison value. 347 * @return string Description of success 348 * or failure. 349 * @access public 350 */ 351 function testMessage($compare) { 352 if ($this->test($compare)) { 353 return $this->describeTextMatch($this->getSubstring(), $compare); 354 } else { 355 $dumper = $this->getDumper(); 356 return "Text [" . $this->getSubstring() . 357 "] not detected in [" . 358 $dumper->describeValue($compare) . "]"; 359 } 360 } 361 362 /** 363 * Describes a pattern match including the string 364 * found and it's position. 365 * @param string $substring Text to search for. 366 * @param string $subject Subject to search. 367 * @access protected 368 */ 369 protected function describeTextMatch($substring, $subject) { 370 $position = strpos($subject, $substring); 371 $dumper = $this->getDumper(); 372 return "Text [$substring] detected at character [$position] in [" . 373 $dumper->describeValue($subject) . "] in region [" . 374 $dumper->clipString($subject, 100, $position) . "]"; 375 } 376} 377 378/** 379 * Fail if a substring is detected within the 380 * comparison text. 381 * @package SimpleTest 382 * @subpackage UnitTester 383 */ 384class NoTextExpectation extends TextExpectation { 385 386 /** 387 * Sets the reject pattern 388 * @param string $substring Text to search for. 389 * @param string $message Customised message on failure. 390 * @access public 391 */ 392 function __construct($substring, $message = '%s') { 393 parent::__construct($substring, $message); 394 } 395 396 /** 397 * Tests the expectation. False if the substring appears 398 * in the text. 399 * @param string $compare Comparison value. 400 * @return boolean True if correct. 401 * @access public 402 */ 403 function test($compare) { 404 return ! parent::test($compare); 405 } 406 407 /** 408 * Returns a human readable test message. 409 * @param string $compare Comparison value. 410 * @return string Description of success 411 * or failure. 412 * @access public 413 */ 414 function testMessage($compare) { 415 if ($this->test($compare)) { 416 $dumper = $this->getDumper(); 417 return "Text [" . $this->getSubstring() . 418 "] not detected in [" . 419 $dumper->describeValue($compare) . "]"; 420 } else { 421 return $this->describeTextMatch($this->getSubstring(), $compare); 422 } 423 } 424} 425 426/** 427 * Test case for testing of web pages. Allows 428 * fetching of pages, parsing of HTML and 429 * submitting forms. 430 * @package SimpleTest 431 * @subpackage WebTester 432 */ 433class WebTestCase extends SimpleTestCase { 434 private $browser; 435 private $ignore_errors = false; 436 437 /** 438 * Creates an empty test case. Should be subclassed 439 * with test methods for a functional test case. 440 * @param string $label Name of test case. Will use 441 * the class name if none specified. 442 * @access public 443 */ 444 function __construct($label = false) { 445 parent::__construct($label); 446 } 447 448 /** 449 * Announces the start of the test. 450 * @param string $method Test method just started. 451 * @access public 452 */ 453 function before($method) { 454 parent::before($method); 455 $this->setBrowser($this->createBrowser()); 456 } 457 458 /** 459 * Announces the end of the test. Includes private clean up. 460 * @param string $method Test method just finished. 461 * @access public 462 */ 463 function after($method) { 464 $this->unsetBrowser(); 465 parent::after($method); 466 } 467 468 /** 469 * Gets a current browser reference for setting 470 * special expectations or for detailed 471 * examination of page fetches. 472 * @return SimpleBrowser Current test browser object. 473 * @access public 474 */ 475 function getBrowser() { 476 return $this->browser; 477 } 478 479 /** 480 * Gets a current browser reference for setting 481 * special expectations or for detailed 482 * examination of page fetches. 483 * @param SimpleBrowser $browser New test browser object. 484 * @access public 485 */ 486 function setBrowser($browser) { 487 return $this->browser = $browser; 488 } 489 490 /** 491 * Sets the HTML parser to use within this browser. 492 * @param object The parser, one of SimplePHPPageBuilder or 493 * SimpleTidyPageBuilder. 494 */ 495 function setParser($parser) { 496 $this->browser->setParser($parser); 497 } 498 499 /** 500 * Clears the current browser reference to help the 501 * PHP garbage collector. 502 * @access public 503 */ 504 function unsetBrowser() { 505 unset($this->browser); 506 } 507 508 /** 509 * Creates a new default web browser object. 510 * Will be cleared at the end of the test method. 511 * @return TestBrowser New browser. 512 * @access public 513 */ 514 function createBrowser() { 515 return new SimpleBrowser(); 516 } 517 518 /** 519 * Gets the last response error. 520 * @return string Last low level HTTP error. 521 * @access public 522 */ 523 function getTransportError() { 524 return $this->browser->getTransportError(); 525 } 526 527 /** 528 * Accessor for the currently selected URL. 529 * @return string Current location or false if 530 * no page yet fetched. 531 * @access public 532 */ 533 function getUrl() { 534 return $this->browser->getUrl(); 535 } 536 537 /** 538 * Dumps the current request for debugging. 539 * @access public 540 */ 541 function showRequest() { 542 $this->dump($this->browser->getRequest()); 543 } 544 545 /** 546 * Dumps the current HTTP headers for debugging. 547 * @access public 548 */ 549 function showHeaders() { 550 $this->dump($this->browser->getHeaders()); 551 } 552 553 /** 554 * Dumps the current HTML source for debugging. 555 * @access public 556 */ 557 function showSource() { 558 $this->dump($this->browser->getContent()); 559 } 560 561 /** 562 * Dumps the visible text only for debugging. 563 * @access public 564 */ 565 function showText() { 566 $this->dump(wordwrap($this->browser->getContentAsText(), 80)); 567 } 568 569 /** 570 * Simulates the closing and reopening of the browser. 571 * Temporary cookies will be discarded and timed 572 * cookies will be expired if later than the 573 * specified time. 574 * @param string/integer $date Time when session restarted. 575 * If ommitted then all persistent 576 * cookies are kept. Time is either 577 * Cookie format string or timestamp. 578 * @access public 579 */ 580 function restart($date = false) { 581 if ($date === false) { 582 $date = time(); 583 } 584 $this->browser->restart($date); 585 } 586 587 /** 588 * Moves cookie expiry times back into the past. 589 * Useful for testing timeouts and expiries. 590 * @param integer $interval Amount to age in seconds. 591 * @access public 592 */ 593 function ageCookies($interval) { 594 $this->browser->ageCookies($interval); 595 } 596 597 /** 598 * Disables frames support. Frames will not be fetched 599 * and the frameset page will be used instead. 600 * @access public 601 */ 602 function ignoreFrames() { 603 $this->browser->ignoreFrames(); 604 } 605 606 /** 607 * Switches off cookie sending and recieving. 608 * @access public 609 */ 610 function ignoreCookies() { 611 $this->browser->ignoreCookies(); 612 } 613 614 /** 615 * Skips errors for the next request only. You might 616 * want to confirm that a page is unreachable for 617 * example. 618 * @access public 619 */ 620 function ignoreErrors() { 621 $this->ignore_errors = true; 622 } 623 624 /** 625 * Issues a fail if there is a transport error anywhere 626 * in the current frameset. Only one such error is 627 * reported. 628 * @param string/boolean $result HTML or failure. 629 * @return string/boolean $result Passes through result. 630 * @access private 631 */ 632 protected function failOnError($result) { 633 if (! $this->ignore_errors) { 634 if ($error = $this->browser->getTransportError()) { 635 $this->fail($error); 636 } 637 } 638 $this->ignore_errors = false; 639 return $result; 640 } 641 642 /** 643 * Adds a header to every fetch. 644 * @param string $header Header line to add to every 645 * request until cleared. 646 * @access public 647 */ 648 function addHeader($header) { 649 $this->browser->addHeader($header); 650 } 651 652 /** 653 * Sets the maximum number of redirects before 654 * the web page is loaded regardless. 655 * @param integer $max Maximum hops. 656 * @access public 657 */ 658 function setMaximumRedirects($max) { 659 if (! $this->browser) { 660 trigger_error( 661 'Can only set maximum redirects in a test method, setUp() or tearDown()'); 662 } 663 $this->browser->setMaximumRedirects($max); 664 } 665 666 /** 667 * Sets the socket timeout for opening a connection and 668 * receiving at least one byte of information. 669 * @param integer $timeout Maximum time in seconds. 670 * @access public 671 */ 672 function setConnectionTimeout($timeout) { 673 $this->browser->setConnectionTimeout($timeout); 674 } 675 676 /** 677 * Sets proxy to use on all requests for when 678 * testing from behind a firewall. Set URL 679 * to false to disable. 680 * @param string $proxy Proxy URL. 681 * @param string $username Proxy username for authentication. 682 * @param string $password Proxy password for authentication. 683 * @access public 684 */ 685 function useProxy($proxy, $username = false, $password = false) { 686 $this->browser->useProxy($proxy, $username, $password); 687 } 688 689 /** 690 * Fetches a page into the page buffer. If 691 * there is no base for the URL then the 692 * current base URL is used. After the fetch 693 * the base URL reflects the new location. 694 * @param string $url URL to fetch. 695 * @param hash $parameters Optional additional GET data. 696 * @return boolean/string Raw page on success. 697 * @access public 698 */ 699 function get($url, $parameters = false) { 700 return $this->failOnError($this->browser->get($url, $parameters)); 701 } 702 703 /** 704 * Fetches a page by POST into the page buffer. 705 * If there is no base for the URL then the 706 * current base URL is used. After the fetch 707 * the base URL reflects the new location. 708 * @param string $url URL to fetch. 709 * @param mixed $parameters Optional POST parameters or content body to send 710 * @param string $content_type Content type of provided body 711 * @return boolean/string Raw page on success. 712 * @access public 713 */ 714 function post($url, $parameters = false, $content_type = false) { 715 return $this->failOnError($this->browser->post($url, $parameters, $content_type)); 716 } 717 718 /** 719 * Fetches a page by PUT into the page buffer. 720 * If there is no base for the URL then the 721 * current base URL is used. After the fetch 722 * the base URL reflects the new location. 723 * @param string $url URL to fetch. 724 * @param mixed $body Optional content body to send 725 * @param string $content_type Content type of provided body 726 * @return boolean/string Raw page on success. 727 * @access public 728 */ 729 function put($url, $body = false, $content_type = false) { 730 return $this->failOnError($this->browser->put($url, $body, $content_type)); 731 } 732 733 /** 734 * Fetches a page by a DELETE request 735 * @param string $url URL to fetch. 736 * @param hash $parameters Optional additional parameters. 737 * @return boolean/string Raw page on success. 738 * @access public 739 */ 740 function delete($url, $parameters = false) { 741 return $this->failOnError($this->browser->delete($url, $parameters)); 742 } 743 744 745 /** 746 * Does a HTTP HEAD fetch, fetching only the page 747 * headers. The current base URL is unchanged by this. 748 * @param string $url URL to fetch. 749 * @param hash $parameters Optional additional GET data. 750 * @return boolean True on success. 751 * @access public 752 */ 753 function head($url, $parameters = false) { 754 return $this->failOnError($this->browser->head($url, $parameters)); 755 } 756 757 /** 758 * Equivalent to hitting the retry button on the 759 * browser. Will attempt to repeat the page fetch. 760 * @return boolean True if fetch succeeded. 761 * @access public 762 */ 763 function retry() { 764 return $this->failOnError($this->browser->retry()); 765 } 766 767 /** 768 * Equivalent to hitting the back button on the 769 * browser. 770 * @return boolean True if history entry and 771 * fetch succeeded. 772 * @access public 773 */ 774 function back() { 775 return $this->failOnError($this->browser->back()); 776 } 777 778 /** 779 * Equivalent to hitting the forward button on the 780 * browser. 781 * @return boolean True if history entry and 782 * fetch succeeded. 783 * @access public 784 */ 785 function forward() { 786 return $this->failOnError($this->browser->forward()); 787 } 788 789 /** 790 * Retries a request after setting the authentication 791 * for the current realm. 792 * @param string $username Username for realm. 793 * @param string $password Password for realm. 794 * @return boolean/string HTML on successful fetch. Note 795 * that authentication may still have 796 * failed. 797 * @access public 798 */ 799 function authenticate($username, $password) { 800 return $this->failOnError( 801 $this->browser->authenticate($username, $password)); 802 } 803 804 /** 805 * Gets the cookie value for the current browser context. 806 * @param string $name Name of cookie. 807 * @return string Value of cookie or false if unset. 808 * @access public 809 */ 810 function getCookie($name) { 811 return $this->browser->getCurrentCookieValue($name); 812 } 813 814 /** 815 * Sets a cookie in the current browser. 816 * @param string $name Name of cookie. 817 * @param string $value Cookie value. 818 * @param string $host Host upon which the cookie is valid. 819 * @param string $path Cookie path if not host wide. 820 * @param string $expiry Expiry date. 821 * @access public 822 */ 823 function setCookie($name, $value, $host = false, $path = '/', $expiry = false) { 824 $this->browser->setCookie($name, $value, $host, $path, $expiry); 825 } 826 827 /** 828 * Accessor for current frame focus. Will be 829 * false if no frame has focus. 830 * @return integer/string/boolean Label if any, otherwise 831 * the position in the frameset 832 * or false if none. 833 * @access public 834 */ 835 function getFrameFocus() { 836 return $this->browser->getFrameFocus(); 837 } 838 839 /** 840 * Sets the focus by index. The integer index starts from 1. 841 * @param integer $choice Chosen frame. 842 * @return boolean True if frame exists. 843 * @access public 844 */ 845 function setFrameFocusByIndex($choice) { 846 return $this->browser->setFrameFocusByIndex($choice); 847 } 848 849 /** 850 * Sets the focus by name. 851 * @param string $name Chosen frame. 852 * @return boolean True if frame exists. 853 * @access public 854 */ 855 function setFrameFocus($name) { 856 return $this->browser->setFrameFocus($name); 857 } 858 859 /** 860 * Clears the frame focus. All frames will be searched 861 * for content. 862 * @access public 863 */ 864 function clearFrameFocus() { 865 return $this->browser->clearFrameFocus(); 866 } 867 868 /** 869 * Clicks a visible text item. Will first try buttons, 870 * then links and then images. 871 * @param string $label Visible text or alt text. 872 * @return string/boolean Raw page or false. 873 * @access public 874 */ 875 function click($label) { 876 return $this->failOnError($this->browser->click($label)); 877 } 878 879 /** 880 * Checks for a click target. 881 * @param string $label Visible text or alt text. 882 * @return boolean True if click target. 883 * @access public 884 */ 885 function assertClickable($label, $message = '%s') { 886 return $this->assertTrue( 887 $this->browser->isClickable($label), 888 sprintf($message, "Click target [$label] should exist")); 889 } 890 891 /** 892 * Clicks the submit button by label. The owning 893 * form will be submitted by this. 894 * @param string $label Button label. An unlabeled 895 * button can be triggered by 'Submit'. 896 * @param hash $additional Additional form values. 897 * @return boolean/string Page on success, else false. 898 * @access public 899 */ 900 function clickSubmit($label = 'Submit', $additional = false) { 901 return $this->failOnError( 902 $this->browser->clickSubmit($label, $additional)); 903 } 904 905 /** 906 * Clicks the submit button by name attribute. The owning 907 * form will be submitted by this. 908 * @param string $name Name attribute of button. 909 * @param hash $additional Additional form values. 910 * @return boolean/string Page on success. 911 * @access public 912 */ 913 function clickSubmitByName($name, $additional = false) { 914 return $this->failOnError( 915 $this->browser->clickSubmitByName($name, $additional)); 916 } 917 918 /** 919 * Clicks the submit button by ID attribute. The owning 920 * form will be submitted by this. 921 * @param string $id ID attribute of button. 922 * @param hash $additional Additional form values. 923 * @return boolean/string Page on success. 924 * @access public 925 */ 926 function clickSubmitById($id, $additional = false) { 927 return $this->failOnError( 928 $this->browser->clickSubmitById($id, $additional)); 929 } 930 931 /** 932 * Checks for a valid button label. 933 * @param string $label Visible text. 934 * @return boolean True if click target. 935 * @access public 936 */ 937 function assertSubmit($label, $message = '%s') { 938 return $this->assertTrue( 939 $this->browser->isSubmit($label), 940 sprintf($message, "Submit button [$label] should exist")); 941 } 942 943 /** 944 * Clicks the submit image by some kind of label. Usually 945 * the alt tag or the nearest equivalent. The owning 946 * form will be submitted by this. Clicking outside of 947 * the boundary of the coordinates will result in 948 * a failure. 949 * @param string $label Alt attribute of button. 950 * @param integer $x X-coordinate of imaginary click. 951 * @param integer $y Y-coordinate of imaginary click. 952 * @param hash $additional Additional form values. 953 * @return boolean/string Page on success. 954 * @access public 955 */ 956 function clickImage($label, $x = 1, $y = 1, $additional = false) { 957 return $this->failOnError( 958 $this->browser->clickImage($label, $x, $y, $additional)); 959 } 960 961 /** 962 * Clicks the submit image by the name. Usually 963 * the alt tag or the nearest equivalent. The owning 964 * form will be submitted by this. Clicking outside of 965 * the boundary of the coordinates will result in 966 * a failure. 967 * @param string $name Name attribute of button. 968 * @param integer $x X-coordinate of imaginary click. 969 * @param integer $y Y-coordinate of imaginary click. 970 * @param hash $additional Additional form values. 971 * @return boolean/string Page on success. 972 * @access public 973 */ 974 function clickImageByName($name, $x = 1, $y = 1, $additional = false) { 975 return $this->failOnError( 976 $this->browser->clickImageByName($name, $x, $y, $additional)); 977 } 978 979 /** 980 * Clicks the submit image by ID attribute. The owning 981 * form will be submitted by this. Clicking outside of 982 * the boundary of the coordinates will result in 983 * a failure. 984 * @param integer/string $id ID attribute of button. 985 * @param integer $x X-coordinate of imaginary click. 986 * @param integer $y Y-coordinate of imaginary click. 987 * @param hash $additional Additional form values. 988 * @return boolean/string Page on success. 989 * @access public 990 */ 991 function clickImageById($id, $x = 1, $y = 1, $additional = false) { 992 return $this->failOnError( 993 $this->browser->clickImageById($id, $x, $y, $additional)); 994 } 995 996 /** 997 * Checks for a valid image with atht alt text or title. 998 * @param string $label Visible text. 999 * @return boolean True if click target. 1000 * @access public 1001 */ 1002 function assertImage($label, $message = '%s') { 1003 return $this->assertTrue( 1004 $this->browser->isImage($label), 1005 sprintf($message, "Image with text [$label] should exist")); 1006 } 1007 1008 /** 1009 * Submits a form by the ID. 1010 * @param string $id Form ID. No button information 1011 * is submitted this way. 1012 * @return boolean/string Page on success. 1013 * @access public 1014 */ 1015 function submitFormById($id, $additional = false) { 1016 return $this->failOnError($this->browser->submitFormById($id, $additional)); 1017 } 1018 1019 /** 1020 * Follows a link by name. Will click the first link 1021 * found with this link text by default, or a later 1022 * one if an index is given. Match is case insensitive 1023 * with normalised space. 1024 * @param string $label Text between the anchor tags. 1025 * @param integer $index Link position counting from zero. 1026 * @return boolean/string Page on success. 1027 * @access public 1028 */ 1029 function clickLink($label, $index = 0) { 1030 return $this->failOnError($this->browser->clickLink($label, $index)); 1031 } 1032 1033 /** 1034 * Follows a link by id attribute. 1035 * @param string $id ID attribute value. 1036 * @return boolean/string Page on success. 1037 * @access public 1038 */ 1039 function clickLinkById($id) { 1040 return $this->failOnError($this->browser->clickLinkById($id)); 1041 } 1042 1043 /** 1044 * Tests for the presence of a link label. Match is 1045 * case insensitive with normalised space. 1046 * @param string $label Text between the anchor tags. 1047 * @param mixed $expected Expected URL or expectation object. 1048 * @param string $message Message to display. Default 1049 * can be embedded with %s. 1050 * @return boolean True if link present. 1051 * @access public 1052 */ 1053 function assertLink($label, $expected = true, $message = '%s') { 1054 $url = $this->browser->getLink($label); 1055 if ($expected === true || ($expected !== true && $url === false)) { 1056 return $this->assertTrue($url !== false, sprintf($message, "Link [$label] should exist")); 1057 } 1058 if (! SimpleExpectation::isExpectation($expected)) { 1059 $expected = new IdenticalExpectation($expected); 1060 } 1061 return $this->assert($expected, $url->asString(), sprintf($message, "Link [$label] should match")); 1062 } 1063 1064 /** 1065 * Tests for the non-presence of a link label. Match is 1066 * case insensitive with normalised space. 1067 * @param string/integer $label Text between the anchor tags 1068 * or ID attribute. 1069 * @param string $message Message to display. Default 1070 * can be embedded with %s. 1071 * @return boolean True if link missing. 1072 * @access public 1073 */ 1074 function assertNoLink($label, $message = '%s') { 1075 return $this->assertTrue( 1076 $this->browser->getLink($label) === false, 1077 sprintf($message, "Link [$label] should not exist")); 1078 } 1079 1080 /** 1081 * Tests for the presence of a link id attribute. 1082 * @param string $id Id attribute value. 1083 * @param mixed $expected Expected URL or expectation object. 1084 * @param string $message Message to display. Default 1085 * can be embedded with %s. 1086 * @return boolean True if link present. 1087 * @access public 1088 */ 1089 function assertLinkById($id, $expected = true, $message = '%s') { 1090 $url = $this->browser->getLinkById($id); 1091 if ($expected === true) { 1092 return $this->assertTrue($url !== false, sprintf($message, "Link ID [$id] should exist")); 1093 } 1094 if (! SimpleExpectation::isExpectation($expected)) { 1095 $expected = new IdenticalExpectation($expected); 1096 } 1097 return $this->assert($expected, $url->asString(), sprintf($message, "Link ID [$id] should match")); 1098 } 1099 1100 /** 1101 * Tests for the non-presence of a link label. Match is 1102 * case insensitive with normalised space. 1103 * @param string $id Id attribute value. 1104 * @param string $message Message to display. Default 1105 * can be embedded with %s. 1106 * @return boolean True if link missing. 1107 * @access public 1108 */ 1109 function assertNoLinkById($id, $message = '%s') { 1110 return $this->assertTrue( 1111 $this->browser->getLinkById($id) === false, 1112 sprintf($message, "Link ID [$id] should not exist")); 1113 } 1114 1115 /** 1116 * Sets all form fields with that label, or name if there 1117 * is no label attached. 1118 * @param string $name Name of field in forms. 1119 * @param string $value New value of field. 1120 * @return boolean True if field exists, otherwise false. 1121 * @access public 1122 */ 1123 function setField($label, $value, $position=false) { 1124 return $this->browser->setField($label, $value, $position); 1125 } 1126 1127 /** 1128 * Sets all form fields with that name. 1129 * @param string $name Name of field in forms. 1130 * @param string $value New value of field. 1131 * @return boolean True if field exists, otherwise false. 1132 * @access public 1133 */ 1134 function setFieldByName($name, $value, $position=false) { 1135 return $this->browser->setFieldByName($name, $value, $position); 1136 } 1137 1138 /** 1139 * Sets all form fields with that id. 1140 * @param string/integer $id Id of field in forms. 1141 * @param string $value New value of field. 1142 * @return boolean True if field exists, otherwise false. 1143 * @access public 1144 */ 1145 function setFieldById($id, $value) { 1146 return $this->browser->setFieldById($id, $value); 1147 } 1148 1149 /** 1150 * Confirms that the form element is currently set 1151 * to the expected value. A missing form will always 1152 * fail. If no value is given then only the existence 1153 * of the field is checked. 1154 * @param string $name Name of field in forms. 1155 * @param mixed $expected Expected string/array value or 1156 * false for unset fields. 1157 * @param string $message Message to display. Default 1158 * can be embedded with %s. 1159 * @return boolean True if pass. 1160 * @access public 1161 */ 1162 function assertField($label, $expected = true, $message = '%s') { 1163 $value = $this->browser->getField($label); 1164 return $this->assertFieldValue($label, $value, $expected, $message); 1165 } 1166 1167 /** 1168 * Confirms that the form element is currently set 1169 * to the expected value. A missing form element will always 1170 * fail. If no value is given then only the existence 1171 * of the field is checked. 1172 * @param string $name Name of field in forms. 1173 * @param mixed $expected Expected string/array value or 1174 * false for unset fields. 1175 * @param string $message Message to display. Default 1176 * can be embedded with %s. 1177 * @return boolean True if pass. 1178 * @access public 1179 */ 1180 function assertFieldByName($name, $expected = true, $message = '%s') { 1181 $value = $this->browser->getFieldByName($name); 1182 return $this->assertFieldValue($name, $value, $expected, $message); 1183 } 1184 1185 /** 1186 * Confirms that the form element is currently set 1187 * to the expected value. A missing form will always 1188 * fail. If no ID is given then only the existence 1189 * of the field is checked. 1190 * @param string/integer $id Name of field in forms. 1191 * @param mixed $expected Expected string/array value or 1192 * false for unset fields. 1193 * @param string $message Message to display. Default 1194 * can be embedded with %s. 1195 * @return boolean True if pass. 1196 * @access public 1197 */ 1198 function assertFieldById($id, $expected = true, $message = '%s') { 1199 $value = $this->browser->getFieldById($id); 1200 return $this->assertFieldValue($id, $value, $expected, $message); 1201 } 1202 1203 /** 1204 * Tests the field value against the expectation. 1205 * @param string $identifier Name, ID or label. 1206 * @param mixed $value Current field value. 1207 * @param mixed $expected Expected value to match. 1208 * @param string $message Failure message. 1209 * @return boolean True if pass 1210 * @access protected 1211 */ 1212 protected function assertFieldValue($identifier, $value, $expected, $message) { 1213 if ($expected === true) { 1214 return $this->assertTrue( 1215 isset($value), 1216 sprintf($message, "Field [$identifier] should exist")); 1217 } 1218 if (! SimpleExpectation::isExpectation($expected)) { 1219 $identifier = str_replace('%', '%%', $identifier); 1220 $expected = new FieldExpectation( 1221 $expected, 1222 "Field [$identifier] should match with [%s]"); 1223 } 1224 return $this->assert($expected, $value, $message); 1225 } 1226 1227 /** 1228 * Checks the response code against a list 1229 * of possible values. 1230 * @param array $responses Possible responses for a pass. 1231 * @param string $message Message to display. Default 1232 * can be embedded with %s. 1233 * @return boolean True if pass. 1234 * @access public 1235 */ 1236 function assertResponse($responses, $message = '%s') { 1237 $responses = (is_array($responses) ? $responses : array($responses)); 1238 $code = $this->browser->getResponseCode(); 1239 $message = sprintf($message, "Expecting response in [" . 1240 implode(", ", $responses) . "] got [$code]"); 1241 return $this->assertTrue(in_array($code, $responses), $message); 1242 } 1243 1244 /** 1245 * Checks the mime type against a list 1246 * of possible values. 1247 * @param array $types Possible mime types for a pass. 1248 * @param string $message Message to display. 1249 * @return boolean True if pass. 1250 * @access public 1251 */ 1252 function assertMime($types, $message = '%s') { 1253 $types = (is_array($types) ? $types : array($types)); 1254 $type = $this->browser->getMimeType(); 1255 $message = sprintf($message, "Expecting mime type in [" . 1256 implode(", ", $types) . "] got [$type]"); 1257 return $this->assertTrue(in_array($type, $types), $message); 1258 } 1259 1260 /** 1261 * Attempt to match the authentication type within 1262 * the security realm we are currently matching. 1263 * @param string $authentication Usually basic. 1264 * @param string $message Message to display. 1265 * @return boolean True if pass. 1266 * @access public 1267 */ 1268 function assertAuthentication($authentication = false, $message = '%s') { 1269 if (! $authentication) { 1270 $message = sprintf($message, "Expected any authentication type, got [" . 1271 $this->browser->getAuthentication() . "]"); 1272 return $this->assertTrue( 1273 $this->browser->getAuthentication(), 1274 $message); 1275 } else { 1276 $message = sprintf($message, "Expected authentication [$authentication] got [" . 1277 $this->browser->getAuthentication() . "]"); 1278 return $this->assertTrue( 1279 strtolower($this->browser->getAuthentication()) == strtolower($authentication), 1280 $message); 1281 } 1282 } 1283 1284 /** 1285 * Checks that no authentication is necessary to view 1286 * the desired page. 1287 * @param string $message Message to display. 1288 * @return boolean True if pass. 1289 * @access public 1290 */ 1291 function assertNoAuthentication($message = '%s') { 1292 $message = sprintf($message, "Expected no authentication type, got [" . 1293 $this->browser->getAuthentication() . "]"); 1294 return $this->assertFalse($this->browser->getAuthentication(), $message); 1295 } 1296 1297 /** 1298 * Attempts to match the current security realm. 1299 * @param string $realm Name of security realm. 1300 * @param string $message Message to display. 1301 * @return boolean True if pass. 1302 * @access public 1303 */ 1304 function assertRealm($realm, $message = '%s') { 1305 if (! SimpleExpectation::isExpectation($realm)) { 1306 $realm = new EqualExpectation($realm); 1307 } 1308 return $this->assert( 1309 $realm, 1310 $this->browser->getRealm(), 1311 "Expected realm -> $message"); 1312 } 1313 1314 /** 1315 * Checks each header line for the required value. If no 1316 * value is given then only an existence check is made. 1317 * @param string $header Case insensitive header name. 1318 * @param mixed $value Case sensitive trimmed string to 1319 * match against. An expectation object 1320 * can be used for pattern matching. 1321 * @return boolean True if pass. 1322 * @access public 1323 */ 1324 function assertHeader($header, $value = false, $message = '%s') { 1325 return $this->assert( 1326 new HttpHeaderExpectation($header, $value), 1327 $this->browser->getHeaders(), 1328 $message); 1329 } 1330 1331 /** 1332 * Confirms that the header type has not been received. 1333 * Only the landing page is checked. If you want to check 1334 * redirect pages, then you should limit redirects so 1335 * as to capture the page you want. 1336 * @param string $header Case insensitive header name. 1337 * @return boolean True if pass. 1338 * @access public 1339 */ 1340 function assertNoHeader($header, $message = '%s') { 1341 return $this->assert( 1342 new NoHttpHeaderExpectation($header), 1343 $this->browser->getHeaders(), 1344 $message); 1345 } 1346 1347 /** 1348 * Tests the text between the title tags. 1349 * @param string/SimpleExpectation $title Expected title. 1350 * @param string $message Message to display. 1351 * @return boolean True if pass. 1352 * @access public 1353 */ 1354 function assertTitle($title = false, $message = '%s') { 1355 if (! SimpleExpectation::isExpectation($title)) { 1356 $title = new EqualExpectation($title); 1357 } 1358 return $this->assert($title, $this->browser->getTitle(), $message); 1359 } 1360 1361 /** 1362 * Will trigger a pass if the text is found in the plain 1363 * text form of the page. 1364 * @param string $text Text to look for. 1365 * @param string $message Message to display. 1366 * @return boolean True if pass. 1367 * @access public 1368 */ 1369 function assertText($text, $message = '%s') { 1370 return $this->assert( 1371 new TextExpectation($text), 1372 $this->browser->getContentAsText(), 1373 $message); 1374 } 1375 1376 /** 1377 * Will trigger a pass if the text is not found in the plain 1378 * text form of the page. 1379 * @param string $text Text to look for. 1380 * @param string $message Message to display. 1381 * @return boolean True if pass. 1382 * @access public 1383 */ 1384 function assertNoText($text, $message = '%s') { 1385 return $this->assert( 1386 new NoTextExpectation($text), 1387 $this->browser->getContentAsText(), 1388 $message); 1389 } 1390 1391 /** 1392 * Will trigger a pass if the Perl regex pattern 1393 * is found in the raw content. 1394 * @param string $pattern Perl regex to look for including 1395 * the regex delimiters. 1396 * @param string $message Message to display. 1397 * @return boolean True if pass. 1398 * @access public 1399 */ 1400 function assertPattern($pattern, $message = '%s') { 1401 return $this->assert( 1402 new PatternExpectation($pattern), 1403 $this->browser->getContent(), 1404 $message); 1405 } 1406 1407 /** 1408 * Will trigger a pass if the perl regex pattern 1409 * is not present in raw content. 1410 * @param string $pattern Perl regex to look for including 1411 * the regex delimiters. 1412 * @param string $message Message to display. 1413 * @return boolean True if pass. 1414 * @access public 1415 */ 1416 function assertNoPattern($pattern, $message = '%s') { 1417 return $this->assert( 1418 new NoPatternExpectation($pattern), 1419 $this->browser->getContent(), 1420 $message); 1421 } 1422 1423 /** 1424 * Checks that a cookie is set for the current page 1425 * and optionally checks the value. 1426 * @param string $name Name of cookie to test. 1427 * @param string $expected Expected value as a string or 1428 * false if any value will do. 1429 * @param string $message Message to display. 1430 * @return boolean True if pass. 1431 * @access public 1432 */ 1433 function assertCookie($name, $expected = false, $message = '%s') { 1434 $value = $this->getCookie($name); 1435 if (! $expected) { 1436 return $this->assertTrue( 1437 $value, 1438 sprintf($message, "Expecting cookie [$name]")); 1439 } 1440 if (! SimpleExpectation::isExpectation($expected)) { 1441 $expected = new EqualExpectation($expected); 1442 } 1443 return $this->assert($expected, $value, "Expecting cookie [$name] -> $message"); 1444 } 1445 1446 /** 1447 * Checks that no cookie is present or that it has 1448 * been successfully cleared. 1449 * @param string $name Name of cookie to test. 1450 * @param string $message Message to display. 1451 * @return boolean True if pass. 1452 * @access public 1453 */ 1454 function assertNoCookie($name, $message = '%s') { 1455 return $this->assertTrue( 1456 $this->getCookie($name) === null or $this->getCookie($name) === false, 1457 sprintf($message, "Not expecting cookie [$name]")); 1458 } 1459 1460 /** 1461 * Called from within the test methods to register 1462 * passes and failures. 1463 * @param boolean $result Pass on true. 1464 * @param string $message Message to display describing 1465 * the test state. 1466 * @return boolean True on pass 1467 * @access public 1468 */ 1469 function assertTrue($result, $message = '%s') { 1470 return $this->assert(new TrueExpectation(), $result, $message); 1471 } 1472 1473 /** 1474 * Will be true on false and vice versa. False 1475 * is the PHP definition of false, so that null, 1476 * empty strings, zero and an empty array all count 1477 * as false. 1478 * @param boolean $result Pass on false. 1479 * @param string $message Message to display. 1480 * @return boolean True on pass 1481 * @access public 1482 */ 1483 function assertFalse($result, $message = '%s') { 1484 return $this->assert(new FalseExpectation(), $result, $message); 1485 } 1486 1487 /** 1488 * Will trigger a pass if the two parameters have 1489 * the same value only. Otherwise a fail. This 1490 * is for testing hand extracted text, etc. 1491 * @param mixed $first Value to compare. 1492 * @param mixed $second Value to compare. 1493 * @param string $message Message to display. 1494 * @return boolean True on pass 1495 * @access public 1496 */ 1497 function assertEqual($first, $second, $message = '%s') { 1498 return $this->assert( 1499 new EqualExpectation($first), 1500 $second, 1501 $message); 1502 } 1503 1504 /** 1505 * Will trigger a pass if the two parameters have 1506 * a different value. Otherwise a fail. This 1507 * is for testing hand extracted text, etc. 1508 * @param mixed $first Value to compare. 1509 * @param mixed $second Value to compare. 1510 * @param string $message Message to display. 1511 * @return boolean True on pass 1512 * @access public 1513 */ 1514 function assertNotEqual($first, $second, $message = '%s') { 1515 return $this->assert( 1516 new NotEqualExpectation($first), 1517 $second, 1518 $message); 1519 } 1520 1521 /** 1522 * Uses a stack trace to find the line of an assertion. 1523 * @return string Line number of first assert* 1524 * method embedded in format string. 1525 * @access public 1526 */ 1527 function getAssertionLine() { 1528 $trace = new SimpleStackTrace(array('assert', 'click', 'pass', 'fail')); 1529 return $trace->traceMethod(); 1530 } 1531} 1532?>