<?php
/**
 * PHPUnit
 *
 * Copyright (c) 2002-2008, Sebastian Bergmann <sb@sebastian-bergmann.de>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *   * Neither the name of Sebastian Bergmann nor the names of his
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @category   Testing
 * @package    PHPUnit
 * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @copyright  2002-2008 Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @version    SVN: $Id: SeleniumTestCase.php 2111 2008-01-15 09:55:15Z sb $
 * @link       http://www.phpunit.de/
 * @since      File available since Release 3.0.0
 */

require_once 'PHPUnit/Framework.php';
require_once 'PHPUnit/Util/Log/Database.php';
require_once 'PHPUnit/Util/Filter.php';
require_once 'PHPUnit/Util/Test.php';
require_once 'PHPUnit/Util/XML.php';

PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');

/**
 * TestCase class that uses Selenium to provide
 * the functionality required for web testing.
 *
 * @category   Testing
 * @package    PHPUnit
 * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @copyright  2002-2008 Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @version    Release: 3.2.9
 * @link       http://www.phpunit.de/
 * @since      Class available since Release 3.0.0
 */
abstract class PHPUnit_Extensions_SeleniumTestCase extends PHPUnit_Framework_TestCase
{
    /**
     * @var    array
     * @access public
     * @static
     */
    public static $browsers = array();

    /**
     * @var    string
     * @access protected
     */
    protected $browser;

    /**
     * @var    string
     * @access protected
     */
    protected $browserName;

    /**
     * @var    string
     * @access protected
     */
    protected $browserUrl;

    /**
     * @var    string
     * @access protected
     */
    protected $host = 'localhost';

    /**
     * @var    integer
     * @access protected
     */
    protected $port = 4444;

    /**
     * @var    integer
     * @access protected
     */
    protected $timeout = 30000;

    /**
     * @var    array
     * @access protected
     */
    protected static $sessionId = array();

    /**
     * @var    integer
     * @access protected
     */
    protected $sleep = 0;

    /**
     * @var    boolean
     * @access protected
     */
    protected $autoStop = TRUE;

    /**
     * @var    boolean
     * @access protected
     */
    protected $collectCodeCoverageInformation = FALSE;

    /**
     * @var    string
     * @access protected
     */
    protected $coverageScriptUrl = '';

    /**
     * @var    string
     * @access protected
     */
    protected $testId;

    /**
     * @var    boolean
     * @access protected
     */
    protected $inDefaultAssertions = FALSE;

    /**
     * @param  string $name
     * @param  array  $browser
     * @throws InvalidArgumentException
     * @access public
     */
    public function __construct($name = NULL, array $data = array(), array $browser = array())
    {
        parent::__construct($name, $data);

        if (isset($browser['name'])) {
            if (!is_string($browser['name'])) {
                throw new InvalidArgumentException;
            }
        } else {
            $browser['name'] = '';
        }

        if (isset($browser['browser'])) {
            if (!is_string($browser['browser'])) {
                throw new InvalidArgumentException;
            }
        } else {
            $browser['browser'] = '';
        }

        if (isset($browser['host'])) {
            if (!is_string($browser['host'])) {
                throw new InvalidArgumentException;
            }
        } else {
            $browser['host'] = 'localhost';
        }

        if (isset($browser['port'])) {
            if (!is_int($browser['port'])) {
                throw new InvalidArgumentException;
            }
        } else {
            $browser['port'] = 4444;
        }

        if (isset($browser['timeout'])) {
            if (!is_int($browser['timeout'])) {
                throw new InvalidArgumentException;
            }
        } else {
            $browser['timeout'] = 30000;
        }

        $this->browserName = $browser['name'];
        $this->browser     = $browser['browser'];
        $this->host        = $browser['host'];
        $this->port        = $browser['port'];
        $this->timeout     = $browser['timeout'];
    }

    /**
     * @param  string $className
     * @return PHPUnit_Framework_TestSuite
     * @access public
     */
    public static function suite($className)
    {
        $suite = new PHPUnit_Framework_TestSuite;
        $suite->setName($className);

        $class            = new ReflectionClass($className);
        $classGroups      = PHPUnit_Util_Test::getGroups($class);
        $staticProperties = $class->getStaticProperties();

        // Create tests from Selenese/HTML files.
        if (isset($staticProperties['seleneseDirectory']) &&
            is_dir($staticProperties['seleneseDirectory'])) {
            $files = new PHPUnit_Util_FilterIterator(
              new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator(
                  $staticProperties['seleneseDirectory']
                )
              ),
              '.htm'
            );

            // Create tests from Selenese/HTML files for multiple browsers.
            if (!empty($staticProperties['browsers'])) {
                foreach ($staticProperties['browsers'] as $browser) {
                    $browserSuite = new PHPUnit_Framework_TestSuite;
                    $browserSuite->setName($className . ': ' . $browser['name']);

                    foreach ($files as $file) {
                        $browserSuite->addTest(
                          new $className((string)$file, array(), $browser)
                        );
                    }

                    $suite->addTest($browserSuite, $classGroups);
                }
            }

            // Create tests from Selenese/HTML files for single browser.
            else {
                foreach ($files as $file) {
                    $suite->addTest(new $className((string)$file), $classGroups);
                }
            }
        }

