3 * Base include file for SimpleTest
5 * @subpackage WebTester
6 * @version $Id: browser.php,v 1.133 2004/08/18 23:10:19 lastcraft Exp $
10 * include other SimpleTest class files
12 require_once(dirname(__FILE__
) . '/options.php');
13 require_once(dirname(__FILE__
) . '/http.php');
14 require_once(dirname(__FILE__
) . '/page.php');
15 require_once(dirname(__FILE__
) . '/frames.php');
16 require_once(dirname(__FILE__
) . '/user_agent.php');
20 * Browser history list.
22 * @subpackage WebTester
24 class SimpleBrowserHistory
{
32 function SimpleBrowserHistory() {
33 $this->_sequence
= array();
34 $this->_position
= -1;
38 * Test for no entries yet.
39 * @return boolean True if empty.
43 return ($this->_position
== -1);
47 * Test for being at the beginning.
48 * @return boolean True if first.
51 function _atBeginning() {
52 return ($this->_position
== 0) && ! $this->_isEmpty();
56 * Test for being at the last entry.
57 * @return boolean True if last.
61 return ($this->_position +
1 >= count($this->_sequence
)) && ! $this->_isEmpty();
65 * Adds a successfully fetched page to the history.
66 * @param string $method GET or POST.
67 * @param SimpleUrl $url URL of fetch.
68 * @param array $parameters Any post data with the fetch.
71 function recordEntry($method, $url, $parameters) {
75 array('method' => $method, 'url' => $url, 'parameters' => $parameters));
80 * Last fetching method for current history
82 * @return string GET or POST for this point in
86 function getMethod() {
87 if ($this->_isEmpty()) {
90 return $this->_sequence
[$this->_position
]['method'];
94 * Last fully qualified URL for current history
96 * @return SimpleUrl URL for this position.
100 if ($this->_isEmpty()) {
103 return $this->_sequence
[$this->_position
]['url'];
107 * Parameters of last fetch from current history
109 * @return array Hash of post parameters.
112 function getParameters() {
113 if ($this->_isEmpty()) {
116 return $this->_sequence
[$this->_position
]['parameters'];
120 * Step back one place in the history. Stops at
122 * @return boolean True if any previous entries.
126 if ($this->_isEmpty() ||
$this->_atBeginning()) {
134 * Step forward one place. If already at the
135 * latest entry then nothing will happen.
136 * @return boolean True if any future entries.
140 if ($this->_isEmpty() ||
$this->_atEnd()) {
148 * Ditches all future entries beyond the current
152 function _dropFuture() {
153 if ($this->_isEmpty()) {
156 while (! $this->_atEnd()) {
157 array_pop($this->_sequence
);
163 * Simulated web browser. This is an aggregate of
164 * the user agent, the HTML parsing, request history
165 * and the last header set.
166 * @package SimpleTest
167 * @subpackage WebTester
169 class SimpleBrowser
{
176 * Starts with a fresh browser with no
177 * cookie or any other state information. The
178 * exception is that a default proxy will be
179 * set up if specified in the options.
182 function SimpleBrowser() {
183 $this->_user_agent
= &$this->_createUserAgent();
184 $this->_user_agent
->useProxy(
185 SimpleTestOptions
::getDefaultProxy(),
186 SimpleTestOptions
::getDefaultProxyUsername(),
187 SimpleTestOptions
::getDefaultProxyPassword());
188 $this->_page
= &new SimplePage();
189 $this->_history
= &$this->_createHistory();
190 $this->_ignore_frames
= false
;
194 * Creates the underlying user agent.
195 * @return SimpleFetcher Content fetcher.
198 function &_createUserAgent() {
199 return new SimpleUserAgent();
203 * Creates a new empty history list.
204 * @return SimpleBrowserHistory New list.
207 function &_createHistory() {
208 return new SimpleBrowserHistory();
212 * Disables frames support. Frames will not be fetched
213 * and the frameset page will be used instead.
216 function ignoreFrames() {
217 $this->_ignore_frames
= true
;
221 * Enables frames support. Frames will be fetched from
225 function useFrames() {
226 $this->_ignore_frames
= false
;
230 * Parses the raw content into a page. Will load further
231 * frame pages unless frames are disabled.
232 * @param SimpleHttpResponse $response Response from fetch.
233 * @return SimplePage Parsed HTML.
236 function &_parse($response) {
237 $builder = &new SimplePageBuilder();
238 $page = &$builder->parse($response);
239 if ($this->_ignore_frames ||
! $page->hasFrames()) {
242 $frameset = &new SimpleFrameset($page);
243 foreach ($page->getFrameset() as $key => $url) {
244 $frame = &$this->_fetch('GET', $url, array());
245 $frameset->addFrame($frame, $key);
252 * @param string $method GET or POST.
253 * @param string/SimpleUrl $url Target to fetch as string.
254 * @param hash $parameters POST parameters.
255 * @return SimplePage Parsed page.
258 function &_fetch($method, $url, $parameters) {
259 $response = &$this->_user_agent
->fetchResponse($method, $url, $parameters);
260 if ($response->isError()) {
261 return new SimplePage($response);
263 return $this->_parse($response);
267 * Fetches a page or a single frame if that is the current
269 * @param string $method GET or POST.
270 * @param string/SimpleUrl $url Target to fetch as string.
271 * @param hash $parameters POST parameters.
272 * @return string Raw content of page.
275 function _load($method, $url, $parameters = false
) {
276 $frame = $url->getTarget();
277 if (! $frame ||
(strtolower($frame) == '_top')) {
278 return $this->_loadPage($method, $url, $parameters);
280 return $this->_loadFrame(array($frame), $method, $url, $parameters);
284 * Fetches a page and makes it the current page/frame.
285 * @param string $method GET or POST.
286 * @param string/SimpleUrl $url Target to fetch as string.
287 * @param hash $parameters POST parameters.
288 * @return string Raw content of page.
291 function _loadPage($method, $url, $parameters = false
) {
292 $this->_page
= &$this->_fetch(strtoupper($method), $url, $parameters);
293 $this->_history
->recordEntry(
294 $this->_page
->getMethod(),
295 $this->_page
->getUrl(),
296 $this->_page
->getRequestData());
297 return $this->_page
->getRaw();
301 * Fetches a frame into the existing frameset replacing the
303 * @param array $frames List of names to drill down.
304 * @param string $method GET or POST.
305 * @param string/SimpleUrl $url Target to fetch as string.
306 * @param hash $parameters POST parameters.
307 * @return string Raw content of page.
310 function _loadFrame($frames, $method, $url, $parameters = false
) {
311 $page = &$this->_fetch(strtoupper($method), $url, $parameters);
312 $this->_page
->setFrame($frames, $page);
316 * Removes expired and temporary cookies as if
317 * the browser was closed and re-opened.
318 * @param string/integer $date Time when session restarted.
319 * If omitted then all persistent
323 function restartSession($date = false
) {
324 $this->_user_agent
->restartSession($date);
328 * Adds a header to every fetch.
329 * @param string $header Header line to add to every
330 * request until cleared.
333 function addHeader($header) {
334 $this->_user_agent
->addHeader($header);
338 * Ages the cookies by the specified time.
339 * @param integer $interval Amount in seconds.
342 function ageCookies($interval) {
343 $this->_user_agent
->ageCookies($interval);
347 * Sets an additional cookie. If a cookie has
348 * the same name and path it is replaced.
349 * @param string $name Cookie key.
350 * @param string $value Value of cookie.
351 * @param string $host Host upon which the cookie is valid.
352 * @param string $path Cookie path if not host wide.
353 * @param string $expiry Expiry date.
356 function setCookie($name, $value, $host = false
, $path = '/', $expiry = false
) {
357 $this->_user_agent
->setCookie($name, $value, $host, $path, $expiry);
361 * Reads the most specific cookie value from the
363 * @param string $host Host to search.
364 * @param string $path Applicable path.
365 * @param string $name Name of cookie to read.
366 * @return string False if not present, else the
370 function getCookieValue($host, $path, $name) {
371 return $this->_user_agent
->getCookieValue($host, $path, $name);
375 * Reads the current cookies for the current URL.
376 * @param string $name Key of cookie to find.
377 * @return string Null if there is no current URL, false
378 * if the cookie is not set.
381 function getCurrentCookieValue($name) {
382 return $this->_user_agent
->getBaseCookieValue($name, $this->_page
->getUrl());
386 * Sets the maximum number of redirects before
387 * a page will be loaded anyway.
388 * @param integer $max Most hops allowed.
391 function setMaximumRedirects($max) {
392 $this->_user_agent
->setMaximumRedirects($max);
396 * Sets the socket timeout for opening a connection.
397 * @param integer $timeout Maximum time in seconds.
400 function setConnectionTimeout($timeout) {
401 $this->_user_agent
->setConnectionTimeout($timeout);
405 * Sets proxy to use on all requests for when
406 * testing from behind a firewall. Set URL
407 * to false to disable.
408 * @param string $proxy Proxy URL.
409 * @param string $username Proxy username for authentication.
410 * @param string $password Proxy password for authentication.
413 function useProxy($proxy, $username = false
, $password = false
) {
414 $this->_user_agent
->useProxy($proxy, $username, $password);
418 * Fetches the page content with a HEAD request.
419 * Will affect cookies, but will not change the base URL.
420 * @param string/SimpleUrl $url Target to fetch as string.
421 * @param hash $parameters Additional parameters for GET request.
422 * @return boolean True if successful.
425 function head($url, $parameters = false
) {
426 if (! is_object($url)) {
427 $url = new SimpleUrl($url);
429 if ($this->getUrl()) {
430 $url = $url->makeAbsolute($this->getUrl());
432 $response = &$this->_user_agent
->fetchResponse(
436 return ! $response->isError();
440 * Fetches the page content with a simple GET request.
441 * @param string/SimpleUrl $url Target to fetch.
442 * @param hash $parameters Additional parameters for GET request.
443 * @return string Content of page or false.
446 function get($url, $parameters = false
) {
447 if (! is_object($url)) {
448 $url = new SimpleUrl($url);
450 if ($this->getUrl()) {
451 $url = $url->makeAbsolute($this->getUrl());
453 return $this->_load('GET', $url, $parameters);
457 * Fetches the page content with a POST request.
458 * @param string/SimpleUrl $url Target to fetch as string.
459 * @param hash $parameters POST parameters.
460 * @return string Content of page.
463 function post($url, $parameters = false
) {
464 if (! is_object($url)) {
465 $url = new SimpleUrl($url);
467 if ($this->getUrl()) {
468 $url = $url->makeAbsolute($this->getUrl());
470 return $this->_load('POST', $url, $parameters);
474 * Equivalent to hitting the retry button on the
475 * browser. Will attempt to repeat the page fetch. If
476 * there is no history to repeat it will give false.
477 * @return string/boolean Content if fetch succeeded
482 $frames = $this->_page
->getFrameFocus();
483 if (count($frames) > 0) {
486 $this->_page
->getMethod(),
487 $this->_page
->getUrl(),
488 $this->_page
->getRequestData());
489 return $this->_page
->getRaw();
491 if ($method = $this->_history
->getMethod()) {
492 $this->_page
= &$this->_fetch(
494 $this->_history
->getUrl(),
495 $this->_history
->getParameters());
496 return $this->_page
->getRaw();
502 * Equivalent to hitting the back button on the
503 * browser. The browser history is unchanged on
505 * @return boolean True if history entry and
510 if (! $this->_history
->back()) {
513 $content = $this->retry();
515 $this->_history
->forward();
521 * Equivalent to hitting the forward button on the
522 * browser. The browser history is unchanged on
524 * @return boolean True if history entry and
529 if (! $this->_history
->forward()) {
532 $content = $this->retry();
534 $this->_history
->back();
540 * Retries a request after setting the authentication
541 * for the current realm.
542 * @param string $username Username for realm.
543 * @param string $password Password for realm.
544 * @return boolean True if successful fetch. Note
545 * that authentication may still have
549 function authenticate($username, $password) {
550 if (! $this->_page
->getRealm()) {
553 $url = $this->_page
->getUrl();
557 $this->_user_agent
->setIdentity(
559 $this->_page
->getRealm(),
562 return $this->retry();
566 * Accessor for a breakdown of the frameset.
567 * @return array Hash tree of frames by name
568 * or index if no name.
571 function getFrames() {
572 return $this->_page
->getFrames();
576 * Accessor for current frame focus. Will be
577 * false if no frame has focus.
578 * @return integer/string/boolean Label if any, otherwise
579 * the position in the frameset
583 function getFrameFocus() {
584 return $this->_page
->getFrameFocus();
588 * Sets the focus by index. The integer index starts from 1.
589 * @param integer $choice Chosen frame.
590 * @return boolean True if frame exists.
593 function setFrameFocusByIndex($choice) {
594 return $this->_page
->setFrameFocusByIndex($choice);
598 * Sets the focus by name.
599 * @param string $name Chosen frame.
600 * @return boolean True if frame exists.
603 function setFrameFocus($name) {
604 return $this->_page
->setFrameFocus($name);
608 * Clears the frame focus. All frames will be searched
612 function clearFrameFocus() {
613 return $this->_page
->clearFrameFocus();
617 * Accessor for last error.
618 * @return string Error from last response.
621 function getTransportError() {
622 return $this->_page
->getTransportError();
626 * Accessor for current MIME type.
627 * @return string MIME type as string; e.g. 'text/html'
630 function getMimeType() {
631 return $this->_page
->getMimeType();
635 * Accessor for last response code.
636 * @return integer Last HTTP response code received.
639 function getResponseCode() {
640 return $this->_page
->getResponseCode();
644 * Accessor for last Authentication type. Only valid
645 * straight after a challenge (401).
646 * @return string Description of challenge type.
649 function getAuthentication() {
650 return $this->_page
->getAuthentication();
654 * Accessor for last Authentication realm. Only valid
655 * straight after a challenge (401).
656 * @return string Name of security realm.
659 function getRealm() {
660 return $this->_page
->getRealm();
664 * Accessor for current URL of page or frame if
666 * @return string Location of current page or frame as
670 $url = $this->_page
->getUrl();
671 return $url ?
$url->asString() : false
;
675 * Accessor for raw bytes sent down the wire.
676 * @return string Original text sent.
679 function getRequest() {
680 return $this->_page
->getRequest();
684 * Accessor for raw page information.
685 * @return string Original text content of web page.
688 function getContent() {
689 return $this->_page
->getRaw();
693 * Accessor for raw header information.
694 * @return string Header block.
697 function getHeaders() {
698 return $this->_page
->getHeaders();
702 * Accessor for parsed title.
703 * @return string Title or false if no title is present.
706 function getTitle() {
707 return $this->_page
->getTitle();
711 * Accessor for a list of all fixed links in current page.
712 * @return array List of urls with scheme of
713 * http or https and hostname.
716 function getAbsoluteUrls() {
717 return $this->_page
->getAbsoluteUrls();
721 * Accessor for a list of all relative links.
722 * @return array List of urls without hostname.
725 function getRelativeUrls() {
726 return $this->_page
->getRelativeUrls();
730 * Sets all form fields with that name.
731 * @param string $name Name of field in forms.
732 * @param string $value New value of field.
733 * @return boolean True if field exists, otherwise false.
736 function setField($name, $value) {
737 return $this->_page
->setField($name, $value);
741 * Sets all form fields with that name.
742 * @param string/integer $id Id of field in forms.
743 * @param string $value New value of field.
744 * @return boolean True if field exists, otherwise false.
747 function setFieldById($id, $value) {
748 return $this->_page
->setFieldById($id, $value);
752 * Accessor for a form element value within the page.
753 * Finds the first match.
754 * @param string $name Field name.
755 * @return string/boolean A string if the field is
756 * present, false if unchecked
757 * and null if missing.
760 function getField($name) {
761 return $this->_page
->getField($name);
765 * Accessor for a form element value within the page.
766 * @param string/integer $id Id of field in forms.
767 * @return string/boolean A string if the field is
768 * present, false if unchecked
769 * and null if missing.
772 function getFieldById($id) {
773 return $this->_page
->getFieldById($id);
777 * Clicks the submit button by label. The owning
778 * form will be submitted by this.
779 * @param string $label Button label. An unlabeled
780 * button can be triggered by 'Submit'.
781 * @return boolean True on success.
784 function clickSubmit($label = 'Submit') {
785 if (! ($form = &$this->_page
->getFormBySubmitLabel($label))) {
791 $form->submitButtonByLabel($label));
795 * Clicks the submit button by name attribute. The owning
796 * form will be submitted by this.
797 * @param string $name Button name.
798 * @return boolean True on success.
801 function clickSubmitByName($name) {
802 if (! ($form = &$this->_page
->getFormBySubmitName($name))) {
808 $form->submitButtonByName($name));
812 * Clicks the submit button by ID attribute of the button
813 * itself. The owning form will be submitted by this.
814 * @param string $id Button ID.
815 * @return boolean True on success.
818 function clickSubmitById($id) {
819 if (! ($form = &$this->_page
->getFormBySubmitId($id))) {
825 $form->submitButtonById($id));
829 * Clicks the submit image by some kind of label. Usually
830 * the alt tag or the nearest equivalent. The owning
831 * form will be submitted by this. Clicking outside of
832 * the boundary of the coordinates will result in
834 * @param string $label ID attribute of button.
835 * @param integer $x X-coordinate of imaginary click.
836 * @param integer $y Y-coordinate of imaginary click.
837 * @return boolean True on successful submit.
840 function clickImage($label, $x = 1, $y = 1) {
841 if (! ($form = &$this->_page
->getFormByImageLabel($label))) {
847 $form->submitImageByLabel($label, $x, $y));
851 * Clicks the submit image by the name. Usually
852 * the alt tag or the nearest equivalent. The owning
853 * form will be submitted by this. Clicking outside of
854 * the boundary of the coordinates will result in
856 * @param string $name Name attribute of button.
857 * @param integer $x X-coordinate of imaginary click.
858 * @param integer $y Y-coordinate of imaginary click.
859 * @return boolean True on successful submit.
862 function clickImageByName($name, $x = 1, $y = 1) {
863 if (! ($form = &$this->_page
->getFormByImageName($name))) {
869 $form->submitImageByName($name, $x, $y));
873 * Clicks the submit image by ID attribute. The owning
874 * form will be submitted by this. Clicking outside of
875 * the boundary of the coordinates will result in
877 * @param integer/string $id ID attribute of button.
878 * @param integer $x X-coordinate of imaginary click.
879 * @param integer $y Y-coordinate of imaginary click.
880 * @return boolean True on successful submit.
883 function clickImageById($id, $x = 1, $y = 1) {
884 if (! ($form = &$this->_page
->getFormByImageId($id))) {
890 $form->submitImageById($id, $x, $y));
894 * Submits a form by the ID.
895 * @param string $id The form ID. No submit button value
897 * @return boolean True on success.
900 function submitFormById($id) {
901 if (! ($form = &$this->_page
->getFormById($id))) {
911 * Follows a link by label. Will click the first link
912 * found with this link text by default, or a later
913 * one if an index is given. The match ignores case and
914 * white space issues.
915 * @param string $label Text between the anchor tags.
916 * @param integer $index Link position counting from zero.
917 * @return boolean True if link present.
920 function clickLink($label, $index = 0) {
921 $urls = $this->_page
->getUrlsByLabel($label);
922 if (count($urls) == 0) {
925 if (count($urls) < $index +
1) {
928 $this->_load('GET', $urls[$index]);
933 * Tests to see if a link is present by label.
934 * @param string $label Text of value attribute.
935 * @return boolean True if link present.
938 function isLink($label) {
939 return (count($this->_page
->getUrlsByLabel($label)) > 0);
943 * Follows a link by id attribute.
944 * @param string $id ID attribute value.
945 * @return boolean True if link present.
948 function clickLinkById($id) {
949 if (! ($url = $this->_page
->getUrlById($id))) {
952 $this->_load('GET', $url);
957 * Tests to see if a link is present by ID attribute.
958 * @param string $id Text of id attribute.
959 * @return boolean True if link present.
962 function isLinkById($id) {
963 return (boolean
)$this->_page
->getUrlById($id);