<?php
include(dirname(__FILE__).'/../../../../test/bootstrap/unit.php');
require_once(dirname(__FILE__).'/../../lib/sfWebBrowser.class.php');
require_once(dirname(__FILE__).'/../../lib/sfFopenAdapter.class.php');
require_once(dirname(__FILE__).'/../../lib/sfCurlAdapter.class.php');
require_once(dirname(__FILE__).'/../../lib/sfSocketsAdapter.class.php');
require_once($sf_symfony_lib_dir.'/exception/sfException.class.php');
require_once(dirname(__FILE__).'/../../lib/sfWebBrowserInvalidResponseException.php');
require_once($sf_symfony_lib_dir.'/config/sfConfig.class.php');
require_once($sf_symfony_lib_dir.'/util/sfDomCssSelector.class.php');
require_once($sf_symfony_lib_dir.'/util/sfToolkit.class.php');
// Configuration
// -- this script is needed for some tests. It is located in plugin's test/unit/utils folder
$dump_headers_url = 'http://localhost/dumpheaders.php';
// tests
$nb_test_orig = 73;
$adapter_list = array('sfCurlAdapter', 'sfFopenAdapter', 'sfSocketsAdapter');
// -- sites used for testing requests
$example_site_url = 'http://www.google.com';
$askeet_params = array(
'url' => 'http://www.askeet.com',
'login' => 'francois',
'password' => 'llactnevda2',
);
// -- cookies, file and directory automatically created
$cookies_dir = dirname(__FILE__).'/../data/sfCurlAdapter';
$cookies_file = $cookies_dir.'/cookies.txt';
/**
* stub class
*
**/
class myTestWebBrowser extends sfWebBrowser
{
protected $requestMethod;
public function call($uri, $method = 'GET', $parameters = array(), $headers = array(), $changeStack = true)
{
parent::call($uri, $method, $parameters, $headers, $changeStack);
$this->requestMethod = $this->stack[$this->stackPosition]['method'];
}
public function getRequestMethod()
{
return $this->requestMethod;
}
}
$t = new lime_test($nb_test_orig * count($adapter_list), new lime_output_color());
foreach($adapter_list as $adapter)
{
$t->diag('current adapter: '.$adapter);
$t->diag('initialization');
$b = new sfWebBrowser(array(), $adapter);
$t->is($b->getUserAgent(), '', 'a new browser has an empty user agent');
$t->is($b->getResponseText(), '', 'a new browser has an empty response');
$t->is($b->getResponseCode(), '', 'a new browser has an empty response code');
$t->is($b->getResponseHeaders(), array(), 'a new browser has empty reponse headers');
$t->diag('utility methods');
$b = new sfWebBrowser(array(), $adapter);
$t->is($b->setUserAgent('foo bar')->getUserAgent(), 'foo bar', 'setUserAgent() sets the user agent');
$t->is($b->setResponseText('foo bar')->getResponseText(), 'foo bar', 'setResponseText() extracts the response');
$t->is($b->setResponseCode('foo 123 bar')->getResponseCode(), '123', 'setResponseCode() extracts the three-digits code');
$t->is($b->setResponseCode('foo 12 bar')->getResponseCode(), '', 'setResponseCode() fails silently when response status is incorrect');
$t->is_deeply($b->setResponseHeaders(array('HTTP1.1 200 OK', 'foo: bar', 'bar: baz'))->getResponseHeaders(), array('Foo' => 'bar', 'Bar' => 'baz'), 'setResponseHeaders() extracts the headers array');
$t->is_deeply($b->setResponseHeaders(array('ETag: "535a8-9fb-44ff4a13"', 'WWW-Authenticate: Basic realm="Myself"'))->getResponseHeaders(), array('ETag' => '"535a8-9fb-44ff4a13"', 'WWW-Authenticate' => 'Basic realm="Myself"'), 'setResponseHeaders() extracts the headers array and accepts response headers with several uppercase characters');
$t->is_deeply($b->setResponseHeaders(array('HTTP1.1 200 OK', 'foo: bar', 'bar:baz', 'baz:bar'))->getResponseHeaders(), array('Foo' => 'bar'), 'setResponseHeaders() ignores malformed headers');
$t->diag('exceptions');
$b = new sfWebBrowser(array(), $adapter);
try
{
$b->get('htp://askeet');
$t->fail('get() throws an exception when passed an uri which is neither http nor https');
}
catch (Exception $e)
{
$t->pass('get() throws an exception when passed an uri which is neither http nor https');
}
$t->diag('simple GET request');
$t->like($b->get($dump_headers_url)->getResponseText(), '/\[REQUEST_METHOD\] => GET/', 'get() performs a GET request');
$t->isa_ok($b, 'sfWebBrowser', 'get() make a web request and returns a browser object');
$t->is($b->getResponseCode(), 200, 'get() fills up the browser status code with the response');
$t->like($b->get($example_site_url)->getResponseHeader('Content-Type'), '/text\/html/', 'get() populates the header array');
$t->like(strtolower($b->getResponseText()), '/<\/body>/', 'get() populates the HTML of the response');
$t->diag('simple HEAD request');
$t->like($b->head($dump_headers_url)->getResponseHeader('Content-Type'), '/text\/html/', 'head() populates the header array');
$t->is($b->getResponseText(), '', 'HEAD requests do not return a response body');
$t->diag('simple POST request');
$t->like($b->post($dump_headers_url)->getResponseText(), '/\[REQUEST_METHOD\] => POST/', 'post() performs a POST request');
$t->like($b->post($dump_headers_url, array('post body'))->getResponseText(), '/post body/', 'post() sends body to server');
$t->diag('simple PUT request');
$t->like($b->put($dump_headers_url)->getResponseText(), '/\[REQUEST_METHOD\] => PUT/', 'put() performs a PUT request');
$t->like($b->put($dump_headers_url, array('PUT body'))->getResponseText(), '/PUT body/', 'put() sends body to server');
$t->diag('simple DELETE request');
$t->like($b->delete($dump_headers_url)->getResponseText(), '/\[REQUEST_METHOD\] => DELETE/', 'delete() performs a DELETE request');
$t->diag('arbitrary request');
$t->like($b->call($dump_headers_url, 'MICHEL')->getResponseText(), '/\[REQUEST_METHOD\] => MICHEL/', 'call() supports any HTTP methods');
$t->diag('Response formats methods');
$b = new sfWebBrowser(array(), $adapter);
$b->get($example_site_url);
$t->like($b->getResponseText(), '/<body .*>/', 'getResponseText() returns the response text');
$t->unlike($b->getResponseBody(), '/<body>/', 'getResponseBody() returns the response body');
$t->isa_ok($b->getResponseDom(), 'DOMDocument', 'getResponseDom() returns the response Dom');
$t->isa_ok($b->getResponseDomCssSelector(), 'sfDomCssSelector', 'getResponseDomCssSelector() returns a CSS selector on the response Dom');
$b->get('http://rss.cnn.com/rss/cnn_topstories.rss');
$t->isa_ok($b->getResponseXml(), 'SimpleXMLElement', 'getResponseXml() returns the response as a SimpleXML Element');
$b->get('http://www.w3.org/StyleSheets/home.css');
try
{
$b->getResponseXml();
$t->fail('Incorrect XML throws an exception');
}
catch (Exception $e)
{
$t->pass('Incorrect XML throws an exception');
}
try
{
$t->diag('absolute and relative URls');
$b = new sfWebBrowser(array(), $adapter);
$t->like($b->get($askeet_params['url'])->getResponseText(), '/<h1>featured questions<\/h1>/', 'get() understands absolute urls');
$t->like($b->get($askeet_params['url'].'/index/1')->getResponseText(), '/<h1>popular questions<\/h1>/', 'get() understands absolute urls');
$t->like($b->get('/recent/1')->getResponseText(), '/<h1>recent questions<\/h1>/', 'get() understands relative urls with a trailing slash');
$t->like($b->get('/')->get('recent/1')->getResponseText(), '/<h1>recent questions<\/h1>/', 'get() understands relative urls without a trailing slash');
$t->diag('interaction methods');
$b = new sfWebBrowser(array(), $adapter);
$t->like($b->get($askeet_params['url'])->click('activities')->getResponseText(), '/tag "activities"/', 'click() clicks on a link and executes the related request');
$t->like($b->get($askeet_params['url'])->click('/tag/activities')->getResponseText(), '/tag "activities"/', 'click() clicks on a link and executes the related request');
$t->like($b->click('askeet')->getResponseText(), '/<h1>featured questions<\/h1>/', 'click() clicks on an image if it finds the argument in the alt');
$t->like($b->click('search it', array('search' => 'foo'))->getResponseText(), '/<h1>questions matching "foo"<\/h1>/', 'click() clicks on a form input');
$t->like($b->setField('search', 'bar')->click('search it')->getResponseText(), '/<h1>questions matching "bar"<\/h1>/', 'setField() fills a form input');
}
catch (Exception $e)
{
$t->fail(sprintf('%s : skipping askeet related tests', $e->getMessage()));
}
try
{
$t->diag('GET request with parameters');
$b = new sfWebBrowser(array(), $adapter);
$test_params = array('foo' => 'bar', 'baz' => '1');
$t->like($b->get($dump_headers_url, $test_params)->getResponseText(), '/\?foo=bar&baz=1/', 'get() can pass parameters with the second argument');
$t->like($b->get($dump_headers_url.'?'.http_build_query($test_params))->getResponseText(), '/\?foo=bar&baz=1/', 'get() can pass parameters concatenated to the URI as a query string');
$t->unlike($b->get($dump_headers_url.'?'.http_build_query($test_params))->getResponseText(), '/\?foo=bar&baz=1\&/', 'get() with an URL already containing request parameters doesn\'t add an extra &');
$t->like($b->get($dump_headers_url.'?'.http_build_query($test_params), array('biz' => 'bil'))->getResponseText(), '/\?foo=bar&baz=1&biz=bil/', 'get() can pass parameters concatenated to the URI as a query string and other parameters as a second argument');
$b = new sfWebBrowser(array(), $adapter);
$b->get($dump_headers_url);
$t->diag('Default request headers');
$headers = array('Accept-language' => 'fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3');
$b = new sfWebBrowser($headers, $adapter);
$t->like($b->get($dump_headers_url)->getResponseText(), "/fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3/",
'sfWebBrowser constructor accepts default request headers as first parameter');
$t->like($b->get($dump_headers_url, array(), array('Accept-language' => 'en-gb;q=0.8,en-us;q=0.5,en;q=0.3'))->getResponseText(), "/en-gb;q=0.8,en-us;q=0.5,en;q=0.3/",
'Default request headers are overriden by request specific headers');
$t->diag('Request headers support');
$b = new sfWebBrowser(array(), $adapter);
$headers = array('Accept-language' => 'fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3',
'Accept' => 'text/xml');
$t->like($b->get($dump_headers_url, array(), $headers)->getResponseText(), "/fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3/",
'get() can pass request headers with the third argument');
$t->like($b->post($dump_headers_url, array(), $headers)->getResponseText(), "/fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3/",
'post() can pass request headers with the third argument');
$msg = "get() can pass request headers not common that are defined uppercase in RFC 2616";
try
{
$t->like($b->get($dump_headers_url, array(), array('TE' =>'trailers, deflate;q=0.5'))->getResponseText(), "/\[TE\] => trailers, deflate;q=0.5/",
$msg);
}
catch (Exception $e)
{
$t->fail($msg);
}
$msg = 'get() can pass request headers not common that are IE7 dependent: see http://www.w3.org/2006/http-header, now: ';
$field = '';
try
{
$headers = array('UA-CPU'=>'x86', 'UA-OS'=>'MacOS', 'UA-Color'=>'color16', 'UA-Pixels'=>'240x320');
$resp = $b->get($dump_headers_url, array(), $headers)->getResponseText();
foreach ($headers as $field => $value)
{
$t->like($resp, "/\[$field\] => $value/", $msg.$field);
}
}
catch (Exception $e)
{
$t->fail($msg.field.' - header refused');
}
$t->diag('Encoded response body support');
$headers = array('Accept-Encoding' => 'gzip');
$t->like($b->get($dump_headers_url, array(), $headers)->getResponseText(), "/gzip/",
'getResponseText() can decode gzip encoded response body');
$headers = array('Accept-Encoding' => 'deflate');
$t->like($b->get($dump_headers_url, array(), $headers)->getResponseText(), "/deflate/",
'getResponseText() can decode deflate encoded response body');
$encodings = array();
if (function_exists('gzuncompress'))
{
$encodings[] = 'deflate';
}
if (function_exists('gzinflate'))
{
$encodings[] = 'gzip';
}
$target_headers = implode(',', $encodings);
$t->like($b->get($dump_headers_url, array(), $headers)->getResponseText(), "/$target_headers/",
'sfWebBrowser autosets accept-encoding headers depending on php capabilities');
$encodings = array();
if (function_exists('gzinflate'))
{
$encodings[] = 'gzip';
}
if (function_exists('gzuncompress'))
{
$encodings[] = 'deflate';
}
$headers = array('accept-encoding' => 'bzip2');
array_unshift($encodings, 'bzip2');
$target_headers = implode(',', $encodings);
$t->like($b->get($dump_headers_url, array(), $headers)->getResponseText(), "/$target_headers/",
'it is possible to set supplementary encodings');
$t->diag('history methods');
$b = new sfWebBrowser(array(), $adapter);
$b->get($dump_headers_url);
$b->get($dump_headers_url.'?foo=bar');
$b->back();
$t->unlike($b->getResponseText(), '/foo=bar/', 'back() executes again the previous request in the history');
$b->forward();
$t->like($b->getResponseText(), '/foo=bar/', 'forward() executes again the next request in the history');
$b->reload();
$t->like($b->getResponseText(), '/foo=bar/', 'reload() executes again the current request in the history');
}
catch (Exception $e)
{
$t->fail(sprintf('%s : skipping tests that need it', $e->getMessage()));
}
$t->diag('Error management');
try
{
$b->get('http://nonexistent');
$t->fail('an exception is thrown when an adapter error occurs');
}
catch (Exception $e)
{
$t->pass('an exception is thrown when an adapter error occurs');
}
$t->diag('responseIsError()');
$t->is($b->get($example_site_url . '/nonexistentpage.html')->responseIsError(), true, 'responseIsError() returns true when response is an error');
$t->is($b->get($example_site_url)->responseIsError(), false, 'responseIsError() returns false when response is not an error');
$t->diag('browser restart');
$b->restart();
try
{
$b->reload();
$t->fail('restart() reinitializes the browser history');
}
catch (Exception $e)
{
$t->pass('restart() reinitializes the browser history');
}
$t->is($b->getResponseText(), '', 'restart() reinitializes the response');
$t->diag('redirects');
$b = new sfWebBrowser(array(), $adapter);
$b->get('http://www.symfony-project.com/trac/wiki/sfUJSPlugin');
$t->like($b->getResponseText(), '/learn more about the unobtrusive approach/', 'follows 302 redirect after a GET');
$b = new myTestWebBrowser(array(), $adapter);
$b->call($askeet_params['url'].'/index.php/login', 'POST', array('nickname' => $askeet_params['login'], 'password' => $askeet_params['password']));
//$t->like($b->getResponseText(), '/url='.preg_quote($askeet_params['url'], '/').'\/index\.php/', 'does NOT follow a 302 redirect after a POST');
$t->like($b->getResponseText(), '/featured questions/', 'follows 302 redirect after POST ****** DESPITE THE HTTP SPEC ******');
$t->is($b->getRequestMethod(), 'GET', 'request method is changed to GET after POST for 302 redirect ***** DESPITE THE HTTP SPEC *****');
$t->todo('request method is changed to GET after POST for 303 redirect');
if ($adapter == 'sfCurlAdapter')
{
$t->diag('cookies');
$b = new sfWebBrowser(array(), $adapter, array(
'cookies' => true,
'cookies_file' => $cookies_file,
'cookies_dir' => $cookies_dir,
));
$b->call($askeet_params['url'].'/login', 'POST', array(
'nickname' => $askeet_params['login'],
'password' => $askeet_params['password'],
));
$t->like($b->getResponseBody(), '/'.$askeet_params['login'].' profile/', 'Understands cookies');
rmdir($cookies_dir);
rmdir(dirname(__FILE__).'/../data');
$t->diag('file uploads');
$b->post($dump_headers_url, array(
'test_file' => realpath(__FILE__),
));
$t->like($b->getResponseText(), '/\[test_file\]/', 'uploads a file');
}
else
{
$t->todo('Understands cookies');
$t->todo('Uploads a file');
}
$t->diag('make XML exchanges to test Content-Type');
$url = 'http://www.abundanttech.com/WebServices/Population/population.asmx';
$headers = array(
'Soapaction' => 'http://www.abundanttech.com/WebServices/Population/getWorldPopulation',
'Content-Type' => 'text/xml'
);
$requestBody = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><getWorldPopulation xmlns="http://www.abundanttech.com/WebServices/Population" /></soap:Body></soap:Envelope>';
$b = new sfWebBrowser(array(), $adapter);
$b->post($url, $requestBody, $headers);
$t->like($b->getResponseText(), '/<Country>World<\/Country>/', 'make a low-level SOAP call without parameter');
$url = 'http://www.abundanttech.com/WebServices/Population/population.asmx';
$headers = array(
'Soapaction' => 'http://www.abundanttech.com/WebServices/Population/getPopulation',
'Content-Type' => 'text/xml'
);
$requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pop="http://www.abundanttech.com/WebServices/Population"><soapenv:Header/><soapenv:Body><pop:getPopulation><pop:strCountry>Comoros</pop:strCountry></pop:getPopulation></soapenv:Body></soapenv:Envelope>';
$b = new sfWebBrowser(array(), $adapter);
$b->post($url, $requestBody, $headers);
$t->like($b->getResponseText(), '/<Country>Comoros<\/Country>/', 'make a low-level SOAP call with parameter');
}