        // Create tests from test methods for multiple browsers.
        if (!empty($staticProperties['browsers'])) {
            foreach ($staticProperties['browsers'] as $browser) {
                $browserSuite = new PHPUnit_Framework_TestSuite;
                $browserSuite->setName($className . ': ' . $browser['name']);

                foreach ($class->getMethods() as $method) {
                    if (PHPUnit_Framework_TestSuite::isPublicTestMethod($method)) {
                        $name   = $method->getName();
                        $data   = PHPUnit_Util_Test::getProvidedData($className, $name);
                        $groups = PHPUnit_Util_Test::getGroups($method, $classGroups);

                        // Test method with @dataProvider.
                        if (is_array($data) || $data instanceof Iterator) {
                            $dataSuite = new PHPUnit_Framework_TestSuite(
                              $className . '::' . $name
                            );

                            foreach ($data as $_data) {
                                $dataSuite->addTest(
                                  new $className($name, $_data, $browser),
                                  $groups
                                );
                            }

                            $browserSuite->addTest($dataSuite);
                        }

                        // Test method without @dataProvider.
                        else {
                            $browserSuite->addTest(
                              new $className($name, array(), $browser), $groups
                            );
                        }
                    }
                }

                $suite->addTest($browserSuite);
            }
        }

        // Create tests from test methods for single browser.
        else {
            foreach ($class->getMethods() as $method) {
                if (PHPUnit_Framework_TestSuite::isPublicTestMethod($method)) {
                    $name   = $method->getName();
                    $data   = PHPUnit_Util_Test::getProvidedData($className, $name);
                    $groups = PHPUnit_Util_Test::getGroups($method, $classGroups);

                    // Test method with @dataProvider.
                    if (is_array($data) || $data instanceof Iterator) {
                        $dataSuite = new PHPUnit_Framework_TestSuite(
                          $className . '::' . $name
                        );

                        foreach ($data as $_data) {
                            $dataSuite->addTest(
                              new $className($name, $_data),
                              $groups
                            );
                        }

                        $suite->addTest($dataSuite);
                    }

                    // Test method without @dataProvider.
                    else {
                        $suite->addTest(
                          new $className($name), $groups
                        );
                    }
                }
            }
        }

