123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 |
- <?php
- require_once(dirname(__FILE__).'/../vendor/lime/lime.php');
- /*
- * This file is part of the symfony package.
- * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- /**
- * sfTestFunctional tests an application by using a browser simulator.
- *
- * @package symfony
- * @subpackage test
- * @author Fabien Potencier <fabien.potencier@symfony-project.com>
- * @version SVN: $Id: sfTestFunctionalBase.class.php 16170 2009-03-11 08:02:42Z fabien $
- */
- abstract class sfTestFunctionalBase
- {
- protected
- $testers = array(),
- $blockTester = null,
- $currentTester = null,
- $browser = null;
- protected static
- $test = null;
- /**
- * Initializes the browser tester instance.
- *
- * @param sfBrowserBase $browser A sfBrowserBase instance
- * @param lime_test $lime A lime instance
- */
- public function __construct(sfBrowserBase $browser, lime_test $lime = null, $testers = array())
- {
- $this->browser = $browser;
- if (is_null(self::$test))
- {
- self::$test = !is_null($lime) ? $lime : new lime_test(null, new lime_output_color());
- }
- $this->setTesters(array_merge(array(
- 'request' => 'sfTesterRequest',
- 'response' => 'sfTesterResponse',
- 'user' => 'sfTesterUser',
- ), $testers));
- // register our shutdown function
- register_shutdown_function(array($this, 'shutdown'));
- // register our error/exception handlers
- set_error_handler(array($this, 'handlePhpError'));
- set_exception_handler(array($this, 'handleException'));
- }
- /**
- * Returns the tester associated with the given name.
- *
- * @param string $name The tester name
- *
- * @param sfTester A sfTester instance
- */
- public function with($name)
- {
- if (!isset($this->testers[$name]))
- {
- throw new InvalidArgumentException(sprintf('The "%s" tester does not exist.', $name));
- }
- if ($this->blockTester)
- {
- throw new LogicException(sprintf('You cannot nest tester blocks.'));
- }
- $this->currentTester = $this->testers[$name];
- $this->currentTester->initialize();
- return $this->currentTester;
- }
- /**
- * Begins a block of test for the current tester.
- *
- * @param sfTester The current sfTester instance
- */
- public function begin()
- {
- if (!$this->currentTester)
- {
- throw new LogicException(sprintf('You must call with() before beginning a tester block.'));
- }
- return $this->blockTester = $this->currentTester;
- }
- /**
- * End a block of test for the current tester.
- *
- * @param sfTestFunctionalBase
- */
- public function end()
- {
- if (is_null($this->blockTester))
- {
- throw new LogicException(sprintf('There is not current tester block to end.'));
- }
- $this->blockTester = null;
- return $this;
- }
- /**
- * Sets the testers.
- *
- * @param array $testers An array of named testers
- */
- public function setTesters($testers)
- {
- foreach ($testers as $name => $tester)
- {
- $this->setTester($name, $tester);
- }
- }
- /**
- * Sets a tester.
- *
- * @param string $name The tester name
- * @param sfTester|string $tester A sfTester instance or a tester class name
- */
- public function setTester($name, $tester)
- {
- if (is_string($tester))
- {
- $tester = new $tester($this, self::$test);
- }
- if (!$tester instanceof sfTester)
- {
- throw new InvalidArgumentException(sprintf('The tester "%s" is not of class sfTester.', $name));
- }
- $this->testers[$name] = $tester;
- }
- /**
- * Shutdown function.
- *
- * @return void
- */
- public function shutdown()
- {
- $this->checkCurrentExceptionIsEmpty();
- }
- /**
- * Retrieves the lime_test instance.
- *
- * @return lime_test The lime_test instance
- */
- public function test()
- {
- return self::$test;
- }
- /**
- * Gets a uri.
- *
- * @param string $uri The URI to fetch
- * @param array $parameters The Request parameters
- * @param bool $changeStack Change the browser history stack?
- *
- * @return sfBrowser
- */
- public function get($uri, $parameters = array(), $changeStack = true)
- {
- return $this->call($uri, 'get', $parameters, $changeStack);
- }
- /**
- * Retrieves and checks an action.
- *
- * @param string $module Module name
- * @param string $action Action name
- * @param string $url Url
- * @param string $code The expected return status code
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function getAndCheck($module, $action, $url = null, $code = 200)
- {
- return $this->
- get(null !== $url ? $url : sprintf('/%s/%s', $module, $action))->
- with('request')->begin()->
- isParameter('module', $module)->
- isParameter('action', $action)->
- end()->
- with('response')->isStatusCode($code)
- ;
- }
- /**
- * Posts a uri.
- *
- * @param string $uri The URI to fetch
- * @param array $parameters The Request parameters
- * @param bool $changeStack Change the browser history stack?
- *
- * @return sfBrowser
- */
- public function post($uri, $parameters = array(), $changeStack = true)
- {
- return $this->call($uri, 'post', $parameters, $changeStack);
- }
- /**
- * Calls a request.
- *
- * @param string $uri URI to be invoked
- * @param string $method HTTP method used
- * @param array $parameters Additional paramaters
- * @param bool $changeStack If set to false ActionStack is not changed
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function call($uri, $method = 'get', $parameters = array(), $changeStack = true)
- {
- $this->checkCurrentExceptionIsEmpty();
- $uri = $this->browser->fixUri($uri);
- $this->test()->comment(sprintf('%s %s', strtolower($method), $uri));
- foreach ($this->testers as $tester)
- {
- $tester->prepare();
- }
- $this->browser->call($uri, $method, $parameters, $changeStack);
- return $this;
- }
- /**
- * Simulates deselecting a checkbox or radiobutton.
- *
- * @param string $name The checkbox or radiobutton id, name or text
- *
- * @return sfBrowser
- */
- public function deselect($name)
- {
- $this->browser->doSelect($name, false);
- return $this;
- }
- /**
- * Simulates selecting a checkbox or radiobutton.
- *
- * @param string $name The checkbox or radiobutton id, name or text
- *
- * @return sfBrowser
- */
- public function select($name)
- {
- $this->browser->doSelect($name, true);
- return $this;
- }
- /**
- * Simulates a click on a link or button.
- *
- * @param string $name The link or button text
- * @param array $arguments The arguments to pass to the link
- * @param array $options An array of options
- *
- * @return sfBrowser
- */
- public function click($name, $arguments = array(), $options = array())
- {
- list($uri, $method, $parameters) = $this->browser->doClick($name, $arguments, $options);
- return $this->call($uri, $method, $parameters);
- }
- /**
- * Simulates the browser back button.
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function back()
- {
- $this->test()->comment('back');
- $this->browser->back();
- return $this;
- }
- /**
- * Simulates the browser forward button.
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function forward()
- {
- $this->test()->comment('forward');
- $this->browser->forward();
- return $this;
- }
- /**
- * Outputs an information message.
- *
- * @param string $message A message
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function info($message)
- {
- $this->test()->info($message);
- return $this;
- }
- /**
- * Tests if the current request has been redirected.
- *
- * @deprecated since 1.2
- *
- * @param bool $boolean Flag for redirection mode
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function isRedirected($boolean = true)
- {
- return $this->with('response')->isRedirected($boolean);
- }
- /**
- * Checks that the current response contains a given text.
- *
- * @param string $uri Uniform resource identifier
- * @param string $text Text in the response
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function check($uri, $text = null)
- {
- $this->get($uri)->with('response')->isStatusCode();
- if ($text !== null)
- {
- $this->with('response')->contains($text);
- }
- return $this;
- }
- /**
- * Test an status code for the current test browser.
- *
- * @deprecated since 1.2
- *
- * @param string Status code to check, default 200
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function isStatusCode($statusCode = 200)
- {
- return $this->with('response')->isStatusCode($statusCode);
- }
- /**
- * Tests whether or not a given string is in the response.
- *
- * @deprecated since 1.2
- *
- * @param string Text to check
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function responseContains($text)
- {
- return $this->with('response')->contains($text);
- }
- /**
- * Tests whether or not a given key and value exists in the current request.
- *
- * @deprecated since 1.2
- *
- * @param string $key
- * @param string $value
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function isRequestParameter($key, $value)
- {
- return $this->with('request')->isParameter($key, $value);
- }
- /**
- * Tests for a response header.
- *
- * @deprecated since 1.2
- *
- * @param string $key
- * @param string $value
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function isResponseHeader($key, $value)
- {
- return $this->with('response')->isHeader($key, $value);
- }
- /**
- * Tests for the user culture.
- *
- * @deprecated since 1.2
- *
- * @param string $culture The user culture
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function isUserCulture($culture)
- {
- return $this->with('user')->isCulture($culture);
- }
- /**
- * Tests for the request is in the given format.
- *
- * @deprecated since 1.2
- *
- * @param string $format The request format
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function isRequestFormat($format)
- {
- return $this->with('request')->isFormat($format);
- }
- /**
- * Tests that the current response matches a given CSS selector.
- *
- * @deprecated since 1.2
- *
- * @param string $selector The response selector or a sfDomCssSelector object
- * @param mixed $value Flag for the selector
- * @param array $options Options for the current test
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function checkResponseElement($selector, $value = true, $options = array())
- {
- return $this->with('response')->checkElement($selector, $value, $options);
- }
- /**
- * Tests if an exception is thrown by the latest request.
- *
- * @param string $class Class name
- * @param string $message Message name
- *
- * @return sfTestBrowser The current sfTestBrowser instance
- */
- public function throwsException($class = null, $message = null)
- {
- $e = $this->browser->getCurrentException();
- if (null === $e)
- {
- $this->test()->fail('response returns an exception');
- }
- else
- {
- if (null !== $class)
- {
- $this->test()->ok($e instanceof $class, sprintf('response returns an exception of class "%s"', $class));
- }
- if (null !== $message && preg_match('/^(!)?([^a-zA-Z0-9\\\\]).+?\\2[ims]?$/', $message, $match))
- {
- if ($match[1] == '!')
- {
- $this->test()->unlike($e->getMessage(), substr($message, 1), sprintf('response exception message does not match regex "%s"', $message));
- }
- else
- {
- $this->test()->like($e->getMessage(), $message, sprintf('response exception message matches regex "%s"', $message));
- }
- }
- else if (null !== $message)
- {
- $this->test()->is($e->getMessage(), $message, sprintf('response exception message is "%s"', $message));
- }
- }
- $this->resetCurrentException();
- return $this;
- }
- /**
- * Triggers a test failure if an uncaught exception is present.
- *
- * @return bool
- */
- public function checkCurrentExceptionIsEmpty()
- {
- if (false === ($empty = $this->browser->checkCurrentExceptionIsEmpty()))
- {
- $this->test()->fail(sprintf('last request threw an uncaught exception "%s: %s"', get_class($this->browser->getCurrentException()), $this->browser->getCurrentException()->getMessage()));
- }
- return $empty;
- }
- public function __call($method, $arguments)
- {
- $retval = call_user_func_array(array($this->browser, $method), $arguments);
- // fix the fluent interface
- return $retval === $this->browser ? $this : $retval;
- }
- /**
- * Error handler for the current test browser instance.
- *
- * @param mixed $errno Error number
- * @param string $errstr Error message
- * @param string $errfile Error file
- * @param mixed $errline Error line
- */
- static public function handlePhpError($errno, $errstr, $errfile, $errline)
- {
- if (($errno & error_reporting()) == 0)
- {
- return false;
- }
- $msg = sprintf('PHP sent a "%%s" error at %s line %s (%s)', $errfile, $errline, $errstr);
- switch ($errno)
- {
- case E_WARNING:
- $msg = sprintf($msg, 'warning');
- throw new RuntimeException($msg);
- break;
- case E_NOTICE:
- $msg = sprintf($msg, 'notice');
- throw new RuntimeException($msg);
- break;
- case E_STRICT:
- $msg = sprintf($msg, 'strict');
- throw new RuntimeException($msg);
- break;
- case E_RECOVERABLE_ERROR:
- $msg = sprintf($msg, 'catchable');
- throw new RuntimeException($msg);
- break;
- }
- return false;
- }
- /**
- * Exception handler for the current test browser instance.
- *
- * @param Exception $exception The exception
- */
- function handleException(Exception $exception)
- {
- $this->test()->error(sprintf('%s: %s', get_class($exception), $exception->getMessage()));
- $traceData = $exception->getTrace();
- array_unshift($traceData, array(
- 'function' => '',
- 'file' => $exception->getFile() != null ? $exception->getFile() : 'n/a',
- 'line' => $exception->getLine() != null ? $exception->getLine() : 'n/a',
- 'args' => array(),
- ));
- $traces = array();
- $lineFormat = ' at %s%s%s() in %s line %s';
- for ($i = 0, $count = count($traceData); $i < $count; $i++)
- {
- $line = isset($traceData[$i]['line']) ? $traceData[$i]['line'] : 'n/a';
- $file = isset($traceData[$i]['file']) ? $traceData[$i]['file'] : 'n/a';
- $args = isset($traceData[$i]['args']) ? $traceData[$i]['args'] : array();
- $this->test()->error(sprintf($lineFormat,
- (isset($traceData[$i]['class']) ? $traceData[$i]['class'] : ''),
- (isset($traceData[$i]['type']) ? $traceData[$i]['type'] : ''),
- $traceData[$i]['function'],
- $file,
- $line
- ));
- }
- $this->test()->fail('An uncaught exception has been thrown.');
- }
- }
|