diff options
Diffstat (limited to 'vendors/simpletest/page.php')
-rw-r--r-- | vendors/simpletest/page.php | 983 |
1 files changed, 983 insertions, 0 deletions
diff --git a/vendors/simpletest/page.php b/vendors/simpletest/page.php new file mode 100644 index 000000000..08e5649dc --- /dev/null +++ b/vendors/simpletest/page.php @@ -0,0 +1,983 @@ +<?php +/** + * Base include file for SimpleTest + * @package SimpleTest + * @subpackage WebTester + * @version $Id: page.php 1672 2008-03-02 04:47:34Z edwardzyang $ + */ + +/**#@+ + * include other SimpleTest class files + */ +require_once(dirname(__FILE__) . '/http.php'); +require_once(dirname(__FILE__) . '/parser.php'); +require_once(dirname(__FILE__) . '/tag.php'); +require_once(dirname(__FILE__) . '/form.php'); +require_once(dirname(__FILE__) . '/selector.php'); +/**#@-*/ + +/** + * Creates tags and widgets given HTML tag + * attributes. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleTagBuilder { + + /** + * Factory for the tag objects. Creates the + * appropriate tag object for the incoming tag name + * and attributes. + * @param string $name HTML tag name. + * @param hash $attributes Element attributes. + * @return SimpleTag Tag object. + * @access public + */ + function createTag($name, $attributes) { + static $map = array( + 'a' => 'SimpleAnchorTag', + 'title' => 'SimpleTitleTag', + 'base' => 'SimpleBaseTag', + 'button' => 'SimpleButtonTag', + 'textarea' => 'SimpleTextAreaTag', + 'option' => 'SimpleOptionTag', + 'label' => 'SimpleLabelTag', + 'form' => 'SimpleFormTag', + 'frame' => 'SimpleFrameTag'); + $attributes = $this->_keysToLowerCase($attributes); + if (array_key_exists($name, $map)) { + $tag_class = $map[$name]; + return new $tag_class($attributes); + } elseif ($name == 'select') { + return $this->_createSelectionTag($attributes); + } elseif ($name == 'input') { + return $this->_createInputTag($attributes); + } + return new SimpleTag($name, $attributes); + } + + /** + * Factory for selection fields. + * @param hash $attributes Element attributes. + * @return SimpleTag Tag object. + * @access protected + */ + function _createSelectionTag($attributes) { + if (isset($attributes['multiple'])) { + return new MultipleSelectionTag($attributes); + } + return new SimpleSelectionTag($attributes); + } + + /** + * Factory for input tags. + * @param hash $attributes Element attributes. + * @return SimpleTag Tag object. + * @access protected + */ + function _createInputTag($attributes) { + if (! isset($attributes['type'])) { + return new SimpleTextTag($attributes); + } + $type = strtolower(trim($attributes['type'])); + $map = array( + 'submit' => 'SimpleSubmitTag', + 'image' => 'SimpleImageSubmitTag', + 'checkbox' => 'SimpleCheckboxTag', + 'radio' => 'SimpleRadioButtonTag', + 'text' => 'SimpleTextTag', + 'hidden' => 'SimpleTextTag', + 'password' => 'SimpleTextTag', + 'file' => 'SimpleUploadTag'); + if (array_key_exists($type, $map)) { + $tag_class = $map[$type]; + return new $tag_class($attributes); + } + return false; + } + + /** + * Make the keys lower case for case insensitive look-ups. + * @param hash $map Hash to convert. + * @return hash Unchanged values, but keys lower case. + * @access private + */ + function _keysToLowerCase($map) { + $lower = array(); + foreach ($map as $key => $value) { + $lower[strtolower($key)] = $value; + } + return $lower; + } +} + +/** + * SAX event handler. Maintains a list of + * open tags and dispatches them as they close. + * @package SimpleTest + * @subpackage WebTester + */ +class SimplePageBuilder extends SimpleSaxListener { + var $_tags; + var $_page; + var $_private_content_tag; + + /** + * Sets the builder up empty. + * @access public + */ + function SimplePageBuilder() { + $this->SimpleSaxListener(); + } + + /** + * Frees up any references so as to allow the PHP garbage + * collection from unset() to work. + * @access public + */ + function free() { + unset($this->_tags); + unset($this->_page); + unset($this->_private_content_tags); + } + + /** + * Reads the raw content and send events + * into the page to be built. + * @param $response SimpleHttpResponse Fetched response. + * @return SimplePage Newly parsed page. + * @access public + */ + function &parse($response) { + $this->_tags = array(); + $this->_page = &$this->_createPage($response); + $parser = &$this->_createParser($this); + $parser->parse($response->getContent()); + $this->_page->acceptPageEnd(); + return $this->_page; + } + + /** + * Creates an empty page. + * @return SimplePage New unparsed page. + * @access protected + */ + function &_createPage($response) { + $page = &new SimplePage($response); + return $page; + } + + /** + * Creates the parser used with the builder. + * @param $listener SimpleSaxListener Target of parser. + * @return SimpleSaxParser Parser to generate + * events for the builder. + * @access protected + */ + function &_createParser(&$listener) { + $parser = &new SimpleHtmlSaxParser($listener); + return $parser; + } + + /** + * Start of element event. Opens a new tag. + * @param string $name Element name. + * @param hash $attributes Attributes without content + * are marked as true. + * @return boolean False on parse error. + * @access public + */ + function startElement($name, $attributes) { + $factory = &new SimpleTagBuilder(); + $tag = $factory->createTag($name, $attributes); + if (! $tag) { + return true; + } + if ($tag->getTagName() == 'label') { + $this->_page->acceptLabelStart($tag); + $this->_openTag($tag); + return true; + } + if ($tag->getTagName() == 'form') { + $this->_page->acceptFormStart($tag); + return true; + } + if ($tag->getTagName() == 'frameset') { + $this->_page->acceptFramesetStart($tag); + return true; + } + if ($tag->getTagName() == 'frame') { + $this->_page->acceptFrame($tag); + return true; + } + if ($tag->isPrivateContent() && ! isset($this->_private_content_tag)) { + $this->_private_content_tag = &$tag; + } + if ($tag->expectEndTag()) { + $this->_openTag($tag); + return true; + } + $this->_page->acceptTag($tag); + return true; + } + + /** + * End of element event. + * @param string $name Element name. + * @return boolean False on parse error. + * @access public + */ + function endElement($name) { + if ($name == 'label') { + $this->_page->acceptLabelEnd(); + return true; + } + if ($name == 'form') { + $this->_page->acceptFormEnd(); + return true; + } + if ($name == 'frameset') { + $this->_page->acceptFramesetEnd(); + return true; + } + if ($this->_hasNamedTagOnOpenTagStack($name)) { + $tag = array_pop($this->_tags[$name]); + if ($tag->isPrivateContent() && $this->_private_content_tag->getTagName() == $name) { + unset($this->_private_content_tag); + } + $this->_addContentTagToOpenTags($tag); + $this->_page->acceptTag($tag); + return true; + } + return true; + } + + /** + * Test to see if there are any open tags awaiting + * closure that match the tag name. + * @param string $name Element name. + * @return boolean True if any are still open. + * @access private + */ + function _hasNamedTagOnOpenTagStack($name) { + return isset($this->_tags[$name]) && (count($this->_tags[$name]) > 0); + } + + /** + * Unparsed, but relevant data. The data is added + * to every open tag. + * @param string $text May include unparsed tags. + * @return boolean False on parse error. + * @access public + */ + function addContent($text) { + if (isset($this->_private_content_tag)) { + $this->_private_content_tag->addContent($text); + } else { + $this->_addContentToAllOpenTags($text); + } + return true; + } + + /** + * Any content fills all currently open tags unless it + * is part of an option tag. + * @param string $text May include unparsed tags. + * @access private + */ + function _addContentToAllOpenTags($text) { + foreach (array_keys($this->_tags) as $name) { + for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) { + $this->_tags[$name][$i]->addContent($text); + } + } + } + + /** + * Parsed data in tag form. The parsed tag is added + * to every open tag. Used for adding options to select + * fields only. + * @param SimpleTag $tag Option tags only. + * @access private + */ + function _addContentTagToOpenTags(&$tag) { + if ($tag->getTagName() != 'option') { + return; + } + foreach (array_keys($this->_tags) as $name) { + for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) { + $this->_tags[$name][$i]->addTag($tag); + } + } + } + + /** + * Opens a tag for receiving content. Multiple tags + * will be receiving input at the same time. + * @param SimpleTag $tag New content tag. + * @access private + */ + function _openTag(&$tag) { + $name = $tag->getTagName(); + if (! in_array($name, array_keys($this->_tags))) { + $this->_tags[$name] = array(); + } + $this->_tags[$name][] = &$tag; + } +} + +/** + * A wrapper for a web page. + * @package SimpleTest + * @subpackage WebTester + */ +class SimplePage { + var $_links; + var $_title; + var $_last_widget; + var $_label; + var $_left_over_labels; + var $_open_forms; + var $_complete_forms; + var $_frameset; + var $_frames; + var $_frameset_nesting_level; + var $_transport_error; + var $_raw; + var $_text; + var $_sent; + var $_headers; + var $_method; + var $_url; + var $_base = false; + var $_request_data; + + /** + * Parses a page ready to access it's contents. + * @param SimpleHttpResponse $response Result of HTTP fetch. + * @access public + */ + function SimplePage($response = false) { + $this->_links = array(); + $this->_title = false; + $this->_left_over_labels = array(); + $this->_open_forms = array(); + $this->_complete_forms = array(); + $this->_frameset = false; + $this->_frames = array(); + $this->_frameset_nesting_level = 0; + $this->_text = false; + if ($response) { + $this->_extractResponse($response); + } else { + $this->_noResponse(); + } + } + + /** + * Extracts all of the response information. + * @param SimpleHttpResponse $response Response being parsed. + * @access private + */ + function _extractResponse($response) { + $this->_transport_error = $response->getError(); + $this->_raw = $response->getContent(); + $this->_sent = $response->getSent(); + $this->_headers = $response->getHeaders(); + $this->_method = $response->getMethod(); + $this->_url = $response->getUrl(); + $this->_request_data = $response->getRequestData(); + } + + /** + * Sets up a missing response. + * @access private + */ + function _noResponse() { + $this->_transport_error = 'No page fetched yet'; + $this->_raw = false; + $this->_sent = false; + $this->_headers = false; + $this->_method = 'GET'; + $this->_url = false; + $this->_request_data = false; + } + + /** + * Original request as bytes sent down the wire. + * @return mixed Sent content. + * @access public + */ + function getRequest() { + return $this->_sent; + } + + /** + * Accessor for raw text of page. + * @return string Raw unparsed content. + * @access public + */ + function getRaw() { + return $this->_raw; + } + + /** + * Accessor for plain text of page as a text browser + * would see it. + * @return string Plain text of page. + * @access public + */ + function getText() { + if (! $this->_text) { + $this->_text = SimpleHtmlSaxParser::normalise($this->_raw); + } + return $this->_text; + } + + /** + * Accessor for raw headers of page. + * @return string Header block as text. + * @access public + */ + function getHeaders() { + if ($this->_headers) { + return $this->_headers->getRaw(); + } + return false; + } + + /** + * Original request method. + * @return string GET, POST or HEAD. + * @access public + */ + function getMethod() { + return $this->_method; + } + + /** + * Original resource name. + * @return SimpleUrl Current url. + * @access public + */ + function getUrl() { + return $this->_url; + } + + /** + * Base URL if set via BASE tag page url otherwise + * @return SimpleUrl Base url. + * @access public + */ + function getBaseUrl() { + return $this->_base; + } + + /** + * Original request data. + * @return mixed Sent content. + * @access public + */ + function getRequestData() { + return $this->_request_data; + } + + /** + * Accessor for last error. + * @return string Error from last response. + * @access public + */ + function getTransportError() { + return $this->_transport_error; + } + + /** + * Accessor for current MIME type. + * @return string MIME type as string; e.g. 'text/html' + * @access public + */ + function getMimeType() { + if ($this->_headers) { + return $this->_headers->getMimeType(); + } + return false; + } + + /** + * Accessor for HTTP response code. + * @return integer HTTP response code received. + * @access public + */ + function getResponseCode() { + if ($this->_headers) { + return $this->_headers->getResponseCode(); + } + return false; + } + + /** + * Accessor for last Authentication type. Only valid + * straight after a challenge (401). + * @return string Description of challenge type. + * @access public + */ + function getAuthentication() { + if ($this->_headers) { + return $this->_headers->getAuthentication(); + } + return false; + } + + /** + * Accessor for last Authentication realm. Only valid + * straight after a challenge (401). + * @return string Name of security realm. + * @access public + */ + function getRealm() { + if ($this->_headers) { + return $this->_headers->getRealm(); + } + return false; + } + + /** + * Accessor for current frame focus. Will be + * false as no frames. + * @return array Always empty. + * @access public + */ + function getFrameFocus() { + return array(); + } + + /** + * Sets the focus by index. The integer index starts from 1. + * @param integer $choice Chosen frame. + * @return boolean Always false. + * @access public + */ + function setFrameFocusByIndex($choice) { + return false; + } + + /** + * Sets the focus by name. Always fails for a leaf page. + * @param string $name Chosen frame. + * @return boolean False as no frames. + * @access public + */ + function setFrameFocus($name) { + return false; + } + + /** + * Clears the frame focus. Does nothing for a leaf page. + * @access public + */ + function clearFrameFocus() { + } + + /** + * Adds a tag to the page. + * @param SimpleTag $tag Tag to accept. + * @access public + */ + function acceptTag(&$tag) { + if ($tag->getTagName() == "a") { + $this->_addLink($tag); + } elseif ($tag->getTagName() == "base") { + $this->_setBase($tag); + } elseif ($tag->getTagName() == "title") { + $this->_setTitle($tag); + } elseif ($this->_isFormElement($tag->getTagName())) { + for ($i = 0; $i < count($this->_open_forms); $i++) { + $this->_open_forms[$i]->addWidget($tag); + } + $this->_last_widget = &$tag; + } + } + + /** + * Opens a label for a described widget. + * @param SimpleFormTag $tag Tag to accept. + * @access public + */ + function acceptLabelStart(&$tag) { + $this->_label = &$tag; + unset($this->_last_widget); + } + + /** + * Closes the most recently opened label. + * @access public + */ + function acceptLabelEnd() { + if (isset($this->_label)) { + if (isset($this->_last_widget)) { + $this->_last_widget->setLabel($this->_label->getText()); + unset($this->_last_widget); + } else { + $this->_left_over_labels[] = SimpleTestCompatibility::copy($this->_label); + } + unset($this->_label); + } + } + + /** + * Tests to see if a tag is a possible form + * element. + * @param string $name HTML element name. + * @return boolean True if form element. + * @access private + */ + function _isFormElement($name) { + return in_array($name, array('input', 'button', 'textarea', 'select')); + } + + /** + * Opens a form. New widgets go here. + * @param SimpleFormTag $tag Tag to accept. + * @access public + */ + function acceptFormStart(&$tag) { + $this->_open_forms[] = &new SimpleForm($tag, $this); + } + + /** + * Closes the most recently opened form. + * @access public + */ + function acceptFormEnd() { + if (count($this->_open_forms)) { + $this->_complete_forms[] = array_pop($this->_open_forms); + } + } + + /** + * Opens a frameset. A frameset may contain nested + * frameset tags. + * @param SimpleFramesetTag $tag Tag to accept. + * @access public + */ + function acceptFramesetStart(&$tag) { + if (! $this->_isLoadingFrames()) { + $this->_frameset = &$tag; + } + $this->_frameset_nesting_level++; + } + + /** + * Closes the most recently opened frameset. + * @access public + */ + function acceptFramesetEnd() { + if ($this->_isLoadingFrames()) { + $this->_frameset_nesting_level--; + } + } + + /** + * Takes a single frame tag and stashes it in + * the current frame set. + * @param SimpleFrameTag $tag Tag to accept. + * @access public + */ + function acceptFrame(&$tag) { + if ($this->_isLoadingFrames()) { + if ($tag->getAttribute('src')) { + $this->_frames[] = &$tag; + } + } + } + + /** + * Test to see if in the middle of reading + * a frameset. + * @return boolean True if inframeset. + * @access private + */ + function _isLoadingFrames() { + if (! $this->_frameset) { + return false; + } + return ($this->_frameset_nesting_level > 0); + } + + /** + * Test to see if link is an absolute one. + * @param string $url Url to test. + * @return boolean True if absolute. + * @access protected + */ + function _linkIsAbsolute($url) { + $parsed = new SimpleUrl($url); + return (boolean)($parsed->getScheme() && $parsed->getHost()); + } + + /** + * Adds a link to the page. + * @param SimpleAnchorTag $tag Link to accept. + * @access protected + */ + function _addLink($tag) { + $this->_links[] = $tag; + } + + /** + * Marker for end of complete page. Any work in + * progress can now be closed. + * @access public + */ + function acceptPageEnd() { + while (count($this->_open_forms)) { + $this->_complete_forms[] = array_pop($this->_open_forms); + } + foreach ($this->_left_over_labels as $label) { + for ($i = 0, $count = count($this->_complete_forms); $i < $count; $i++) { + $this->_complete_forms[$i]->attachLabelBySelector( + new SimpleById($label->getFor()), + $label->getText()); + } + } + } + + /** + * Test for the presence of a frameset. + * @return boolean True if frameset. + * @access public + */ + function hasFrames() { + return (boolean)$this->_frameset; + } + + /** + * Accessor for frame name and source URL for every frame that + * will need to be loaded. Immediate children only. + * @return boolean/array False if no frameset or + * otherwise a hash of frame URLs. + * The key is either a numerical + * base one index or the name attribute. + * @access public + */ + function getFrameset() { + if (! $this->_frameset) { + return false; + } + $urls = array(); + for ($i = 0; $i < count($this->_frames); $i++) { + $name = $this->_frames[$i]->getAttribute('name'); + $url = new SimpleUrl($this->_frames[$i]->getAttribute('src')); + $urls[$name ? $name : $i + 1] = $this->expandUrl($url); + } + return $urls; + } + + /** + * Fetches a list of loaded frames. + * @return array/string Just the URL for a single page. + * @access public + */ + function getFrames() { + $url = $this->expandUrl($this->getUrl()); + return $url->asString(); + } + + /** + * Accessor for a list of all links. + * @return array List of urls with scheme of + * http or https and hostname. + * @access public + */ + function getUrls() { + $all = array(); + foreach ($this->_links as $link) { + $url = $this->_getUrlFromLink($link); + $all[] = $url->asString(); + } + return $all; + } + + /** + * Accessor for URLs by the link label. Label will match + * regardess of whitespace issues and case. + * @param string $label Text of link. + * @return array List of links with that label. + * @access public + */ + function getUrlsByLabel($label) { + $matches = array(); + foreach ($this->_links as $link) { + if ($link->getText() == $label) { + $matches[] = $this->_getUrlFromLink($link); + } + } + return $matches; + } + + /** + * Accessor for a URL by the id attribute. + * @param string $id Id attribute of link. + * @return SimpleUrl URL with that id of false if none. + * @access public + */ + function getUrlById($id) { + foreach ($this->_links as $link) { + if ($link->getAttribute('id') === (string)$id) { + return $this->_getUrlFromLink($link); + } + } + return false; + } + + /** + * Converts a link tag into a target URL. + * @param SimpleAnchor $link Parsed link. + * @return SimpleUrl URL with frame target if any. + * @access private + */ + function _getUrlFromLink($link) { + $url = $this->expandUrl($link->getHref()); + if ($link->getAttribute('target')) { + $url->setTarget($link->getAttribute('target')); + } + return $url; + } + + /** + * Expands expandomatic URLs into fully qualified + * URLs. + * @param SimpleUrl $url Relative URL. + * @return SimpleUrl Absolute URL. + * @access public + */ + function expandUrl($url) { + if (! is_object($url)) { + $url = new SimpleUrl($url); + } + $location = $this->getBaseUrl() ? $this->getBaseUrl() : new SimpleUrl(); + return $url->makeAbsolute($location->makeAbsolute($this->getUrl())); + } + + /** + * Sets the base url for the page. + * @param SimpleTag $tag Base URL for page. + * @access protected + */ + function _setBase(&$tag) { + $url = $tag->getAttribute('href'); + $this->_base = new SimpleUrl($url); + } + + /** + * Sets the title tag contents. + * @param SimpleTitleTag $tag Title of page. + * @access protected + */ + function _setTitle(&$tag) { + $this->_title = &$tag; + } + + /** + * Accessor for parsed title. + * @return string Title or false if no title is present. + * @access public + */ + function getTitle() { + if ($this->_title) { + return $this->_title->getText(); + } + return false; + } + + /** + * Finds a held form by button label. Will only + * search correctly built forms. + * @param SimpleSelector $selector Button finder. + * @return SimpleForm Form object containing + * the button. + * @access public + */ + function &getFormBySubmit($selector) { + for ($i = 0; $i < count($this->_complete_forms); $i++) { + if ($this->_complete_forms[$i]->hasSubmit($selector)) { + return $this->_complete_forms[$i]; + } + } + $null = null; + return $null; + } + + /** + * Finds a held form by image using a selector. + * Will only search correctly built forms. + * @param SimpleSelector $selector Image finder. + * @return SimpleForm Form object containing + * the image. + * @access public + */ + function &getFormByImage($selector) { + for ($i = 0; $i < count($this->_complete_forms); $i++) { + if ($this->_complete_forms[$i]->hasImage($selector)) { + return $this->_complete_forms[$i]; + } + } + $null = null; + return $null; + } + + /** + * Finds a held form by the form ID. A way of + * identifying a specific form when we have control + * of the HTML code. + * @param string $id Form label. + * @return SimpleForm Form object containing the matching ID. + * @access public + */ + function &getFormById($id) { + for ($i = 0; $i < count($this->_complete_forms); $i++) { + if ($this->_complete_forms[$i]->getId() == $id) { + return $this->_complete_forms[$i]; + } + } + $null = null; + return $null; + } + + /** + * Sets a field on each form in which the field is + * available. + * @param SimpleSelector $selector Field finder. + * @param string $value Value to set field to. + * @return boolean True if value is valid. + * @access public + */ + function setField($selector, $value, $position=false) { + $is_set = false; + for ($i = 0; $i < count($this->_complete_forms); $i++) { + if ($this->_complete_forms[$i]->setField($selector, $value, $position)) { + $is_set = true; + } + } + return $is_set; + } + + /** + * Accessor for a form element value within a page. + * @param SimpleSelector $selector Field finder. + * @return string/boolean A string if the field is + * present, false if unchecked + * and null if missing. + * @access public + */ + function getField($selector) { + for ($i = 0; $i < count($this->_complete_forms); $i++) { + $value = $this->_complete_forms[$i]->getValue($selector); + if (isset($value)) { + return $value; + } + } + return null; + } +} +?>
\ No newline at end of file |