        return $suite;
    }

    /**
     * Runs the test case and collects the results in a TestResult object.
     * If no TestResult object is passed a new one will be created.
     *
     * @param  PHPUnit_Framework_TestResult $result
     * @return PHPUnit_Framework_TestResult
     * @throws InvalidArgumentException
     * @access public
     */
    public function run(PHPUnit_Framework_TestResult $result = NULL)
    {
        if ($result === NULL) {
            $result = $this->createResult();
        }

        $this->collectCodeCoverageInformation = $result->getCollectCodeCoverageInformation();

        $result->run($this);

        if ($this->collectCodeCoverageInformation) {
            $result->appendCodeCoverageInformation(
              $this, $this->getCodeCoverage()
            );
        }

        return $result;
    }

    /**
     * @access protected
     */
    protected function runTest()
    {
        $this->start();

        if (!is_file($this->name)) {
            parent::runTest();
        } else {
            $this->runSelenese($this->name);
        }

        if ($this->autoStop) {
            try {
                $this->stop();
            }

            catch (RuntimeException $e) {
            }
        }
    }

    /**
     * If you want to override tearDown() make sure to either call stop() or
     * parent::tearDown(). Otherwise the Selenium RC session will not be
     * closed upon test failure.
     *
     * @access protected
     */
    protected function tearDown()
    {
        if ($this->autoStop) {
            try {
                $this->stop();
            }

            catch (RuntimeException $e) {
            }
        }
    }

    /**
     * Returns a string representation of the test case.
     *
     * @return string
     * @access public
     */
    public function toString()
    {
        $buffer = parent::toString();

        if (!empty($this->browserName)) {
            $buffer .= ' with browser ' . $this->browserName;
        }

        return $buffer;
    }

    /**
     * @return string
     * @access public
     */
    public function start()
    {
        if (!isset(self::$sessionId[$this->host][$this->port][$this->browser])) {
            self::$sessionId[$this->host][$this->port][$this->browser] = $this->getString(
              'getNewBrowserSession',
              array($this->browser, $this->browserUrl)
            );

            $this->doCommand('setTimeout', array($this->timeout));
        }


        $this->testId = md5(uniqid(rand(), TRUE));

        return self::$sessionId[$this->host][$this->port][$this->browser];
    }

    /**
     * @access public
     */
    public function stop()
    {
        if (!isset(self::$sessionId[$this->host][$this->port][$this->browser])) {
            return;
        }

        $this->doCommand('testComplete');

        unset(self::$sessionId[$this->host][$this->port][$this->browser]);
    }

    /**
     * @param  boolean $autoStop
     * @throws InvalidArgumentException
     * @access public
     */
    public function setAutoStop($autoStop)
    {
        if (!is_bool($autoStop)) {
            throw new InvalidArgumentException;
        }

        $this->autoStop = $autoStop;
    }

    /**
     * @param  string $browser
     * @throws InvalidArgumentException
     * @access public
     */
    public function setBrowser($browser)
    {
        if (!is_string($browser)) {
            throw new InvalidArgumentException;
        }

        $this->browser = $browser;
    }

    /**
     * @param  string $browserUrl
     * @throws InvalidArgumentException
     * @access public
     */
    public function setBrowserUrl($browserUrl)
    {
        if (!is_string($browserUrl)) {
            throw new InvalidArgumentException;
        }

        $this->browserUrl = $browserUrl;
    }

    /**
     * @param  string $host
     * @throws InvalidArgumentException
     * @access public
     */
    public function setHost($host)
    {
        if (!is_string($host)) {
            throw new InvalidArgumentException;
        }

        $this->host = $host;
    }

    /**
     * @param  integer $port
     * @throws InvalidArgumentException
     * @access public
     */
    public function setPort($port)
    {
        if (!is_int($port)) {
            throw new InvalidArgumentException;
        }

        $this->port = $port;
    }

    /**
     * @param  integer $timeout
     * @throws InvalidArgumentException
     * @access public
     */
    public function setTimeout($timeout)
    {
        if (!is_int($timeout)) {
            throw new InvalidArgumentException;
        }

        $this->timeout = $timeout;
    }

    /**
     * @param  integer $seconds
     * @throws InvalidArgumentException
     * @access public
     */
    public function setSleep($seconds)
    {
        if (!is_int($seconds)) {
            throw new InvalidArgumentException;
        }

        $this->sleep = $seconds;
    }

    /**
     * Runs a test from a Selenese (HTML) specification.
     *
     * @param string $filename
     * @access public
     */
    public function runSelenese($filename)
    {
        $document = PHPUnit_Util_XML::load($filename, TRUE);
        $xpath    = new DOMXPath($document);
        $rows     = $xpath->query('body/table/tbody/tr');

        foreach ($rows as $row)
        {
            $action    = NULL;
            $arguments = array();
            $columns   = $xpath->query('td', $row);

            foreach ($columns as $column)
            {
                if ($action === NULL) {
                    $action = $column->nodeValue;
                } else {
                    $arguments[] = $column->nodeValue;
                }
            }

            $this->__call($action, $arguments);
        }
    }

    /**
     * This method implements the Selenium RC protocol.
     *
     * @param  string $command
     * @param  array  $arguments
     * @return mixed
     * @access public
     * @method unknown  addLocationStrategy()
     * @method unknown  addSelection()
     * @method unknown  allowNativeXpath()
     * @method unknown  altKeyDown()
     * @method unknown  altKeyUp()
     * @method unknown  answerOnNextPrompt()
     * @method unknown  assignId()
     * @method unknown  captureScreenshot()
     * @method unknown  check()
     * @method unknown  chooseCancelOnNextConfirmation()
     * @method unknown  click()
     * @method unknown  clickAndWait()
     * @method unknown  clickAt()
     * @method unknown  close()
     * @method unknown  controlKeyDown()
     * @method unknown  controlKeyUp()
     * @method unknown  createCookie()
     * @method unknown  deleteCookie()
     * @method unknown  doubleClick()
     * @method unknown  doubleClickAt()
     * @method unknown  dragAndDrop()
     * @method unknown  dragAndDropToObject()
     * @method unknown  dragDrop()
     * @method unknown  fireEvent()
     * @method string   getAlert()
     * @method array    getAllButtons()
     * @method array    getAllFields()
     * @method array    getAllLinks()
     * @method array    getAllWindowIds()
     * @method array    getAllWindowNames()
     * @method array    getAllWindowTitles()
     * @method string   getAttribute()
     * @method array    getAttributeFromAllWindows()
     * @method string   getBodyText()
     * @method string   getConfirmation()
     * @method string   getCookie()
     * @method integer  getCursorPosition()
     * @method integer  getElementHeight()
     * @method integer  getElementIndex()
     * @method integer  getElementPositionLeft()
     * @method integer  getElementPositionTop()
     * @method integer  getElementWidth()
     * @method string   getEval()
     * @method string   getExpression()
     * @method string   getHtmlSource()
     * @method string   getLocation()
     * @method string   getLogMessages()
     * @method integer  getMouseSpeed()
     * @method string   getPrompt()
     * @method array    getSelectOptions()
     * @method string   getSelectedId()
     * @method array    getSelectedIds()
     * @method string   getSelectedIndex()
     * @method array    getSelectedIndexes()
     * @method string   getSelectedLabel()
     * @method array    getSelectedLabels()
     * @method string   getSelectedValue()
     * @method array    getSelectedValues()
     * @method unknown  getSpeed()
     * @method string   getTable()
     * @method string   getText()
     * @method string   getTitle()
     * @method string   getValue()
     * @method boolean  getWhetherThisFrameMatchFrameExpression()
     * @method boolean  getWhetherThisWindowMatchWindowExpression()
     * @method integer  getXpathCount()
     * @method unknown  goBack()
     * @method unknown  highlight()
     * @method boolean  isAlertPresent()
     * @method boolean  isChecked()
     * @method boolean  isConfirmationPresent()
     * @method boolean  isEditable()
     * @method boolean  isElementPresent()
     * @method boolean  isOrdered()
     * @method boolean  isPromptPresent()
     * @method boolean  isSomethingSelected()
     * @method boolean  isTextPresent()
     * @method boolean  isVisible()
     * @method unknown  keyDown()
     * @method unknown  keyPress()
     * @method unknown  keyUp()
     * @method unknown  metaKeyDown()
     * @method unknown  metaKeyUp()
     * @method unknown  mouseDown()
     * @method unknown  mouseDownAt()
     * @method unknown  mouseMove()
     * @method unknown  mouseMoveAt()
     * @method unknown  mouseOut()
     * @method unknown  mouseOver()
     * @method unknown  mouseUp()
     * @method unknown  mouseUpAt()
     * @method unknown  open()
     * @method unknown  openWindow()
     * @method unknown  refresh()
     * @method unknown  removeAllSelections()
     * @method unknown  removeSelection()
     * @method unknown  select()
     * @method unknown  selectFrame()
     * @method unknown  selectWindow()
     * @method unknown  setContext()
     * @method unknown  setCursorPosition()
     * @method unknown  setMouseSpeed()
     * @method unknown  setSpeed()
     * @method unknown  shiftKeyDown()
     * @method unknown  shiftKeyUp()
     * @method unknown  submit()
     * @method unknown  type()
     * @method unknown  typeKeys()
     * @method unknown  uncheck()
     * @method unknown  waitForCondition()
     * @method unknown  waitForPageToLoad()
     * @method unknown  waitForPopUp()
     * @method unknown  windowFocus()
     * @method unknown  windowMaximize()
     */
    public function __call($command, $arguments)
    {
        switch ($command) {
            case 'addLocationStrategy':
            case 'addSelection':
            case 'allowNativeXpath':
            case 'altKeyDown':
            case 'altKeyUp':
            case 'answerOnNextPrompt':
            case 'assignId':
            case 'captureScreenshot':
            case 'check':
            case 'chooseCancelOnNextConfirmation':
            case 'click':
            case 'clickAt':
            case 'close':
            case 'controlKeyDown':
            case 'controlKeyUp':
            case 'createCookie':
            case 'deleteCookie':
            case 'doubleClick':
            case 'doubleClickAt':
            case 'dragAndDrop':
            case 'dragAndDropToObject':
            case 'dragDrop':
            case 'fireEvent':
            case 'goBack':
            case 'highlight':
            case 'keyDown':
            case 'keyPress':
            case 'keyUp':
            case 'metaKeyDown':
            case 'metaKeyUp':
            case 'mouseDown':
            case 'mouseDownAt':
            case 'mouseMove':
            case 'mouseMoveAt':
            case 'mouseOut':
            case 'mouseOver':
            case 'mouseUp':
            case 'mouseUpAt':
            case 'open':
            case 'openWindow':
            case 'refresh':
            case 'removeAllSelections':
            case 'removeSelection':
            case 'select':
            case 'selectFrame':
            case 'selectWindow':
            case 'setContext':
            case 'setCursorPosition':
            case 'setMouseSpeed':
            case 'setSpeed':
            case 'shiftKeyDown':
            case 'shiftKeyUp':
            case 'submit':
            case 'type':
            case 'typeKeys':
            case 'uncheck':
            case 'windowFocus':
            case 'windowMaximize': {
                // Pre-Command Actions
                switch ($command) {
                    case 'open':
                    case 'openWindow': {
                        if ($this->collectCodeCoverageInformation) {
                            $this->deleteCookie('PHPUNIT_SELENIUM_TEST_ID', '/');

                            $this->createCookie(
                              'PHPUNIT_SELENIUM_TEST_ID=' . $this->testId,
                              'path=/'
                            );
                        }
                    }
                    break;
                }

                $this->doCommand($command, $arguments);

                // Post-Command Actions
                switch ($command) {
                    case 'addLocationStrategy':
                    case 'allowNativeXpath':
                    case 'assignId':
                    case 'captureScreenshot': {
                        // intentionally empty
                    }
                    break;

                    default: {
                        if ($this->sleep > 0) {
                            sleep($this->sleep);
                        }

                        $this->runDefaultAssertions($command);
                    }
                }
            }
            break;

            case 'getWhetherThisFrameMatchFrameExpression':
            case 'getWhetherThisWindowMatchWindowExpression':
            case 'isAlertPresent':
            case 'isChecked':
            case 'isConfirmationPresent':
            case 'isEditable':
            case 'isElementPresent':
            case 'isOrdered':
            case 'isPromptPresent':
            case 'isSomethingSelected':
            case 'isTextPresent':
            case 'isVisible': {
                return $this->getBoolean($command, $arguments);
            }
            break;

            case 'getCursorPosition':
            case 'getElementHeight':
            case 'getElementIndex':
            case 'getElementPositionLeft':
            case 'getElementPositionTop':
            case 'getElementWidth':
            case 'getMouseSpeed':
            case 'getSpeed':
            case 'getXpathCount': {
                return $this->getNumber($command, $arguments);
            }
            break;

            case 'getAlert':
            case 'getAttribute':
            case 'getBodyText':
            case 'getConfirmation':
            case 'getCookie':
            case 'getEval':
            case 'getExpression':
            case 'getHtmlSource':
            case 'getLocation':
            case 'getLogMessages':
            case 'getPrompt':
            case 'getSelectedId':
            case 'getSelectedIndex':
            case 'getSelectedLabel':
            case 'getSelectedValue':
            case 'getTable':
            case 'getText':
            case 'getTitle':
            case 'getValue': {
                return $this->getString($command, $arguments);
            }
            break;

            case 'getAllButtons':
            case 'getAllFields':
            case 'getAllLinks':
            case 'getAllWindowIds':
            case 'getAllWindowNames':
            case 'getAllWindowTitles':
            case 'getAttributeFromAllWindows':
            case 'getSelectedIds':
            case 'getSelectedIndexes':
            case 'getSelectedLabels':
            case 'getSelectedValues':
            case 'getSelectOptions': {
                return $this->getStringArray($command, $arguments);
            }
            break;

            case 'clickAndWait': {
                $this->doCommand('click', $arguments);
                $this->doCommand('waitForPageToLoad', array($this->timeout));

                if ($this->sleep > 0) {
                    sleep($this->sleep);
                }

                $this->runDefaultAssertions($command);
            }
            break;

            case 'waitForCondition':
            case 'waitForPopUp': {
                if (count($arguments) == 1) {
                    $arguments[] = $this->timeout;
                }

                $this->doCommand($command, $arguments);
                $this->runDefaultAssertions($command);
            }
            break;

            case 'waitForPageToLoad': {
                if (empty($arguments)) {
                    $arguments[] = $this->timeout;
                }

                $this->doCommand($command, $arguments);
                $this->runDefaultAssertions($command);
            }
            break;

            default: {
                $this->stop();

                throw new BadMethodCallException(
                  "Method $command not defined."
                );
            }
        }
    }

    /**
     * Asserts that an alert is present.
     *
     * @param  string $message
     * @access public
     */
    public function assertAlertPresent($message = 'No alert present.')
    {
        $this->assertTrue($this->isAlertPresent(), $message);
    }

    /**
     * Asserts that no alert is present.
     *
     * @param  string $message
     * @access public
     */
    public function assertNoAlertPresent($message = 'Alert present.')
    {
        $this->assertFalse($this->isAlertPresent(), $message);
    }

    /**
     * Asserts that an option is checked.
     *
     * @param  string $locator
     * @param  string $message
     * @access public
     */
    public function assertChecked($locator, $message = '')
    {    	
        if ($message == '') {
            $message = sprintf(
              '"%s" not checked.',
              $locator
            );
        }

        $this->assertTrue($this->isChecked($locator), $message);
    }

    /**
     * Asserts that an option is not checked.
     *
     * @param  string $locator
     * @param  string $message
     * @access public
     */
    public function assertNotChecked($locator, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              '"%s" checked.',
              $locator
            );
        }

        $this->assertFalse($this->isChecked($locator), $message);
    }

    /**
     * Assert that a confirmation is present.
     *
     * @param  string $message
     * @access public
     */
    public function assertConfirmationPresent($message = 'No confirmation present.')
    {
        $this->assertTrue($this->isConfirmationPresent(), $message);
    }

    /**
     * Assert that no confirmation is present.
     *
     * @param  string $message
     * @access public
     */
    public function assertNoConfirmationPresent($message = 'Confirmation present.')
    {
        $this->assertFalse($this->isConfirmationPresent(), $message);
    }

    /**
     * Asserts that an input field is editable.
     *
     * @param  string $locator
     * @param  string $message
     * @access public
     */
    public function assertEditable($locator, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              '"%s" not editable.',
              $locator
            );
        }

        $this->assertTrue($this->isEditable($locator), $message);
    }

    /**
     * Asserts that an input field is not editable.
     *
     * @param  string $locator
     * @param  string $message
     * @access public
     */
    public function assertNotEditable($locator, $message = '')    
    {
        if ($message == '') {
            $message = sprintf(
              '"%s" editable.',
              $locator
            );
        }

        $this->assertFalse($this->isEditable($locator), $message);
    }

    /**
     * Asserts that an element's value is equal to a given string.
     *
     * @param  string $locator
     * @param  string $text
     * @param  string $message
     * @access public
     */
    public function assertElementValueEquals($locator, $text, $message = '')
    {
        $this->assertEquals($text, $this->getValue($locator), $message);
    }

    /**
     * Asserts that an element's value is not equal to a given string.
     *
     * @param  string $locator
     * @param  string $text
     * @param  string $message
     * @access public
     */
    public function assertElementValueNotEquals($locator, $text, $message = '')
    {
        $this->assertNotEquals($text, $this->getValue($locator), $message);
    }

    /**
     * Asserts that an element contains a given string.
     *
     * @param  string $locator
     * @param  string $text
     * @param  string $message
     * @access public
     */
    public function assertElementContainsText($locator, $text, $message = '')
    {
        $this->assertContains($text, $this->getValue($locator), $message);
    }

    /**
     * Asserts that an element does not contain a given string.
     *
     * @param  string $locator
     * @param  string $text
     * @param  string $message
     * @access public
     */
    public function assertElementNotContainsText($locator, $text, $message = '')
    {
        $this->assertNotContains($text, $this->getValue($locator), $message);
    }

    /**
     * Asserts than an element is present.
     *
     * @param  string $locator
     * @param  string $message
     * @access public
     */
    public function assertElementPresent($locator, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              'Element "%s" not present.',
              $locator
            );
        }

        $this->assertTrue($this->isElementPresent($locator), $message);
    }

    /**
     * Asserts than an element is not present.
     *
     * @param  string $locator
     * @param  string $message
     * @access public
     */
    public function assertElementNotPresent($locator, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              'Element "%s" present.',
              $locator
            );
        }

        $this->assertFalse($this->isElementPresent($locator), $message);
    }

    /**
     * Asserts that the location is equal to a specified one.
     *
     * @param  string $location
     * @param  string $message
     * @access public
     */
    public function assertLocationEquals($location, $message = '')
    {
        $this->assertEquals($location, $this->getLocation(), $message);
    }

    /**
     * Asserts that the location is not equal to a specified one.
     *
     * @param  string $location
     * @param  string $message
     * @access public
     */
    public function assertLocationNotEquals($location, $message = '')
    {
        $this->assertNotEquals($location, $this->getLocation(), $message);
    }

    /**
     * Asserts than a prompt is present.
     *
     * @param  string $message
     * @access public
     */
    public function assertPromptPresent($message = 'No prompt present.')
    {
        $this->assertTrue($this->isPromptPresent(), $message);
    }

    /**
     * Asserts than no prompt is present.
     *
     * @param  string $message
     * @access public
     */
    public function assertNoPromptPresent($message = 'Prompt present.')
    {
        $this->assertFalse($this->isPromptPresent(), $message);
    }

    /**
     * Asserts that a select element has a specific option.
     *
     * @param  string $selectLocator
     * @param  string $option
     * @param  string $message
     * @access public
     * @since  Method available since Release 3.2.0
     */
    public function assertSelectHasOption($selectLocator, $option, $message = '')
    {
        $this->assertContains($option, $this->getSelectOptions($selectLocator), $message);
    }

    /**
     * Asserts that a select element does not have a specific option.
     *
     * @param  string $selectLocator
     * @param  string $option
     * @param  string $message
     * @access public
     * @since  Method available since Release 3.2.0
     */
    public function assertSelectNotHasOption($selectLocator, $option, $message = '')
    {
        $this->assertNotContains($option, $this->getSelectOptions($selectLocator), $message);
    }

    /**
     * Asserts that a specific label is selected.
     *
     * @param  string $selectLocator
     * @param  string $value
     * @param  string $message
     * @access public
     * @since  Method available since Release 3.2.0
     */
    public function assertSelected($selectLocator, $option, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              'Label "%s" not selected in "%s".',
              $option,
              $selectLocator
            );
        }

        $this->assertEquals(
          $option,
          $this->getSelectedLabel($selectLocator),
          $message
        );
    }

    /**
     * Asserts that a specific label is not selected.
     *
     * @param  string $selectLocator
     * @param  string $value
     * @param  string $message
     * @access public
     * @since  Method available since Release 3.2.0
     */
    public function assertNotSelected($selectLocator, $option, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              'Label "%s" selected in "%s".',
              $option,
              $selectLocator
            );
        }

        $this->assertNotEquals(
          $option,
          $this->getSelectedLabel($selectLocator),
          $message
        );
    }

    /**
     * Asserts that a specific value is selected.
     *
     * @param  string $selectLocator
     * @param  string $value
     * @param  string $message
     * @access public
     */
    public function assertIsSelected($selectLocator, $value, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              'Value "%s" not selected in "%s".',
              $value,
              $selectLocator
            );
        }

        $this->assertEquals(
          $value, $this->getSelectedValue($selectLocator),
          $message
        );
    }

    /**
     * Asserts that a specific value is not selected.
     *
     * @param  string $selectLocator
     * @param  string $value
     * @param  string $message
     * @access public
     */
    public function assertIsNotSelected($selectLocator, $value, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              'Value "%s" selected in "%s".',
              $value,
              $selectLocator
            );
        }

        $this->assertNotEquals(
          $value,
          $this->getSelectedValue($selectLocator),
          $message
        );
    }

    /**
     * Asserts that something is selected.
     *
     * @param  string $selectLocator
     * @param  string $message
     * @access public
     */
    public function assertSomethingSelected($selectLocator, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              'Nothing selected from "%s".',
              $selectLocator
            );
        }

        $this->assertTrue($this->isSomethingSelected($selectLocator), $message);
    }

    /**
     * Asserts that nothing is selected.
     *
     * @param  string $selectLocator
     * @param  string $message
     * @access public
     */
    public function assertNothingSelected($selectLocator, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              'Something selected from "%s".',
              $selectLocator
            );
        }

        $this->assertFalse($this->isSomethingSelected($selectLocator), $message);
    }

    /**
     * Asserts that a given text is present.
     *
     * @param  string $pattern
     * @param  string $message
     * @access public
     */
    public function assertTextPresent($pattern, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              '"%s" not present.',
              $pattern
            );
        }

        $this->assertTrue($this->isTextPresent($pattern), $message);
    }

    /**
     * Asserts that a given text is not present.
     *
     * @param  string $pattern
     * @param  string $message
     * @access public
     */
    public function assertTextNotPresent($pattern, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              '"%s" present.',
              $pattern
            );
        }

        $this->assertFalse($this->isTextPresent($pattern), $message);
    }

    /**
     * Asserts that the title is equal to a given string.
     *
     * @param  string $title
     * @param  string $message
     * @access public
     */
    public function assertTitleEquals($title, $message = '')
    {
        $this->assertEquals($title, $this->getTitle(), $message);
    }

    /**
     * Asserts that the title is not equal to a given string.
     *
     * @param  string $title
     * @param  string $message
     * @access public
     */
    public function assertTitleNotEquals($title, $message = '')
    {
        $this->assertNotEquals($title, $this->getTitle(), $message);
    }

    /**
     * Asserts that something is visible.
     *
     * @param  string $locator
     * @param  string $message
     * @access public
     */
    public function assertVisible($locator, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              '"%s" not visible.',
              $locator
            );
        }

        $this->assertTrue($this->isVisible($locator), $message);
    }

    /**
     * Asserts that something is not visible.
     *
     * @param  string $locator
     * @param  string $message
     * @access public
     */
    public function assertNotVisible($locator, $message = '')
    {
        if ($message == '') {
            $message = sprintf(
              '"%s" visible.',
              $locator
            );
        }

        $this->assertFalse($this->isVisible($locator), $message);
    }

    /**
     * Template Method that is called after Selenium actions.
     *
     * @param  string $action
     * @access protected
     * @since  Method available since Release 3.1.0
     */
    protected function defaultAssertions($action)
    {
    }

    /**
     * Send a command to the Selenium RC server.
     *
     * @param  string $command
     * @param  array  $arguments
     * @return string
     * @access protected
     * @author Shin Ohno <ganchiku@gmail.com>
     * @author Bjoern Schotte <schotte@mayflower.de>
     * @since  Method available since Release 3.1.0
     */
    protected function doCommand($command, array $arguments = array())
    {
        $url = sprintf(
          'http://%s:%s/selenium-server/driver/?cmd=%s',
          $this->host,
          $this->port,
          urlencode($command)
        );

        for ($i = 0; $i < count($arguments); $i++) {
            $argNum = strval($i + 1);
            $url .= sprintf('&%s=%s', $argNum, urlencode(trim($arguments[$i])));
        }

        if (isset(self::$sessionId[$this->host][$this->port][$this->browser])) {
            $url .= sprintf('&%s=%s', 'sessionId', self::$sessionId[$this->host][$this->port][$this->browser]);
        }

        if (!$handle = @fopen($url, 'r')) {
            throw new RuntimeException(
              'Could not connect to the Selenium RC server.'
            );
        }

        stream_set_blocking($handle, 1);
        stream_set_timeout($handle, 0, $this->timeout);

        $info     = stream_get_meta_data($handle);
        $response = '';

        while ((!feof($handle)) && (!$info['timed_out'])) {
            $response .= fgets($handle, 4096); 
            $info = stream_get_meta_data($handle); 
        }

        fclose($handle);

        if (!preg_match('/^OK/', $response)) {
            $this->stop();

            throw new RuntimeException(
              'The response from the Selenium RC server is invalid: ' . $response
            );
        }

        return $response;
    }

    /**
     * Send a command to the Selenium RC server and treat the result
     * as a boolean.
     *
     * @param  string $command
     * @param  array  $arguments
     * @return boolean
     * @access protected
     * @author Shin Ohno <ganchiku@gmail.com>
     * @author Bjoern Schotte <schotte@mayflower.de>
     * @since  Method available since Release 3.1.0
     */
    protected function getBoolean($command, array $arguments)
    {
        $result = $this->getString($command, $arguments);

        switch ($result) {
            case 'true':  return TRUE;

            case 'false': return FALSE;

            default: {
                $this->stop();

                throw new RuntimeException(
                  'Result is neither "true" nor "false": ' . PHPUnit_Util_Type::toString($result, TRUE)
                );
            }
        }
    }

    /**
     * Send a command to the Selenium RC server and treat the result
     * as a number.
     *
     * @param  string $command
     * @param  array  $arguments
     * @return numeric
     * @access protected
     * @author Shin Ohno <ganchiku@gmail.com>
     * @author Bjoern Schotte <schotte@mayflower.de>
     * @since  Method available since Release 3.1.0
     */
    protected function getNumber($command, array $arguments)
    {
        $result = $this->getString($command, $arguments);

        if (!is_numeric($result)) {
            $this->stop();

            throw new RuntimeException(
              'Result is not numeric: ' . PHPUnit_Util_Type::toString($result, TRUE)
            );
        }

        return $result;
    }

    /**
     * Send a command to the Selenium RC server and treat the result
     * as a string.
     *
     * @param  string $command
     * @param  array  $arguments
     * @return string
     * @access protected
     * @author Shin Ohno <ganchiku@gmail.com>
     * @author Bjoern Schotte <schotte@mayflower.de>
     * @since  Method available since Release 3.1.0
     */
    protected function getString($command, array $arguments)
    {
        try {
            $result = $this->doCommand($command, $arguments);
        }

        catch (RuntimeException $e) {
            $this->stop();

            throw $e;
        }

        return (strlen($result) > 3) ? substr($result, 3) : '';
    }

    /**
     * Send a command to the Selenium RC server and treat the result
     * as an array of strings.
     *
     * @param  string $command
     * @param  array  $arguments
     * @return array
     * @access protected
     * @author Shin Ohno <ganchiku@gmail.com>
     * @author Bjoern Schotte <schotte@mayflower.de>
     * @since  Method available since Release 3.1.0
     */
    protected function getStringArray($command, array $arguments)
    {
        $csv     = $this->getString($command, $arguments);
        $token   = '';
        $tokens  = array();
        $letters = preg_split('//', $csv, -1, PREG_SPLIT_NO_EMPTY);
        $count   = count($letters);

        for ($i = 0; $i < $count; $i++) {
            $letter = $letters[$i];

            switch($letter) {
                case '\\': {
                    $letter = $letters[++$i];
                    $token .= $letter;
                }
                break;

                case ',': {
                    $tokens[] = $token;
                    $token    = '';
                }
                break;

                default: {
                    $token .= $letter;
                }
            }
        }

        $tokens[] = $token;

        return $tokens;
    }

    /**
     * @return array
     * @access protected
     * @since  Method available since Release 3.2.0
     */
    protected function getCodeCoverage()
    {
        if (!empty($this->coverageScriptUrl)) {
            $url = sprintf(
              '%s?PHPUNIT_SELENIUM_TEST_ID=%s',
              $this->coverageScriptUrl,
              $this->testId
            );

            return $this->matchLocalAndRemotePaths(
              eval('return ' . file_get_contents($url) . ';')
            );
        } else {
            return array();
        }
    }

    /**
     * @param  array $coverage
     * @return array
     * @access protected
     * @author Mattis Stordalen Flister <mattis@xait.no>
     * @since  Method available since Release 3.2.9
     */
    protected function matchLocalAndRemotePaths(array &$coverage) {
        $coverageWithLocalPaths = array();

        foreach($coverage as $originalRemotePath => $value) {
            $remotePath = $originalRemotePath;
            $separator  = $this->findDirectorySeparator($remotePath);

            while (!($localpath = PHPUnit_Util_Filesystem::fileExistsInIncludePath($remotePath)) &&
                   strpos($remotePath, $separator) !== FALSE) {
                $remotePath = substr($remotePath, strpos($remotePath, $separator) + 1);
            }

            if ($localpath && md5_file($localpath) == $value['md5']) {
                $coverageWithLocalPaths[$localpath] = $value;
                unset($coverageWithLocalPaths[$localpath]['md5']);
            }
        }

        return $coverageWithLocalPaths;
    }

    /**
     * @param  string $path
     * @return string
     * @access protected
     * @author Mattis Stordalen Flister <mattis@xait.no>
     * @since  Method available since Release 3.2.9
     */
    protected function findDirectorySeparator($path) {
        if (strpos($path, '/') !== FALSE) {
            return '/';
        }

        return '\\';
    }

    /**
     * @param  string $path
     * @return array
     * @access protected
     * @author Mattis Stordalen Flister <mattis@xait.no>
     * @since  Method available since Release 3.2.9
     */
    protected function explodeDirectories($path) {
        return explode($this->findDirectorySeparator($path), dirname($path));
    }

    /**
     * @param  string $action
     * @access private
     * @since  Method available since Release 3.2.0
     */
    private function runDefaultAssertions($action)
    {
        if (!$this->inDefaultAssertions) {
            $this->inDefaultAssertions = TRUE;
            $this->defaultAssertions($action);
            $this->inDefaultAssertions = FALSE;
        }
    }
}
?>
