diff --git a/composer.json b/composer.json index 8bc14b6..9939a7d 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,9 @@ "license": "GPL-2.0+", "require": { "php": ">=5.4.5", + "behat/mink": "~1.6", + "behat/mink-goutte-driver": "dev-master", + "fabpot/goutte": "dev-master", "sdboyer/gliph": "0.1.*", "symfony/class-loader": "2.6.0-beta1", "symfony/css-selector": "2.6.0-beta1", diff --git a/composer.lock b/composer.lock index 868008a..7671fb2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,9 +4,171 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "34a4f5a56891e51217c305b97e8cc675", + "hash": "96cc29836072e508794530779438cf8b", "packages": [ { + "name": "behat/mink", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Mink.git", + "reference": "090900a0049c441f1e072bbd837db4079b2250c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Mink/zipball/090900a0049c441f1e072bbd837db4079b2250c5", + "reference": "090900a0049c441f1e072bbd837db4079b2250c5", + "shasum": "" + }, + "require": { + "php": ">=5.3.1", + "symfony/css-selector": "~2.0" + }, + "suggest": { + "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)", + "behat/mink-goutte-driver": "fast headless driver for any app without JS emulation", + "behat/mink-selenium2-driver": "slow, but JS-enabled driver for any app (requires Selenium2)", + "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Mink": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Web acceptance testing framework for PHP 5.3", + "homepage": "http://mink.behat.org/", + "keywords": [ + "browser", + "testing", + "web" + ], + "time": "2014-09-26 09:25:05" + }, + { + "name": "behat/mink-browserkit-driver", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/MinkBrowserKitDriver.git", + "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/MinkBrowserKitDriver/zipball/aed8f4a596b79014a75254c3e337511c33e38cbd", + "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd", + "shasum": "" + }, + "require": { + "behat/mink": "~1.6@dev", + "php": ">=5.3.1", + "symfony/browser-kit": "~2.0", + "symfony/dom-crawler": "~2.0" + }, + "require-dev": { + "silex/silex": "~1.2" + }, + "type": "mink-driver", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Mink\\Driver": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Symfony2 BrowserKit driver for Mink framework", + "homepage": "http://mink.behat.org/", + "keywords": [ + "Mink", + "Symfony2", + "browser", + "testing" + ], + "time": "2014-09-26 11:35:19" + }, + { + "name": "behat/mink-goutte-driver", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Behat/MinkGoutteDriver.git", + "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/MinkGoutteDriver/zipball/2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", + "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", + "shasum": "" + }, + "require": { + "behat/mink": "~1.6@dev", + "behat/mink-browserkit-driver": "~1.2@dev", + "fabpot/goutte": "~1.0.4|~2.0", + "php": ">=5.3.1" + }, + "type": "mink-driver", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Mink\\Driver": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Goutte driver for Mink framework", + "homepage": "http://mink.behat.org/", + "keywords": [ + "browser", + "goutte", + "headless", + "testing" + ], + "time": "2014-10-09 09:21:12" + }, + { "name": "doctrine/annotations", "version": "v1.2.1", "source": { @@ -567,6 +729,55 @@ "time": "2014-11-06 08:59:44" }, { + "name": "fabpot/goutte", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/Goutte.git", + "reference": "513c8f832645626f60e8441955ceb339d89d0c23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/513c8f832645626f60e8441955ceb339d89d0c23", + "reference": "513c8f832645626f60e8441955ceb339d89d0c23", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": ">=4,<6", + "php": ">=5.4.0", + "symfony/browser-kit": "~2.1", + "symfony/css-selector": "~2.1", + "symfony/dom-crawler": "~2.1" + }, + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Goutte\\": "Goutte" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A simple PHP Web Scraper", + "homepage": "https://github.com/FriendsOfPHP/Goutte", + "keywords": [ + "scraper" + ], + "time": "2014-11-17 13:32:14" + }, + { "name": "guzzlehttp/guzzle", "version": "5.0.3", "source": { @@ -1642,6 +1853,61 @@ "time": "2014-10-20 20:55:17" }, { + "name": "symfony/browser-kit", + "version": "v2.5.7", + "target-dir": "Symfony/Component/BrowserKit", + "source": { + "type": "git", + "url": "https://github.com/symfony/BrowserKit.git", + "reference": "4792cf459d0298bcf4b4f56adfa5d11afd601fc9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/4792cf459d0298bcf4b4f56adfa5d11afd601fc9", + "reference": "4792cf459d0298bcf4b4f56adfa5d11afd601fc9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/dom-crawler": "~2.0" + }, + "require-dev": { + "symfony/css-selector": "~2.0", + "symfony/process": "~2.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\BrowserKit\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "http://symfony.com", + "time": "2014-10-26 07:41:27" + }, + { "name": "symfony/class-loader", "version": "v2.6.0-BETA1", "target-dir": "Symfony/Component/ClassLoader", @@ -1856,6 +2122,59 @@ "time": "2014-11-03 03:55:50" }, { + "name": "symfony/dom-crawler", + "version": "v2.5.7", + "target-dir": "Symfony/Component/DomCrawler", + "source": { + "type": "git", + "url": "https://github.com/symfony/DomCrawler.git", + "reference": "3245e012a7c88fd9ba5896be8d9b3a8bb99db869" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/3245e012a7c88fd9ba5896be8d9b3a8bb99db869", + "reference": "3245e012a7c88fd9ba5896be8d9b3a8bb99db869", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/css-selector": "~2.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\DomCrawler\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "http://symfony.com", + "time": "2014-11-20 13:22:25" + }, + { "name": "symfony/event-dispatcher", "version": "v2.6.0-BETA1", "target-dir": "Symfony/Component/EventDispatcher", @@ -2522,6 +2841,8 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "behat/mink-goutte-driver": 20, + "fabpot/goutte": 20, "symfony/class-loader": 10, "symfony/css-selector": 10, "symfony/debug": 10, diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 43d7368..02da883 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -113,7 +113,9 @@ * @see http://php.net/manual/reserved.variables.server.php * @see http://php.net/manual/function.time.php */ -define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']); +if (!defined('REQUEST_TIME')) { + define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']); +} /** * Regular expression to match PHP function names. @@ -141,7 +143,9 @@ * * This strips two levels of directories off the current directory. */ -define('DRUPAL_ROOT', dirname(dirname(__DIR__))); +if (!defined('DRUPAL_ROOT')) { + define('DRUPAL_ROOT', dirname(dirname(__DIR__))); +} /** * Returns the appropriate configuration directory. @@ -978,7 +982,9 @@ function drupal_valid_test_ua($new_prefix = NULL) { // Perform a basic check on the User-Agent HTTP request header first. Any // inbound request that uses the simpletest UA header needs to be validated. - if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $_SERVER['HTTP_USER_AGENT'], $matches)) { + $http_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : NULL; + $user_agent = isset($_COOKIE['SIMPLETEST_USER_AGENT']) ? $_COOKIE['SIMPLETEST_USER_AGENT'] : $http_user_agent; + if (isset($user_agent) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $user_agent, $matches)) { list(, $prefix, $time, $salt, $hmac) = $matches; $check_string = $prefix . ';' . $time . ';' . $salt; // Read the hash salt prepared by drupal_generate_test_ua(). diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 968192e..12ff582 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -280,7 +280,8 @@ function install_begin_request(&$install_state) { // The user agent header is used to pass a database prefix in the request when // running tests. However, for security reasons, it is imperative that no // installation be permitted using such a prefix. - if ($install_state['interactive'] && strpos($request->server->get('HTTP_USER_AGENT'), 'simpletest') !== FALSE && !drupal_valid_test_ua()) { + $user_agent = $request->cookies->get('SIMPLETEST_USER_AGENT') ?: $request->server->get('HTTP_USER_AGENT'); + if ($install_state['interactive'] && strpos($user_agent, 'simpletest') !== FALSE && !drupal_valid_test_ua()) { header($request->server->get('SERVER_PROTOCOL') . ' 403 Forbidden'); exit; } diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index feeffd4..766eba9 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -270,7 +270,8 @@ function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpun // exec in a subshell so that the environment is isolated when running tests // via the simpletest UI. - $ret = exec(join($command, " ")); + $ret = exec(join($command, " "), $output); + echo implode(PHP_EOL, $output) . PHP_EOL; chdir($old_cwd); return $ret; } diff --git a/core/modules/simpletest/src/BrowserTestBase.php b/core/modules/simpletest/src/BrowserTestBase.php new file mode 100644 index 0000000..6522453 --- /dev/null +++ b/core/modules/simpletest/src/BrowserTestBase.php @@ -0,0 +1,1361 @@ +skipClasses[__CLASS__] = TRUE; + } + + /** + * Initializes mink sessions. + */ + protected function initMink() { + $driver = new GoutteDriver(); + $session = new Session($driver); + $this->mink = new Mink(); + $this->mink->registerSession('goutte', $session); + $this->mink->setDefaultSessionName('goutte'); + $this->registerSessions(); + return $session; + } + + /** + * Registers additional mink sessions. + * + * Tests wishing to use a different driver or change the default driver should + * override this method. + * + * @code + * // Register a new session that uses the MinkPonyDriver. + * $pony = new MinkPonyDriver(); + * $session = new Session($pony); + * $this->mink->registerSession('pony', $session); + * @endcode + */ + protected function registerSessions() {} + + /** + * {@inheritdoc} + */ + public function setUp() { + global $base_url; + parent::setUp(); + + // Get and set the domain of the environment we are running our test + // coverage against. + $base_url = getenv('SIMPLETEST_BASE_URL'); + if (!$base_url) { + throw new \InvalidArgumentException('You must provide a SIMPLETEST_BASE_URL environment variable to run PHPUnit based functional tests.'); + } + + // Setup $_SERVER variable. + $parsed_url = parse_url($base_url); + $host = $parsed_url['host'] . (isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''); + $path = isset($parsed_url['path']) ? rtrim(rtrim($parsed_url['path']), '/') : ''; + $port = (isset($parsed_url['port']) ? $parsed_url['port'] : 80); + if ($path == '/') { + $path = ''; + } + // If the passed URL schema is 'https' then setup the $_SERVER variables + // properly so that testing will run under HTTPS. + if ($parsed_url['scheme'] == 'https') { + $_SERVER['HTTPS'] = 'on'; + } + $_SERVER['HTTP_HOST'] = $host; + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $_SERVER['SERVER_ADDR'] = '127.0.0.1'; + $_SERVER['SERVER_PORT'] = $port; + $_SERVER['SERVER_SOFTWARE'] = NULL; + $_SERVER['SERVER_NAME'] = 'localhost'; + $_SERVER['REQUEST_URI'] = $path .'/'; + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SCRIPT_NAME'] = $path .'/index.php'; + $_SERVER['SCRIPT_FILENAME'] = $path .'/index.php'; + $_SERVER['PHP_SELF'] = $path .'/index.php'; + $_SERVER['HTTP_USER_AGENT'] = 'Drupal command line'; + + // Install drupal test site. + $this->prepareEnvironment(); + $this->installDrupal(); + + // Setup Mink. + $session = $this->initMink(); + + // In order to debug web tests you need to either set a cookie, have the + // Xdebug session in the URL or set an environment variable in case of CLI + // requests. If the developer listens to connection when running tests, by + // default the cookie is not forwarded to the client side, so you cannot + // debug the code running on the test site. In order to make debuggers work + // this bit of information is forwarded. Make sure that the debugger listens + // to at least three external connections. + $request = \Drupal::request(); + $cookie_params = $request->cookies; + if ($cookie_params->has('XDEBUG_SESSION')) { + $session->setCookie('XDEBUG_SESSION', $cookie_params->get('XDEBUG_SESSION')); + } + // For CLI requests, the information is stored in $_SERVER. + $server = $request->server; + if ($server->has('XDEBUG_CONFIG')) { + // $_SERVER['XDEBUG_CONFIG'] has the form "key1=value1 key2=value2 ...". + $pairs = explode(' ', $server->get('XDEBUG_CONFIG')); + foreach ($pairs as $pair) { + list($key, $value) = explode('=', $pair); + // Account for key-value pairs being separated by multiple spaces. + if (trim($key, ' ') == 'idekey') { + $session->setCookie('XDEBUG_SESSION', trim($value, ' ')); + } + } + } + } + + /** + * Ensures test files are deletable within file_unmanaged_delete_recursive(). + * + * Some tests chmod generated files to be read only. During + * BrowserTestBase::cleanupEnvironment() and other cleanup operations, + * these files need to get deleted too. + */ + public static function filePreDeleteCallback($path) { + chmod($path, 0700); + } + + /** + * Clean up the simpletest environment. + */ + protected function cleanupEnvironment() { + // Remove all prefixed tables. + $original_connection_info = Database::getConnectionInfo('simpletest_original_default'); + $original_prefix = $original_connection_info['default']['prefix']['default']; + $test_connection_info = Database::getConnectionInfo('default'); + $test_prefix = $test_connection_info['default']['prefix']['default']; + if ($original_prefix != $test_prefix) { + $tables = Database::getConnection()->schema()->findTables($test_prefix . '%'); + $prefix_length = strlen($test_prefix); + foreach ($tables as $table) { + if (Database::getConnection()->schema()->dropTable(substr($table, $prefix_length))) { + unset($tables[$table]); + } + } + } + + // Delete test site directory. + file_unmanaged_delete_recursive($this->siteDirectory, array($this, 'filePreDeleteCallback')); + } + + /** + * {@inheritdoc} + */ + public function tearDown() { + parent::tearDown(); + + $this->cleanupEnvironment(); + + // Destroy the testing kernel. + if (isset($this->kernel)) { + $this->kernel->shutdown(); + } + + // Ensure that internal logged in variable is reset. + $this->loggedInUser = FALSE; + + if ($this->mink) { + $this->mink->stopSessions(); + } + } + + /** + * Returns Mink session. + * + * @param string|null $name + * Name of the session OR active session will be used. + * + * @return \Behat\Mink\Session + * The active mink session object. + */ + public function getSession($name = NULL) { + return $this->mink->getSession($name); + } + + /** + * Returns Mink assert session. + * + * @param string|null $name + * (optional) Name of the session. Defaults to the active session. + * + * @return \Drupal\Simpletest\WebAssert + * A new web-assert option for asserting the presence of elements with. + */ + public function assertSession($name = NULL) { + return new WebAssert($this->getSession($name)); + } + + /** + * Prepare for a request to testing site. + * + * The testing site is protected via a SIMPLETEST_USER_AGENT cookie that + * is checked by drupal_valid_test_ua(). + * + * @see drupal_valid_test_ua() + */ + protected function prepareRequest() { + $session = $this->getSession(); + $session->setCookie('SIMPLETEST_USER_AGENT', drupal_generate_test_ua($this->databasePrefix)); + } + + /** + * Retrieves a Drupal path or an absolute path. + * + * @param string $path + * Drupal path or URL to load into internal browser + * @param array $options + * Options to be forwarded to the url generator. + * + * @return string + * The retrieved HTML string, also available as $this->getRawContent() + */ + protected function drupalGet($path, array $options = array()) { + $options['absolute'] = TRUE; + + // The URL generator service is not necessarily available yet; e.g., in + // interactive installer tests. + if ($this->container->has('url_generator')) { + $url = $this->container->get('url_generator')->generateFromPath($path, $options); + } + else { + $url = $this->getAbsoluteUrl($path); + } + $session = $this->getSession(); + + $this->prepareRequest(); + $session->visit($url); + $out = $session->getPage()->getContent(); + + // Ensure that any changes to variables in the other thread are picked up. + $this->refreshVariables(); + + return $out; + } + + /** + * Takes a path and returns an absolute path. + * + * @param string $path + * A path from the internal browser content. + * + * @return string + * The $path with $base_url prepended, if necessary. + */ + protected function getAbsoluteUrl($path) { + global $base_url, $base_path; + + $parts = parse_url($path); + if (empty($parts['host'])) { + // Ensure that we have a string (and no xpath object). + $path = (string) $path; + // Strip $base_path, if existent. + $length = strlen($base_path); + if (substr($path, 0, $length) === $base_path) { + $path = substr($path, $length); + } + // Ensure that we have an absolute path. + if ($path[0] !== '/') { + $path = '/' . $path; + } + // Finally, prepend the $base_url. + $path = $base_url . $path; + } + return $path; + } + + /** + * Create a user with a given set of permissions. + * + * @param array $permissions + * Array of permission names to assign to user. Note that the user always + * has the default permissions derived from the "authenticated users" role. + * @param string $name + * The user name. + * + * @return \Drupal\user\Entity\User|false + * A fully loaded user object with pass_raw property, or FALSE if account + * creation fails. + */ + protected function drupalCreateUser(array $permissions = array(), $name = NULL) { + // Create a role with the given permission set, if any. + $rid = FALSE; + if ($permissions) { + $rid = $this->drupalCreateRole($permissions); + if (!$rid) { + return FALSE; + } + } + + // Create a user assigned to that role. + $edit = array(); + $edit['name'] = !empty($name) ? $name : $this->randomName(); + $edit['mail'] = $edit['name'] . '@example.com'; + $edit['pass'] = user_password(); + $edit['status'] = 1; + if ($rid) { + $edit['roles'] = array($rid); + } + + $account = entity_create('user', $edit); + $account->save(); + + if (!$account->id()) { + return FALSE; + } + + // Add the raw password so that we can log in as this user. + $account->pass_raw = $edit['pass']; + return $account; + } + + /** + * Creates a role with specified permissions. + * + * @param array $permissions + * Array of permission names to assign to role. + * @param string $rid + * (optional) The role ID (machine name). Defaults to a random name. + * @param string $name + * (optional) The label for the role. Defaults to a random string. + * @param int $weight + * (optional) The weight for the role. Defaults NULL so that entity_create() + * sets the weight to maximum + 1. + * + * @return string + * Role ID of newly created role, or FALSE if role creation failed. + */ + protected function drupalCreateRole(array $permissions, $rid = NULL, $name = NULL, $weight = NULL) { + // Generate a random, lowercase machine name if none was passed. + if (!isset($rid)) { + $rid = strtolower($this->randomMachineName(8)); + } + // Generate a random label. + if (!isset($name)) { + // In the role UI role names are trimmed and random string can start or + // end with a space. + $name = trim($this->randomString(8)); + } + + // Check the all the permissions strings are valid. + if (!$this->checkPermissions($permissions)) { + return FALSE; + } + + // Create new role. + /* @var \Drupal\user\RoleInterface $role */ + $role = entity_create('user_role', array( + 'id' => $rid, + 'label' => $name, + )); + if (!is_null($weight)) { + $role->set('weight', $weight); + } + $result = $role->save(); + + $this->assertSame($result, SAVED_NEW, String::format('Created role ID @rid with name @name.', array( + '@name' => var_export($role->label(), TRUE), + '@rid' => var_export($role->id(), TRUE), + )), 'Role'); + + if ($result === SAVED_NEW) { + // Grant the specified permissions to the role, if any. + if (!empty($permissions)) { + user_role_grant_permissions($role->id(), $permissions); + $assigned_permissions = entity_load('user_role', $role->id())->getPermissions(); + $missing_permissions = array_diff($permissions, $assigned_permissions); + if ($missing_permissions) { + $this->fail(String::format('Failed to create permissions: @perms', array('@perms' => implode(', ', $missing_permissions))), 'Role'); + } + } + return $role->id(); + } + else { + return FALSE; + } + } + + /** + * Generates a unique random string containing letters and numbers. + * + * Do not use this method when testing unvalidated user input. Instead, use + * \Drupal\simpletest\TestBase::randomString(). + * + * @param int $length + * Length of random string to generate. + * + * @return string + * Randomly generated unique string. + * + * @see \Drupal\Component\Utility\Random::name() + */ + public function randomMachineName($length = 8) { + return $this->getRandomGenerator()->name($length, TRUE); + } + + /** + * Generates a pseudo-random string of ASCII characters of codes 32 to 126. + * + * Do not use this method when special characters are not possible (e.g., in + * machine or file names that have already been validated); instead, use + * \Drupal\simpletest\TestBase::randomMachineName(). If $length is greater + * than 2 the random string will include at least one ampersand ('&') + * character to ensure coverage for special characters and avoid the + * introduction of random test failures. + * + * @param int $length + * Length of random string to generate. + * + * @return string + * Pseudo-randomly generated unique string including special characters. + * + * @see \Drupal\Component\Utility\Random::string() + */ + public function randomString($length = 8) { + if ($length < 3) { + return $this->getRandomGenerator()->string($length, TRUE, array($this, 'randomStringValidate')); + } + + // To prevent the introduction of random test failures, ensure that the + // returned string contains a character that needs to be escaped in HTML by + // injecting an ampersand into it. + $replacement_pos = floor($length / 2); + // Remove 1 from the length to account for the ampersand character. + $string = $this->getRandomGenerator()->string($length - 1, TRUE, array($this, 'randomStringValidate')); + return substr_replace($string, '&', $replacement_pos, 0); + } + + /** + * Checks whether a given list of permission names is valid. + * + * @param array $permissions + * The permission names to check. + * + * @return bool + * TRUE if the permissions are valid, FALSE otherwise. + */ + protected function checkPermissions(array $permissions) { + $available = array_keys(\Drupal::service('user.permissions')->getPermissions()); + $valid = TRUE; + foreach ($permissions as $permission) { + if (!in_array($permission, $available)) { + $this->fail(String::format('Invalid permission %permission.', array('%permission' => $permission))); + $valid = FALSE; + } + } + return $valid; + } + + /** + * Log in a user with the internal browser. + * + * If a user is already logged in, then the current user is logged out before + * logging in the specified user. + * + * Please note that neither the current user nor the passed-in user object is + * populated with data of the logged in user. If you need full access to the + * user object after logging in, it must be updated manually. If you also need + * access to the plain-text password of the user (set by drupalCreateUser()), + * e.g. to log in the same user again, then it must be re-assigned manually. + * For example: + * @code + * // Create a user. + * $account = $this->drupalCreateUser(array()); + * $this->drupalLogin($account); + * // Load real user object. + * $pass_raw = $account->pass_raw; + * $account = user_load($account->id()); + * $account->pass_raw = $pass_raw; + * @endcode + * + * @param \Drupal\Core\Session\AccountInterface $account + * User object representing the user to log in. + * + * @see drupalCreateUser() + */ + protected function drupalLogin(AccountInterface $account) { + if ($this->loggedInUser) { + $this->drupalLogout(); + } + + $this->drupalGet('user'); + $this->submitForm(array( + 'name' => $account->getUsername(), + 'pass' => $account->pass_raw, + ), t('Log in')); + + // @see WebTestBase::drupalUserIsLoggedIn() + $account->session_id = $this->getSession()->getCookie(session_name()); + $this->assertTrue($this->drupalUserIsLoggedIn($account), String::format('User %name successfully logged in.', array('name' => $account->getUsername()))); + + $this->loggedInUser = $account; + $this->container->get('current_user')->setAccount($account); + } + + /** + * Logs a user out of the internal browser and confirms. + * + * Confirms logout by checking the login page. + */ + protected function drupalLogout() { + // Make a request to the logout page, and redirect to the user page, the + // idea being if you were properly logged out you should be seeing a login + // screen. + $assert_session = $this->assertSession(); + $this->drupalGet('user/logout', array('query' => array('destination' => 'user'))); + $assert_session->statusCodeEquals(200); + $assert_session->fieldExists('name'); + $assert_session->fieldExists('pass'); + + // @see BrowserTestBase::drupalUserIsLoggedIn() + unset($this->loggedInUser->session_id); + $this->loggedInUser = FALSE; + $this->container->get('current_user')->setAccount(new AnonymousUserSession()); + } + + /** + * Fill and submit a form. + * + * @param array $edit + * Field data in an associative array. Changes the current input fields + * (where possible) to the values indicated. + * + * A checkbox can be set to TRUE to be checked and should be set to FALSE to + * be unchecked. + * @param string $submit + * Value of the submit button whose click is to be emulated. For example, + * t('Save'). The processing of the request depends on this value. For + * example, a form may have one button with the value t('Save') and another + * button with the value t('Delete'), and execute different code depending + * on which one is clicked. + * @param string $form_html_id + * (optional) HTML ID of the form to be submitted. On some pages + * there are many identical forms, so just using the value of the submit + * button is not enough. For example: 'trigger-node-presave-assign-form'. + * Note that this is not the Drupal $form_id, but rather the HTML ID of the + * form, which is typically the same thing but with hyphens replacing the + * underscores. + */ + protected function submitForm($edit, $submit, $form_html_id = NULL) { + $assert_session = $this->assertSession(); + + // Get the form. + if (isset($form_html_id)) { + $form = $assert_session->elementExists('xpath', "//form[@id='" . $form_html_id . "']"); + $submit_button = $assert_session->buttonExists($submit, $form); + } + else { + $submit_button = $assert_session->buttonExists($submit); + $form = $assert_session->elementExists('xpath', './ancestor::form', $submit_button); + } + + // Edit the form values. + foreach ($edit as $name => $value) { + $field = $assert_session->fieldExists($name, $form); + $field->setValue($value); + } + + // Submit form. + $this->prepareRequest(); + $submit_button->press(); + + // Ensure that any changes to variables in the other thread are picked up. + $this->refreshVariables(); + } + + /** + * Helper function to get the options of select field. + * + * @param NodeElement|string $select + * Name, ID, or Label of select field to assert. + * @param Element $container + * (optional) Container element to check against. Defaults to current page. + * + * @return array + * Associative array of option keys and values. + */ + protected function getOptions($select, Element $container = NULL) { + if (is_string($select)) { + $select = $this->assertSession()->selectExists($select, $container); + } + $options = []; + /* @var \Behat\Mink\Element\NodeElement $option */ + foreach ($select->findAll('xpath', '//option') as $option) { + $label = $option->getText(); + $value = $option->getAttribute('value') ?: $label; + $options[$value] = $label; + } + return $options; + } + + /** + * {@inheritdoc} + */ + public function run(\PHPUnit_Framework_TestResult $result = NULL) { + if ($result === NULL) { + $result = $this->createResult(); + } + + parent::run($result); + return $result; + } + + /** + * Override to use Mink exceptions. + * + * @return mixed + * Either a test result or NULL. + * @throws \Exception When exception was thrown inside the test. + */ + protected function runTest() { + try { + return parent::runTest(); + } + catch (Exception $e) { + throw new \PHPUnit_Framework_AssertionFailedError($e->getMessage()); + } + } + + /** + * Generates a unique random string containing letters and numbers. + * + * Do not use this method when testing unvalidated user input. Instead, use + * \Drupal\simpletest\TestBase::randomString(). + * + * @param int $length + * Length of random string to generate. + * + * @return string + * Randomly generated unique string. + * + * @see \Drupal\Component\Utility\Random::name() + */ + public function randomName($length = 8) { + return $this->getRandomGenerator()->name($length, TRUE); + } + + /** + * Gets the random generator for the utility methods. + * + * @return \Drupal\Component\Utility\Random + * The random generator + */ + protected function getRandomGenerator() { + if (!is_object($this->randomGenerator)) { + $this->randomGenerator = new Random(); + } + return $this->randomGenerator; + } + + /** + * {@inheritdoc} + */ + public function installDrupal() { + // Define information about the user 1 account. + $this->root_user = new UserSession(array( + 'uid' => 1, + 'name' => 'admin', + 'mail' => 'admin@example.com', + 'pass_raw' => $this->randomName(), + )); + + // Some tests (SessionTest and SessionHttpsTest) need to examine whether the + // proper session cookies were set on a response. Because the child site + // uses the same session name as the test runner, it is necessary to make + // that available to test-methods. + $this->session_name = session_name(); + + // Get parameters for install_drupal() before removing global variables. + $parameters = $this->installParameters(); + + // Prepare installer settings that are not install_drupal() parameters. + // Copy and prepare an actual settings.php, so as to resemble a regular + // installation. + // Not using File API; a potential error must trigger a PHP warning. + $directory = DRUPAL_ROOT . '/' . $this->siteDirectory; + copy(DRUPAL_ROOT . '/sites/default/default.settings.php', $directory . '/settings.php'); + copy(DRUPAL_ROOT . '/sites/default/default.services.yml', $directory . '/services.yml'); + + // All file system paths are created by System module during installation. + // @see system_requirements() + // @see TestBase::prepareEnvironment() + $settings['settings']['file_public_path'] = (object) array( + 'value' => $this->public_files_directory, + 'required' => TRUE, + ); + $this->writeSettings($settings); + // Allow for test-specific overrides. + $settings_testing_file = DRUPAL_ROOT . '/' . $this->originalSite . '/settings.testing.php'; + if (file_exists($settings_testing_file)) { + // Copy the testing-specific settings.php overrides in place. + copy($settings_testing_file, $directory . '/settings.testing.php'); + // Add the name of the testing class to settings.php and include the + // testing specific overrides. + file_put_contents($directory . '/settings.php', "\n\$test_class = '" . get_class($this) . "';\n" . 'include DRUPAL_ROOT . \'/\' . $site_path . \'/settings.testing.php\';' . "\n", FILE_APPEND); + } + $settings_services_file = DRUPAL_ROOT . '/' . $this->originalSite . '/testing.services.yml'; + if (file_exists($settings_services_file)) { + // Copy the testing-specific service overrides in place. + copy($settings_services_file, $directory . '/services.yml'); + } + + // Since Drupal is bootstrapped already, install_begin_request() will not + // bootstrap into DRUPAL_BOOTSTRAP_CONFIGURATION (again). Hence, we have to + // reload the newly written custom settings.php manually. + Settings::initialize($directory, $this->classLoader); + + // Execute the non-interactive installer. + require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; + install_drupal($parameters); + + // Import new settings.php written by the installer. + Settings::initialize($directory, $this->classLoader); + foreach ($GLOBALS['config_directories'] as $type => $path) { + $this->configDirectories[$type] = $path; + } + + // After writing settings.php, the installer removes write permissions + // from the site directory. To allow drupal_generate_test_ua() to write + // a file containing the private key for drupal_valid_test_ua(), the site + // directory has to be writable. + // TestBase::restoreEnvironment() will delete the entire site directory. + // Not using File API; a potential error must trigger a PHP warning. + chmod($directory, 0777); + + $request = \Drupal::request(); + $this->kernel = DrupalKernel::createFromRequest($request, $this->classLoader, 'prod', TRUE); + $this->kernel->prepareLegacyRequest($request); + // Force the container to be built from scratch instead of loaded from the + // disk. This forces us to not accidently load the parent site. + $container = $this->kernel->rebuildContainer(); + + $config = $container->get('config.factory'); + + // Manually create and configure private and temporary files directories. + // While these could be preset/enforced in settings.php like the public + // files directory above, some tests expect them to be configurable in the + // UI. If declared in settings.php, they would no longer be configurable. + file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY); + file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY); + $config->get('system.file') + ->set('path.private', $this->private_files_directory) + ->set('path.temporary', $this->temp_files_directory) + ->save(); + + // Manually configure the test mail collector implementation to prevent + // tests from sending out emails and collect them in state instead. + // While this should be enforced via settings.php prior to installation, + // some tests expect to be able to test mail system implementations. + $config->get('system.mail') + ->set('interface.default', 'test_mail_collector') + ->save(); + + // By default, verbosely display all errors and disable all production + // environment optimizations for all tests to avoid needless overhead and + // ensure a sane default experience for test authors. + // @see https://drupal.org/node/2259167 + $config->get('system.logging') + ->set('error_level', 'verbose') + ->save(); + $config->get('system.performance') + ->set('css.preprocess', FALSE) + ->set('js.preprocess', FALSE) + ->save(); + + // Collect modules to install. + $class = get_class($this); + $modules = array(); + while ($class) { + if (property_exists($class, 'modules')) { + $modules = array_merge($modules, $class::$modules); + } + $class = get_parent_class($class); + } + if ($modules) { + $modules = array_unique($modules); + $success = $container->get('module_handler')->install($modules, TRUE); + $this->assertTrue($success, String::format('Enabled modules: %modules', array('%modules' => implode(', ', $modules)))); + $this->rebuildContainer(); + } + + // Reset/rebuild all data structures after enabling the modules, primarily + // to synchronize all data structures and caches between the test runner and + // the child site. + // Affects e.g. file_get_stream_wrappers(). + // @see \Drupal\Core\DrupalKernel::bootCode() + // @todo Test-specific setUp() methods may set up further fixtures; find a + // way to execute this after setUp() is done, or to eliminate it entirely. + $this->resetAll(); + $this->kernel->prepareLegacyRequest($request); + } + + /** + * Returns the parameters that will be used when Simpletest installs Drupal. + * + * @see install_drupal() + * @see install_state_defaults() + */ + protected function installParameters() { + $connection_info = Database::getConnectionInfo(); + $driver = $connection_info['default']['driver']; + $connection_info['default']['prefix'] = $connection_info['default']['prefix']['default']; + unset($connection_info['default']['driver']); + unset($connection_info['default']['namespace']); + unset($connection_info['default']['pdo']); + unset($connection_info['default']['init_commands']); + $parameters = array( + 'interactive' => FALSE, + 'parameters' => array( + 'profile' => $this->profile, + 'langcode' => 'en', + ), + 'forms' => array( + 'install_settings_form' => array( + 'driver' => $driver, + $driver => $connection_info['default'], + ), + 'install_configure_form' => array( + 'site_name' => 'Drupal', + 'site_mail' => 'simpletest@example.com', + 'account' => array( + 'name' => $this->root_user->name, + 'mail' => $this->root_user->getEmail(), + 'pass' => array( + 'pass1' => $this->root_user->pass_raw, + 'pass2' => $this->root_user->pass_raw, + ), + ), + // form_type_checkboxes_value() requires NULL instead of FALSE values + // for programmatic form submissions to disable a checkbox. + 'update_status_module' => array( + 1 => NULL, + 2 => NULL, + ), + ), + ), + ); + return $parameters; + } + + /** + * Generates a database prefix for running tests. + * + * The database prefix is used by prepareEnvironment() to setup a public files + * directory for the test to be run, which also contains the PHP error log, + * which is written to in case of a fatal error. Since that directory is based + * on the database prefix, all tests (even unit tests) need to have one, in + * order to access and read the error log. + * + * The generated database table prefix is used for the Drupal installation + * being performed for the test. It is also used as user agent HTTP header + * value by the cURL-based browser of DrupalWebTestCase, which is sent to the + * Drupal installation of the test. During early Drupal bootstrap, the user + * agent HTTP header is parsed, and if it matches, all database queries use + * the database table prefix that has been generated here. + * + * @see WebTestBase::curlInitialize() + * @see drupal_valid_test_ua() + * @see \Drupal\simpletest\TestBase::prepareEnvironment + */ + private function prepareDatabasePrefix() { + // Ensure that the generated test site directory does not exist already, + // which may happen with a large amount of concurrent threads and + // long-running tests. + do { + $suffix = mt_rand(100000, 999999); + $this->siteDirectory = 'sites/simpletest/' . $suffix; + $this->databasePrefix = 'simpletest' . $suffix; + } while (is_dir(DRUPAL_ROOT . '/' . $this->siteDirectory)); + } + + /** + * Changes the database connection to the prefixed one. + * + * @see TestBase::prepareEnvironment() + */ + private function changeDatabasePrefix() { + if (empty($this->databasePrefix)) { + $this->prepareDatabasePrefix(); + } + + // Clone the current connection and replace the current prefix. + $connection_info = Database::getConnectionInfo('default'); + Database::renameConnection('default', 'simpletest_original_default'); + foreach ($connection_info as $target => $value) { + // Replace the full table prefix definition to ensure that no table + // prefixes of the test runner leak into the test. + $connection_info[$target]['prefix'] = array( + 'default' => $value['prefix']['default'] . $this->databasePrefix, + ); + } + Database::addConnectionInfo('default', 'default', $connection_info['default']); + } + + /** + * Prepares the current environment for running the test. + * + * Backups various current environment variables and resets them, so they do + * not interfere with the Drupal site installation in which tests are executed + * and can be restored in TestBase::restoreEnvironment(). + * + * Also sets up new resources for the testing environment, such as the public + * filesystem and configuration directories. + * + * This method is private as it must only be called once by TestBase::run() + * (multiple invocations for the same test would have unpredictable + * consequences) and it must not be callable or overridable by test classes. + * + * @see TestBase::beforePrepareEnvironment() + */ + protected function prepareEnvironment() { + // Drupal kernel expects the working directory to be DRUPAL_ROOT. + chdir(DRUPAL_ROOT); + + // Bootstrap Drupal so we can use Drupal's built in functions. + $this->classLoader = require __DIR__ . '/../../../vendor/autoload.php'; + $request = Request::createFromGlobals(); + $kernel = TestRunnerKernel::createFromRequest($request, $this->classLoader); + $kernel->prepareLegacyRequest($request); + $this->prepareDatabasePrefix(); + + // Create test directory ahead of installation so fatal errors and debug + // information can be logged during installation process. + file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + + // Prepare filesystem directory paths. + $this->public_files_directory = $this->siteDirectory . '/files'; + $this->private_files_directory = $this->siteDirectory . '/private'; + $this->temp_files_directory = $this->siteDirectory . '/temp'; + $this->translation_files_directory = $this->siteDirectory . '/translations'; + + // Ensure the configImporter is refreshed for each test. + $this->configImporter = NULL; + + // Unregister all custom stream wrappers of the parent site. + // Availability of Drupal stream wrappers varies by test base class: + // - UnitTestBase operates in a completely empty environment. + // - KernelTestBase supports and maintains stream wrappers in a custom + // way. + // - WebTestBase re-initializes Drupal stream wrappers after installation. + // The original stream wrappers are restored after the test run. + // @see TestBase::restoreEnvironment() + $wrappers = file_get_stream_wrappers(); + foreach ($wrappers as $scheme => $info) { + stream_wrapper_unregister($scheme); + } + + // Reset statics. + drupal_static_reset(); + + // Ensure there is no service container. + $this->container = NULL; + \Drupal::setContainer(NULL); + + // Unset globals. + unset($GLOBALS['config_directories']); + unset($GLOBALS['config']); + unset($GLOBALS['conf']); + + // Log fatal errors. + ini_set('log_errors', 1); + ini_set('error_log', DRUPAL_ROOT . '/' . $this->siteDirectory . '/error.log'); + + // Change the database prefix. + $this->changeDatabasePrefix(); + + // After preparing the environment and changing the database prefix, we are + // in a valid test environment. + drupal_valid_test_ua($this->databasePrefix); + conf_path(FALSE, TRUE); + + // Reset settings. + new Settings(array( + // For performance, simply use the database prefix as hash salt. + 'hash_salt' => $this->databasePrefix, + )); + + drupal_set_time_limit($this->timeLimit); + } + + /** + * Returns the database connection to the site running Simpletest. + * + * @return \Drupal\Core\Database\Connection + * The database connection to use for inserting assertions. + */ + public static function getDatabaseConnection() { + // Check whether there is a test runner connection. + // @see run-tests.sh + // @todo Convert Simpletest UI runner to create + use this connection, too. + try { + $connection = Database::getConnection('default', 'test-runner'); + } + catch (ConnectionNotDefinedException $e) { + // Check whether there is a backup of the original default connection. + // @see TestBase::prepareEnvironment() + try { + $connection = Database::getConnection('default', 'simpletest_original_default'); + } + catch (ConnectionNotDefinedException $e) { + error_log('8'); + // If TestBase::prepareEnvironment() or TestBase::restoreEnvironment() + // failed, the test-specific database connection does not exist + // yet/anymore, so fall back to the default of the (UI) test runner. + $connection = Database::getConnection('default', 'default'); + } + } + return $connection; + } + + /** + * Rewrites the settings.php file of the test site. + * + * @param array $settings + * An array of settings to write out, in the format expected by + * drupal_rewrite_settings(). + * + * @see drupal_rewrite_settings() + */ + protected function writeSettings(array $settings) { + include_once DRUPAL_ROOT . '/core/includes/install.inc'; + $filename = $this->siteDirectory . '/settings.php'; + + error_log($filename); + + // system_requirements() removes write permissions from settings.php + // whenever it is invoked. + // Not using File API; a potential error must trigger a PHP warning. + chmod($filename, 0666); + drupal_rewrite_settings($settings, $filename); + } + + /** + * Rebuilds \Drupal::getContainer(). + * + * Use this to build a new kernel and service container. For example, when the + * list of enabled modules is changed via the internal browser, in which case + * the test process still contains an old kernel and service container with an + * old module list. + * + * @see BrowserTestBase::prepareEnvironment() + * @see BrowserTestBase::restoreEnvironment() + * + * @todo Fix https://www.drupal.org/node/2021959 so that module enable/disable + * changes are immediately reflected in \Drupal::getContainer(). Until then, + * tests can invoke this workaround when requiring services from newly + * enabled modules to be immediately available in the same request. + */ + protected function rebuildContainer() { + // Maintain the current global request object. + $request = \Drupal::request(); + // Rebuild the kernel and bring it back to a fully bootstrapped state. + $this->container = $this->kernel->rebuildContainer(); + + // Make sure the url generator has a request object, otherwise calls to + // $this->drupalGet() will fail. + $this->prepareRequestForGenerator(); + } + + /** + * Creates a mock request and sets it on the generator. + * + * This is used to manipulate how the generator generates paths during tests. + * It also ensures that calls to $this->drupalGet() will work when running + * from run-tests.sh because the url generator no longer looks at the global + * variables that are set there but relies on getting this information from a + * request object. + * + * @param bool $clean_urls + * Whether to mock the request using clean urls. + * @param array $override_server_vars + * An array of server variables to override. + * + * @return Request + * The mocked request object. + */ + protected function prepareRequestForGenerator($clean_urls = TRUE, $override_server_vars = array()) { + $request = Request::createFromGlobals(); + $server = $request->server->all(); + if (basename($server['SCRIPT_FILENAME']) != basename($server['SCRIPT_NAME'])) { + // We need this for when the test is executed by run-tests.sh. + // @todo Remove this once run-tests.sh has been converted to use a Request + // object. + $cwd = getcwd(); + $server['SCRIPT_FILENAME'] = $cwd . '/' . basename($server['SCRIPT_NAME']); + $base_path = rtrim($server['REQUEST_URI'], '/'); + } + else { + $base_path = $request->getBasePath(); + } + if ($clean_urls) { + $request_path = $base_path ? $base_path . '/user' : 'user'; + } + else { + $request_path = $base_path ? $base_path . '/index.php/user' : '/index.php/user'; + } + $server = array_merge($server, $override_server_vars); + + $request = Request::create($request_path, 'GET', array(), array(), array(), $server); + $this->container->get('request_stack')->push($request); + + // The request context is normally set by the router_listener from within + // its KernelEvents::REQUEST listener. In the simpletest parent site this + // event is not fired, therefore it is necessary to updated the request + // context manually here. + $this->container->get('router.request_context')->fromRequest($request); + + return $request; + } + + /** + * Resets all data structures after having enabled new modules. + * + * This method is called by \Drupal\simpletest\WebTestBase::setUp() after + * enabling the requested modules. It must be called again when additional + * modules are enabled later. + */ + protected function resetAll() { + // Clear all database and static caches and rebuild data structures. + drupal_flush_all_caches(); + $this->container = \Drupal::getContainer(); + + // Reset static variables and reload permissions. + $this->refreshVariables(); + } + + /** + * Refreshes in-memory configuration and state information. + * + * Useful after a page request is made that changes configuration or state in + * a different thread. + * + * In other words calling a settings page with $this->drupalPostForm() with a + * changed value would update configuration to reflect that change, but in the + * thread that made the call (thread running the test) the changed values + * would not be picked up. + * + * This method clears the cache and loads a fresh copy. + */ + protected function refreshVariables() { + // Clear the tag cache. + // @todo Replace drupal_static() usage within classes and provide a + // proper interface for invoking reset() on a cache backend: + // https://www.drupal.org/node/2311945. + drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache'); + drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::deletedTags'); + drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::invalidatedTags'); + foreach (Cache::getBins() as $backend) { + if (is_callable(array($backend, 'reset'))) { + $backend->reset(); + } + } + + $this->container->get('config.factory')->reset(); + $this->container->get('state')->resetCache(); + } + + /** + * Returns whether a given user account is logged in. + * + * @param \Drupal\user\UserInterface $account + * The user account object to check. + * + * @return bool + * Return TRUE if the user is logged in, FALSE otherwise. + */ + protected function drupalUserIsLoggedIn($account) { + if (!isset($account->session_id)) { + return FALSE; + } + // The session ID is hashed before being stored in the database. + // @see \Drupal\Core\Session\SessionHandler::read() + return (bool) db_query("SELECT sid FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array(':sid' => Crypt::hashBase64($account->session_id)))->fetchField(); + } + +} diff --git a/core/modules/simpletest/src/Form/SimpletestTestForm.php b/core/modules/simpletest/src/Form/SimpletestTestForm.php index b55de4c..44c3530 100644 --- a/core/modules/simpletest/src/Form/SimpletestTestForm.php +++ b/core/modules/simpletest/src/Form/SimpletestTestForm.php @@ -186,6 +186,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + global $base_url; // Test discovery does not run upon form submission. simpletest_classloader_register(); @@ -216,6 +217,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { } } if (!empty($tests_list)) { + putenv('SIMPLETEST_BASE_URL=' . $base_url); $test_id = simpletest_run_tests($tests_list, 'drupal'); $form_state->setRedirect( 'simpletest.result_form', diff --git a/core/modules/simpletest/src/WebAssert.php b/core/modules/simpletest/src/WebAssert.php new file mode 100644 index 0000000..c5b0a71 --- /dev/null +++ b/core/modules/simpletest/src/WebAssert.php @@ -0,0 +1,70 @@ +session->getPage(); + $node = $container->findButton($button); + + if (NULL === $node) { + throw new ElementNotFoundException($this->session, 'button', 'id|name|label|value', $button); + } + + return $node; + } + + /** + * Checks that specific field exists on the current page. + * + * @param string $select + * One of id|name|label|value for the select field. + * @param \Behat\Mink\Element\TraversableElement $container + * The document to check against. + * + * @return \Behat\Mink\Element\NodeElement + * The matching element + * + * @throws \Behat\Mink\Exception\ElementNotFoundException + * When the element doesn't exist. + */ + public function selectExists($select, TraversableElement $container = NULL) { + $container = $container ?: $this->session->getPage(); + $node = $container->find('named', array( + 'select', + $this->session->getSelectorsHandler()->xpathLiteral($select), + )); + + if (NULL === $node) { + throw new ElementNotFoundException($this->session, 'select', 'id|name|label|value', $select); + } + + return $node; + } +} diff --git a/core/modules/simpletest/tests/simpletest_test/config/install/simpletest_test.settings.yml b/core/modules/simpletest/tests/simpletest_test/config/install/simpletest_test.settings.yml new file mode 100644 index 0000000..c2e2313 --- /dev/null +++ b/core/modules/simpletest/tests/simpletest_test/config/install/simpletest_test.settings.yml @@ -0,0 +1 @@ +name: '' diff --git a/core/modules/simpletest/tests/simpletest_test/config/schema/simpletest_test.schema.yml b/core/modules/simpletest/tests/simpletest_test/config/schema/simpletest_test.schema.yml new file mode 100644 index 0000000..6d91dfa --- /dev/null +++ b/core/modules/simpletest/tests/simpletest_test/config/schema/simpletest_test.schema.yml @@ -0,0 +1,9 @@ +# Schema for the configuration files of the Simpletest test module. + +simpletest_test.settings: + type: mapping + label: 'Testing the testers' + mapping: + name: + type: string + label: 'A test config value' diff --git a/core/modules/simpletest/tests/simpletest_test/simpletest_test.info.yml b/core/modules/simpletest/tests/simpletest_test/simpletest_test.info.yml new file mode 100644 index 0000000..15241d1 --- /dev/null +++ b/core/modules/simpletest/tests/simpletest_test/simpletest_test.info.yml @@ -0,0 +1,6 @@ +name: Testing test +type: module +description: 'Provides dummy classes for use by SimpleTest tests.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/simpletest/tests/simpletest_test/simpletest_test.module b/core/modules/simpletest/tests/simpletest_test/simpletest_test.module new file mode 100644 index 0000000..651884e --- /dev/null +++ b/core/modules/simpletest/tests/simpletest_test/simpletest_test.module @@ -0,0 +1,6 @@ + $this->t('Hello Amsterdam'), + ); + } + +} diff --git a/core/modules/simpletest/tests/simpletest_test/src/Form/ExampleForm.php b/core/modules/simpletest/tests/simpletest_test/src/Form/ExampleForm.php new file mode 100644 index 0000000..bb6f95d --- /dev/null +++ b/core/modules/simpletest/tests/simpletest_test/src/Form/ExampleForm.php @@ -0,0 +1,46 @@ + 'textfield', + '#default_value' => \Drupal::config('simpletest_test.settings')->get('name'), + ]; + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // Normally this config object would be injected but this is a only a test + // so...who cares? + \Drupal::config('simpletest_test.settings')->set('name', $form_state->getValue('name'))->save(); + parent::submitForm($form, $form_state); + } + +} diff --git a/core/modules/simpletest/tests/src/Functional/BrowserTestBaseTest.php b/core/modules/simpletest/tests/src/Functional/BrowserTestBaseTest.php new file mode 100644 index 0000000..53371b0 --- /dev/null +++ b/core/modules/simpletest/tests/src/Functional/BrowserTestBaseTest.php @@ -0,0 +1,62 @@ +drupalCreateUser(array('simpletest_test access tests')); + $this->drupalLogin($account); + + // Visit a Drupal page that requires login. + $this->drupalGet('/simpletest/hello'); + $this->assertSession()->statusCodeEquals(200); + + // Test page contains some text. + $this->assertSession()->pageTextContains('Hello Amsterdam'); + } + + /** + * Tests basic form functionality. + */ + public function testForm() { + + // Ensure the proper response code for a _form route. + $this->drupalGet('simpletest/example-form'); + $this->assertSession()->statusCodeEquals(200); + + // Ensure the form and text field exist. + $this->assertSession()->elementExists('css', 'form#simpletest-test-example-form'); + $this->assertSession()->fieldExists('name'); + + $edit = ['name' => 'Foobaz']; + $this->submitForm($edit, 'Save configuration', 'simpletest-test-example-form'); + + $this->drupalGet('/simpletest/example-form'); + $this->assertSession()->fieldValueEquals('name', 'Foobaz'); + } + +} diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist index aa4acfd..78f2f0e 100644 --- a/core/phpunit.xml.dist +++ b/core/phpunit.xml.dist @@ -6,9 +6,10 @@ + - + ./tests ./modules/*/tests ../modules @@ -20,6 +21,15 @@ ./modules/config/tests/config_test/src + + ./modules/*/tests/src/Functional + + ./vendor + + ./drush/tests + + ./modules/config/tests/config_test/src + @@ -32,3 +42,4 @@ + diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index c552472..fe9cee1 100644 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -337,6 +337,17 @@ function simpletest_script_init() { } } + if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') { + $base_url = 'https://'; + } + else { + $base_url = 'http://'; + } + $base_url .= $host; + if ($path !== '') { + $base_url .= $path; + } + putenv('SIMPLETEST_BASE_URL=' . $base_url); $_SERVER['HTTP_HOST'] = $host; $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['SERVER_ADDR'] = '127.0.0.1'; @@ -527,6 +538,12 @@ function simpletest_script_execute_batch($test_classes) { continue; } + // Process phpunit web-tests next as they are faster. + if (is_subclass_of($test_class, 'Drupal\simpletest\BrowserTestBase')) { + simpletest_script_run_phpunit($test_id, $test_class); + continue; + } + // Fork a child process. $command = simpletest_script_command($test_id, $test_class); $process = proc_open($command, array(), $pipes, NULL, NULL, array('bypass_shell' => TRUE)); diff --git a/core/vendor/behat/mink-browserkit-driver/.gitignore b/core/vendor/behat/mink-browserkit-driver/.gitignore new file mode 100644 index 0000000..3ccd9ee --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/.gitignore @@ -0,0 +1,3 @@ +vendor +composer.lock +/phpunit.xml diff --git a/core/vendor/behat/mink-browserkit-driver/.travis.yml b/core/vendor/behat/mink-browserkit-driver/.travis.yml new file mode 100644 index 0000000..838fc4e --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/.travis.yml @@ -0,0 +1,20 @@ +language: php + +php: [5.3, 5.4, 5.5, 5.6, hhvm] + +matrix: + include: + - php: 5.5 + env: SYMFONY_VERSION='2.3.*' + - php: 5.5 + env: SYMFONY_VERSION='2.5.*@dev' + +before_script: + - sh -c 'if [ "$SYMFONY_VERSION" != "" ]; then composer require -n --no-update symfony/symfony=$SYMFONY_VERSION; fi;' + - composer install -n --prefer-source + +script: phpunit -v --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/core/vendor/behat/mink-browserkit-driver/CHANGELOG.md b/core/vendor/behat/mink-browserkit-driver/CHANGELOG.md new file mode 100644 index 0000000..774663f --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/CHANGELOG.md @@ -0,0 +1,33 @@ +1.2.0 / 2014-09-26 +================== + +BC break: + +* Changed the behavior of `getValue` for checkboxes according to the BC break in Mink 1.6 + +New features: + +* Implemented `getOuterHtml` +* Added the support of manipulating forms without submit buttons +* Added support of any request headers instead of supporting only a few hardcoded ones +* Added support of any BrowserKit client using `filterResponse` when using BrowserKit 2.3+ +* Added the support of reset buttons +* Implemented `submitForm` +* Implemented `isSelected` + +Bug fixes: + +* Fixed the support of options without value attribute in `isSelected` and `getValue` +* Added the support of radio buttons in `isChecked` +* Fixed the submission of empty textarea fields +* Refactored the handling of request headers to ensure they are reset when resetting the driver +* Fixed the handling of buttons to submit only for submit buttons rather than all buttons +* Fixed the code to throw exceptions rather than triggering a fatal error for invalid usages of the driver +* Fixed the removal of cookies +* Fixed the submission of form fields with same name and without id +* Fixed `getAttribute` to return `null` for missing attributes rather than an empty string + +Testing: + +* Updated the testsuite to use the new Mink 1.6 driver testsuite +* Added testing on HHVM diff --git a/core/vendor/behat/mink-browserkit-driver/LICENSE b/core/vendor/behat/mink-browserkit-driver/LICENSE new file mode 100644 index 0000000..3365ae6 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012-2013 Konstantin Kudryashov + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/vendor/behat/mink-browserkit-driver/README.md b/core/vendor/behat/mink-browserkit-driver/README.md new file mode 100755 index 0000000..fc36e41 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/README.md @@ -0,0 +1,54 @@ +Mink BrowserKit Driver +====================== + +[![Latest Stable Version](https://poser.pugx.org/behat/mink-browserkit-driver/v/stable.png)](https://packagist.org/packages/behat/mink-browserkit-driver) +[![Latest Unstable Version](https://poser.pugx.org/behat/mink-browserkit-driver/v/unstable.svg)](https://packagist.org/packages/behat/mink-browserkit-driver) +[![Total Downloads](https://poser.pugx.org/behat/mink-browserkit-driver/downloads.png)](https://packagist.org/packages/behat/mink-browserkit-driver) +[![Build Status](https://travis-ci.org/Behat/MinkBrowserKitDriver.svg?branch=master)](https://travis-ci.org/Behat/MinkBrowserKitDriver) +[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Behat/MinkBrowserKitDriver/badges/quality-score.png?s=0443d284940e099ea560eb39b6b2fcdc5d4e7f29)](https://scrutinizer-ci.com/g/Behat/MinkBrowserKitDriver/) +[![Code Coverage](https://scrutinizer-ci.com/g/Behat/MinkBrowserKitDriver/badges/coverage.png?s=48960c4495488ab0b7d310b62322f017497f5bfa)](https://scrutinizer-ci.com/g/Behat/MinkBrowserKitDriver/) +[![License](https://poser.pugx.org/behat/mink-browserkit-driver/license.svg)](https://packagist.org/packages/behat/mink-browserkit-driver) + +Usage Example +------------- + +``` php + new Session(new BrowserKitDriver(new Client($app))), +)); + +$mink->getSession('silex')->getPage()->findLink('Chat')->click(); +``` + +Installation +------------ + +``` json +{ + "require": { + "behat/mink": "~1.5", + "behat/mink-browserkit-driver": "~1.1" + } +} +``` + +``` bash +$> curl -sS https://getcomposer.org/installer | php +$> php composer.phar install +``` + +Maintainers +----------- + +* Konstantin Kudryashov [everzet](http://github.com/everzet) +* Other [awesome developers](https://github.com/Behat/MinkBrowserKitDriver/graphs/contributors) diff --git a/core/vendor/behat/mink-browserkit-driver/composer.json b/core/vendor/behat/mink-browserkit-driver/composer.json new file mode 100644 index 0000000..c0c5df9 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/composer.json @@ -0,0 +1,45 @@ +{ + "name": "behat/mink-browserkit-driver", + "description": "Symfony2 BrowserKit driver for Mink framework", + "keywords": ["Symfony2", "Mink", "testing", "browser"], + "homepage": "http://mink.behat.org/", + "type": "mink-driver", + "license": "MIT", + + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + + "require": { + "php": ">=5.3.1", + "behat/mink": "~1.6@dev", + "symfony/browser-kit": "~2.0", + "symfony/dom-crawler": "~2.0" + }, + + "require-dev": { + "silex/silex": "~1.2" + }, + + "autoload": { + "psr-0": { + "Behat\\Mink\\Driver": "src/" + } + }, + + "autoload-dev": { + "psr-4": { + "Behat\\Mink\\Tests\\Driver\\": "tests" + } + }, + + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + } +} diff --git a/core/vendor/behat/mink-browserkit-driver/phpunit.xml.dist b/core/vendor/behat/mink-browserkit-driver/phpunit.xml.dist new file mode 100644 index 0000000..4f9b932 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + + + + + + tests + vendor/behat/mink/driver-testsuite/tests/Basic + vendor/behat/mink/driver-testsuite/tests/Form + + + + + + ./src/Behat/Mink/Driver + + + diff --git a/core/vendor/behat/mink-browserkit-driver/src/Behat/Mink/Driver/BrowserKitDriver.php b/core/vendor/behat/mink-browserkit-driver/src/Behat/Mink/Driver/BrowserKitDriver.php new file mode 100644 index 0000000..28b99dc --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/src/Behat/Mink/Driver/BrowserKitDriver.php @@ -0,0 +1,949 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Driver; + +use Behat\Mink\Element\NodeElement; +use Behat\Mink\Exception\DriverException; +use Behat\Mink\Exception\UnsupportedDriverActionException; +use Behat\Mink\Session; +use Symfony\Component\BrowserKit\Client; +use Symfony\Component\BrowserKit\Cookie; +use Symfony\Component\BrowserKit\Request; +use Symfony\Component\BrowserKit\Response; +use Symfony\Component\DomCrawler\Crawler; +use Symfony\Component\DomCrawler\Field\ChoiceFormField; +use Symfony\Component\DomCrawler\Field\FileFormField; +use Symfony\Component\DomCrawler\Field\FormField; +use Symfony\Component\DomCrawler\Field\InputFormField; +use Symfony\Component\DomCrawler\Field\TextareaFormField; +use Symfony\Component\DomCrawler\Form; +use Symfony\Component\HttpFoundation\Request as HttpFoundationRequest; +use Symfony\Component\HttpFoundation\Response as HttpFoundationResponse; +use Symfony\Component\HttpKernel\Client as HttpKernelClient; + +/** + * Symfony2 BrowserKit driver. + * + * @author Konstantin Kudryashov + */ +class BrowserKitDriver extends CoreDriver +{ + private $session; + private $client; + + /** + * @var Form[] + */ + private $forms = array(); + private $serverParameters = array(); + private $started = false; + private $removeScriptFromUrl = false; + private $removeHostFromUrl = false; + + /** + * Initializes BrowserKit driver. + * + * @param Client $client BrowserKit client instance + * @param string|null $baseUrl Base URL for HttpKernel clients + */ + public function __construct(Client $client, $baseUrl = null) + { + $this->client = $client; + $this->client->followRedirects(true); + + if ($baseUrl !== null && $client instanceof HttpKernelClient) { + $client->setServerParameter('SCRIPT_FILENAME', parse_url($baseUrl, PHP_URL_PATH)); + } + } + + /** + * Returns BrowserKit HTTP client instance. + * + * @return Client + */ + public function getClient() + { + return $this->client; + } + + /** + * {@inheritdoc} + */ + public function setSession(Session $session) + { + $this->session = $session; + } + + /** + * Tells driver to remove hostname from URL. + * + * @param Boolean $remove + * + * @deprecated Deprecated as of 1.2, to be removed in 2.0. Pass the base url in the constructor instead. + */ + public function setRemoveHostFromUrl($remove = true) + { + trigger_error( + 'setRemoveHostFromUrl() is deprecated as of 1.2 and will be removed in 2.0. Pass the base url in the constructor instead.', + E_USER_DEPRECATED + ); + $this->removeHostFromUrl = (bool) $remove; + } + + /** + * Tells driver to remove script name from URL. + * + * @param Boolean $remove + * + * @deprecated Deprecated as of 1.2, to be removed in 2.0. Pass the base url in the constructor instead. + */ + public function setRemoveScriptFromUrl($remove = true) + { + trigger_error( + 'setRemoveScriptFromUrl() is deprecated as of 1.2 and will be removed in 2.0. Pass the base url in the constructor instead.', + E_USER_DEPRECATED + ); + $this->removeScriptFromUrl = (bool) $remove; + } + + /** + * {@inheritdoc} + */ + public function start() + { + $this->started = true; + } + + /** + * {@inheritdoc} + */ + public function isStarted() + { + return $this->started; + } + + /** + * {@inheritdoc} + */ + public function stop() + { + $this->reset(); + $this->started = false; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + // Restarting the client resets the cookies and the history + $this->client->restart(); + $this->forms = array(); + $this->serverParameters = array(); + } + + /** + * {@inheritdoc} + */ + public function visit($url) + { + $this->client->request('GET', $this->prepareUrl($url), array(), array(), $this->serverParameters); + $this->forms = array(); + } + + /** + * {@inheritdoc} + */ + public function getCurrentUrl() + { + if (method_exists($this->client, 'getInternalRequest')) { + $request = $this->client->getInternalRequest(); + } else { + // BC layer for BrowserKit 2.2.x and older + $request = $this->client->getRequest(); + + if (null !== $request && !$request instanceof Request && !$request instanceof HttpFoundationRequest) { + throw new DriverException(sprintf( + 'The BrowserKit client returned an unsupported request implementation: %s. Please upgrade your BrowserKit package to 2.3 or newer.', + get_class($request) + )); + } + } + + if ($request === null) { + throw new DriverException('Unable to access the request before visiting a page'); + } + + return $request->getUri(); + } + + /** + * {@inheritdoc} + */ + public function reload() + { + $this->client->reload(); + $this->forms = array(); + } + + /** + * {@inheritdoc} + */ + public function forward() + { + $this->client->forward(); + $this->forms = array(); + } + + /** + * {@inheritdoc} + */ + public function back() + { + $this->client->back(); + $this->forms = array(); + } + + /** + * {@inheritdoc} + */ + public function setBasicAuth($user, $password) + { + if (false === $user) { + unset($this->serverParameters['PHP_AUTH_USER'], $this->serverParameters['PHP_AUTH_PW']); + + return; + } + + $this->serverParameters['PHP_AUTH_USER'] = $user; + $this->serverParameters['PHP_AUTH_PW'] = $password; + } + + /** + * {@inheritdoc} + */ + public function setRequestHeader($name, $value) + { + $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true); + $name = str_replace('-', '_', strtoupper($name)); + + // CONTENT_* are not prefixed with HTTP_ in PHP when building $_SERVER + if (!isset($contentHeaders[$name])) { + $name = 'HTTP_' . $name; + } + + $this->serverParameters[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function getResponseHeaders() + { + return $this->getResponse()->getHeaders(); + } + + /** + * {@inheritdoc} + */ + public function setCookie($name, $value = null) + { + if (null === $value) { + $this->deleteCookie($name); + + return; + } + + $jar = $this->client->getCookieJar(); + $jar->set(new Cookie($name, $value)); + } + + /** + * Deletes a cookie by name. + * + * @param string $name Cookie name. + */ + private function deleteCookie($name) + { + $path = $this->getCookiePath(); + $jar = $this->client->getCookieJar(); + + do { + if (null !== $jar->get($name, $path)) { + $jar->expire($name, $path); + } + + $path = preg_replace('/.$/', '', $path); + } while ($path); + } + + /** + * Returns current cookie path. + * + * @return string + */ + private function getCookiePath() + { + $path = dirname(parse_url($this->getCurrentUrl(), PHP_URL_PATH)); + + if ('\\' === DIRECTORY_SEPARATOR) { + $path = str_replace('\\', '/', $path); + } + + return $path; + } + + /** + * {@inheritdoc} + */ + public function getCookie($name) + { + // Note that the following doesn't work well because + // Symfony\Component\BrowserKit\CookieJar stores cookies by name, + // path, AND domain and if you don't fill them all in correctly then + // you won't get the value that you're expecting. + // + // $jar = $this->client->getCookieJar(); + // + // if (null !== $cookie = $jar->get($name)) { + // return $cookie->getValue(); + // } + + $allValues = $this->client->getCookieJar()->allValues($this->getCurrentUrl()); + + if (isset($allValues[$name])) { + return $allValues[$name]; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getStatusCode() + { + return $this->getResponse()->getStatus(); + } + + /** + * {@inheritdoc} + */ + public function getContent() + { + return $this->getResponse()->getContent(); + } + + /** + * {@inheritdoc} + */ + public function find($xpath) + { + $nodes = $this->getCrawler()->filterXPath($xpath); + + $elements = array(); + foreach ($nodes as $i => $node) { + $elements[] = new NodeElement(sprintf('(%s)[%d]', $xpath, $i + 1), $this->session); + } + + return $elements; + } + + /** + * {@inheritdoc} + */ + public function getTagName($xpath) + { + return $this->getCrawlerNode($this->getFilteredCrawler($xpath))->nodeName; + } + + /** + * {@inheritdoc} + */ + public function getText($xpath) + { + $text = $this->getFilteredCrawler($xpath)->text(); + $text = str_replace("\n", ' ', $text); + $text = preg_replace('/ {2,}/', ' ', $text); + + return trim($text); + } + + /** + * {@inheritdoc} + */ + public function getHtml($xpath) + { + // cut the tag itself (making innerHTML out of outerHTML) + return preg_replace('/^\<[^\>]+\>|\<[^\>]+\>$/', '', $this->getOuterHtml($xpath)); + } + + /** + * {@inheritdoc} + */ + public function getOuterHtml($xpath) + { + $node = $this->getCrawlerNode($this->getFilteredCrawler($xpath)); + + return $node->ownerDocument->saveXML($node); + } + + /** + * {@inheritdoc} + */ + public function getAttribute($xpath, $name) + { + $node = $this->getFilteredCrawler($xpath); + + if ($this->getCrawlerNode($node)->hasAttribute($name)) { + return $node->attr($name); + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getValue($xpath) + { + if (in_array($this->getAttribute($xpath, 'type'), array('submit', 'image', 'button'))) { + return $this->getAttribute($xpath, 'value'); + } + + $node = $this->getCrawlerNode($this->getFilteredCrawler($xpath)); + + if ('option' === $node->tagName) { + return $this->getOptionValue($node); + } + + try { + $field = $this->getFormField($xpath); + } catch (\InvalidArgumentException $e) { + return $this->getAttribute($xpath, 'value'); + } + + return $field->getValue(); + } + + /** + * {@inheritdoc} + */ + public function setValue($xpath, $value) + { + $this->getFormField($xpath)->setValue($value); + } + + /** + * {@inheritdoc} + */ + public function check($xpath) + { + $this->getCheckboxField($xpath)->tick(); + } + + /** + * {@inheritdoc} + */ + public function uncheck($xpath) + { + $this->getCheckboxField($xpath)->untick(); + } + + /** + * {@inheritdoc} + */ + public function selectOption($xpath, $value, $multiple = false) + { + $field = $this->getFormField($xpath); + + if (!$field instanceof ChoiceFormField) { + throw new DriverException(sprintf('Impossible to select an option on the element with XPath "%s" as it is not a select or radio input', $xpath)); + } + + if ($multiple) { + $oldValue = (array) $field->getValue(); + $oldValue[] = $value; + $value = $oldValue; + } + + $field->select($value); + } + + /** + * {@inheritdoc} + */ + public function isSelected($xpath) + { + $optionValue = $this->getOptionValue($this->getCrawlerNode($this->getFilteredCrawler($xpath))); + $selectField = $this->getFormField('(' . $xpath . ')/ancestor-or-self::*[local-name()="select"]'); + $selectValue = $selectField->getValue(); + + return is_array($selectValue) ? in_array($optionValue, $selectValue) : $optionValue == $selectValue; + } + + /** + * {@inheritdoc} + */ + public function click($xpath) + { + $node = $this->getFilteredCrawler($xpath); + $crawlerNode = $this->getCrawlerNode($node); + $tagName = $crawlerNode->nodeName; + + if ('a' === $tagName) { + $this->client->click($node->link()); + $this->forms = array(); + } elseif ($this->canSubmitForm($crawlerNode)) { + $this->submit($node->form()); + } elseif ($this->canResetForm($crawlerNode)) { + $this->resetForm($crawlerNode); + } else { + $message = sprintf('%%s supports clicking on links and buttons only. But "%s" provided', $tagName); + + throw new UnsupportedDriverActionException($message, $this); + } + } + + /** + * {@inheritdoc} + */ + public function isChecked($xpath) + { + $field = $this->getFormField($xpath); + + if (!$field instanceof ChoiceFormField || 'select' === $field->getType()) { + throw new DriverException(sprintf('Impossible to get the checked state of the element with XPath "%s" as it is not a checkbox or radio input', $xpath)); + } + + if ('checkbox' === $field->getType()) { + return $field->hasValue(); + } + + $radio = $this->getCrawlerNode($this->getFilteredCrawler($xpath)); + + return $radio->getAttribute('value') === $field->getValue(); + } + + /** + * {@inheritdoc} + */ + public function attachFile($xpath, $path) + { + $field = $this->getFormField($xpath); + + if (!$field instanceof FileFormField) { + throw new DriverException(sprintf('Impossible to attach a file on the element with XPath "%s" as it is not a file input', $xpath)); + } + + $field->upload($path); + } + + /** + * {@inheritdoc} + */ + public function submitForm($xpath) + { + $crawler = $this->getFilteredCrawler($xpath); + + $this->submit($crawler->form()); + } + + /** + * @return Response + * + * @throws DriverException If there is not response yet + */ + protected function getResponse() + { + if (!method_exists($this->client, 'getInternalResponse')) { + $implementationResponse = $this->client->getResponse(); + + if (null === $implementationResponse) { + throw new DriverException('Unable to access the response before visiting a page'); + } + + return $this->convertImplementationResponse($implementationResponse); + } + + $response = $this->client->getInternalResponse(); + + if (null === $response) { + throw new DriverException('Unable to access the response before visiting a page'); + } + + return $response; + } + + /** + * Gets the BrowserKit Response for legacy BrowserKit versions. + * + * Before 2.3.0, there was no Client::getInternalResponse method, and the + * return value of Client::getResponse can be anything when the implementation + * uses Client::filterResponse because of a bad choice done in BrowserKit and + * kept for BC reasons (the Client::getInternalResponse method has been added + * to solve it). + * + * This implementation supports client which don't rely Client::filterResponse + * and clients which use an HttpFoundation Response (like the HttpKernel client). + * + * @param object $response the response specific to the BrowserKit implementation + * + * @return Response + * + * @throws DriverException If the response cannot be converted to a BrowserKit response + */ + private function convertImplementationResponse($response) + { + if ($response instanceof Response) { + return $response; + } + + // due to a bug, the HttpKernel client implementation returns the HttpFoundation response + // The conversion logic is copied from Symfony\Component\HttpKernel\Client::filterResponse + if ($response instanceof HttpFoundationResponse) { + $headers = $response->headers->all(); + if ($response->headers->getCookies()) { + $cookies = array(); + foreach ($response->headers->getCookies() as $cookie) { + $cookies[] = new Cookie( + $cookie->getName(), + $cookie->getValue(), + $cookie->getExpiresTime(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->isSecure(), + $cookie->isHttpOnly() + ); + } + $headers['Set-Cookie'] = $cookies; + } + + // this is needed to support StreamedResponse + ob_start(); + $response->sendContent(); + $content = ob_get_clean(); + + return new Response($content, $response->getStatusCode(), $headers); + } + + throw new DriverException(sprintf( + 'The BrowserKit client returned an unsupported response implementation: %s. Please upgrade your BrowserKit package to 2.3 or newer.', + get_class($response) + )); + } + + /** + * Prepares URL for visiting. + * Removes "*.php/" from urls and then passes it to BrowserKitDriver::visit(). + * + * @param string $url + * + * @return string + */ + protected function prepareUrl($url) + { + $replacement = ($this->removeHostFromUrl ? '' : '$1') . ($this->removeScriptFromUrl ? '' : '$2'); + + return preg_replace('#(https?\://[^/]+)(/[^/\.]+\.php)?#', $replacement, $url); + } + + /** + * Returns form field from XPath query. + * + * @param string $xpath + * + * @return FormField + * + * @throws DriverException + */ + protected function getFormField($xpath) + { + $fieldNode = $this->getCrawlerNode($this->getFilteredCrawler($xpath)); + $fieldName = str_replace('[]', '', $fieldNode->getAttribute('name')); + + $formNode = $this->getFormNode($fieldNode); + $formId = $this->getFormNodeId($formNode); + + if (!isset($this->forms[$formId])) { + $this->forms[$formId] = new Form($formNode, $this->getCurrentUrl()); + } + + if (is_array($this->forms[$formId][$fieldName])) { + return $this->forms[$formId][$fieldName][$this->getFieldPosition($fieldNode)]; + } + + return $this->forms[$formId][$fieldName]; + } + + /** + * Returns the checkbox field from xpath query, ensuring it is valid. + * + * @param string $xpath + * + * @return ChoiceFormField + * + * @throws DriverException when the field is not a checkbox + */ + private function getCheckboxField($xpath) + { + $field = $this->getFormField($xpath); + + if (!$field instanceof ChoiceFormField) { + throw new DriverException(sprintf('Impossible to check the element with XPath "%s" as it is not a checkbox', $xpath)); + } + + return $field; + } + + /** + * @param \DOMElement $element + * + * @return \DOMElement + * + * @throws DriverException if the form node cannot be found + */ + private function getFormNode(\DOMElement $element) + { + if ($element->hasAttribute('form')) { + $formId = $element->getAttribute('form'); + $formNode = $element->ownerDocument->getElementById($formId); + + if (null === $formNode || 'form' !== $formNode->nodeName) { + throw new DriverException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); + } + + return $formNode; + } + + $formNode = $element; + + do { + // use the ancestor form element + if (null === $formNode = $formNode->parentNode) { + throw new DriverException('The selected node does not have a form ancestor.'); + } + } while ('form' !== $formNode->nodeName); + + return $formNode; + } + + /** + * Gets the position of the field node among elements with the same name + * + * BrowserKit uses the field name as index to find the field in its Form object. + * When multiple fields have the same name (checkboxes for instance), it will return + * an array of elements in the order they appear in the DOM. + * + * @param \DOMElement $fieldNode + * + * @return integer + */ + private function getFieldPosition(\DOMElement $fieldNode) + { + $elements = $this->getCrawler()->filterXPath('//*[@name=\''.$fieldNode->getAttribute('name').'\']'); + + if (count($elements) > 1) { + // more than one element contains this name ! + // so we need to find the position of $fieldNode + foreach ($elements as $key => $element) { + /** @var \DOMElement $element */ + if ($element->getNodePath() === $fieldNode->getNodePath()) { + return $key; + } + } + } + + return 0; + } + + private function submit(Form $form) + { + $formId = $this->getFormNodeId($form->getFormNode()); + + if (isset($this->forms[$formId])) { + $this->mergeForms($form, $this->forms[$formId]); + } + + // remove empty file fields from request + foreach ($form->getFiles() as $name => $field) { + if (empty($field['name']) && empty($field['tmp_name'])) { + $form->remove($name); + } + } + + foreach ($form->all() as $field) { + // Add a fix for https://github.com/symfony/symfony/pull/10733 to support Symfony versions which are not fixed + if ($field instanceof TextareaFormField && null === $field->getValue()) { + $field->setValue(''); + } + } + + $this->client->submit($form); + + $this->forms = array(); + } + + private function resetForm(\DOMElement $fieldNode) + { + $formNode = $this->getFormNode($fieldNode); + $formId = $this->getFormNodeId($formNode); + unset($this->forms[$formId]); + } + + /** + * Determines if a node can submit a form. + * + * @param \DOMElement $node Node. + * + * @return boolean + */ + private function canSubmitForm(\DOMElement $node) + { + $type = $node->hasAttribute('type') ? $node->getAttribute('type') : null; + + if ('input' == $node->nodeName && in_array($type, array('submit', 'image'))) { + return true; + } + + return 'button' == $node->nodeName && (null === $type || 'submit' == $type); + } + + /** + * Determines if a node can reset a form. + * + * @param \DOMElement $node Node. + * + * @return boolean + */ + private function canResetForm(\DOMElement $node) + { + $type = $node->hasAttribute('type') ? $node->getAttribute('type') : null; + + return in_array($node->nodeName, array('input', 'button')) && 'reset' == $type; + } + + /** + * Returns form node unique identifier. + * + * @param \DOMElement $form + * + * @return string + */ + private function getFormNodeId(\DOMElement $form) + { + return md5($form->getLineNo() . $form->getNodePath() . $form->nodeValue); + } + + /** + * Gets the value of an option element + * + * @param \DOMElement $option + * + * @return string + * + * @see \Symfony\Component\DomCrawler\Field\ChoiceFormField::buildOptionValue + */ + private function getOptionValue(\DOMElement $option) + { + if ($option->hasAttribute('value')) { + return $option->getAttribute('value'); + } + + if (!empty($option->nodeValue)) { + return $option->nodeValue; + } + + return '1'; // DomCrawler uses 1 by default if there is no text in the option + } + + /** + * Merges second form values into first one. + * + * @param Form $to merging target + * @param Form $from merging source + */ + private function mergeForms(Form $to, Form $from) + { + foreach ($from->all() as $name => $field) { + $fieldReflection = new \ReflectionObject($field); + $nodeReflection = $fieldReflection->getProperty('node'); + $valueReflection = $fieldReflection->getProperty('value'); + + $nodeReflection->setAccessible(true); + $valueReflection->setAccessible(true); + + if (!($field instanceof InputFormField && in_array( + $nodeReflection->getValue($field)->getAttribute('type'), + array('submit', 'button', 'image') + ))) { + $valueReflection->setValue($to[$name], $valueReflection->getValue($field)); + } + } + } + + /** + * Returns DOMElement from crawler instance. + * + * @param Crawler $crawler + * + * @return \DOMElement + * + * @throws DriverException when the node does not exist + */ + private function getCrawlerNode(Crawler $crawler) + { + $crawler->rewind(); + $node = $crawler->current(); + + if (null !== $node) { + return $node; + } + + throw new DriverException('The element does not exist'); + } + + /** + * Returns a crawler filtered for the given XPath, requiring at least 1 result. + * + * @param string $xpath + * + * @return Crawler + * + * @throws DriverException when no matching elements are found + */ + private function getFilteredCrawler($xpath) + { + if (!count($crawler = $this->getCrawler()->filterXPath($xpath))) { + throw new DriverException(sprintf('There is no element matching XPath "%s"', $xpath)); + } + + return $crawler; + } + + /** + * Returns crawler instance (got from client). + * + * @return Crawler + * + * @throws DriverException + */ + private function getCrawler() + { + $crawler = $this->client->getCrawler(); + + if (null === $crawler) { + throw new DriverException('Unable to access the response content before visiting a page'); + } + + return $crawler; + } +} diff --git a/core/vendor/behat/mink-browserkit-driver/tests/BrowserKitConfig.php b/core/vendor/behat/mink-browserkit-driver/tests/BrowserKitConfig.php new file mode 100644 index 0000000..b80a2fb --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/BrowserKitConfig.php @@ -0,0 +1,37 @@ +visit('http://localhost/foo/index.html'); + $this->assertEquals(200, $session->getStatusCode()); + $this->assertEquals('http://localhost/foo/index.html', $session->getCurrentUrl()); + } +} diff --git a/core/vendor/behat/mink-browserkit-driver/tests/Custom/ErrorHandlingTest.php b/core/vendor/behat/mink-browserkit-driver/tests/Custom/ErrorHandlingTest.php new file mode 100644 index 0000000..32335c5 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/Custom/ErrorHandlingTest.php @@ -0,0 +1,181 @@ +client = new TestClient(); + } + + public function testGetClient() + { + $this->assertSame($this->client, $this->getDriver()->getClient()); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + * @expectedExceptionMessage Unable to access the response before visiting a page + */ + public function testGetResponseHeaderWithoutVisit() + { + $this->getDriver()->getResponseHeaders(); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + * @expectedExceptionMessage Unable to access the response content before visiting a page + */ + public function testFindWithoutVisit() + { + $this->getDriver()->find('//html'); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + * @expectedExceptionMessage Unable to access the request before visiting a page + */ + public function testGetCurrentUrlWithoutVisit() + { + $this->getDriver()->getCurrentUrl(); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + * @expectedExceptionMessage The selected node has an invalid form attribute (foo) + */ + public function testNotMatchingHtml5FormId() + { + $html = <<<'HTML' + + +
+ + +
+ + +HTML; + + $this->client->setNextResponse(new Response($html)); + + $driver = $this->getDriver(); + $driver->visit('/index.php'); + $driver->setValue('//input[./@name="test"]', 'bar'); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + * @expectedExceptionMessage The selected node has an invalid form attribute (foo) + */ + public function testInvalidHtml5FormId() + { + $html = <<<'HTML' + + +
+ + +
+
+ + +HTML; + + $this->client->setNextResponse(new Response($html)); + + $driver = $this->getDriver(); + $driver->visit('/index.php'); + $driver->setValue('//input[./@name="test"]', 'bar'); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + * @expectedExceptionMessage The selected node does not have a form ancestor. + */ + public function testManipulateInputWithoutForm() + { + $html = <<<'HTML' + + +
+ +
+
+ +
+ + +HTML; + + $this->client->setNextResponse(new Response($html)); + + $driver = $this->getDriver(); + $driver->visit('/index.php'); + $driver->setValue('//input[./@name="test"]', 'bar'); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + * @expectedExceptionMessage Behat\Mink\Driver\BrowserKitDriver supports clicking on links and buttons only. But "div" provided + */ + public function testClickOnUnsupportedElement() + { + $html = <<<'HTML' + + +
+ + +HTML; + + $this->client->setNextResponse(new Response($html)); + + $driver = $this->getDriver(); + $driver->visit('/index.php'); + $driver->click('//div'); + } + + private function getDriver() + { + return new BrowserKitDriver($this->client); + } +} + +class TestClient extends Client +{ + protected $nextResponse = null; + protected $nextScript = null; + + public function setNextResponse(Response $response) + { + $this->nextResponse = $response; + } + + public function setNextScript($script) + { + $this->nextScript = $script; + } + + protected function doRequest($request) + { + if (null === $this->nextResponse) { + return new Response(); + } + + $response = $this->nextResponse; + $this->nextResponse = null; + + return $response; + } +} diff --git a/core/vendor/behat/mink-browserkit-driver/tests/app.php b/core/vendor/behat/mink-browserkit-driver/tests/app.php new file mode 100644 index 0000000..d3f1236 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/app.php @@ -0,0 +1,37 @@ +register(new \Silex\Provider\SessionServiceProvider()); + +$def = realpath(__DIR__.'/../vendor/behat/mink/driver-testsuite/web-fixtures'); +$ovr = realpath(__DIR__.'/web-fixtures'); +$cbk = function ($file) use ($app, $def, $ovr) { + $file = str_replace('.file', '.php', $file); + $path = file_exists($ovr.'/'.$file) ? $ovr.'/'.$file : $def.'/'.$file; + $resp = null; + + ob_start(); + include($path); + $content = ob_get_clean(); + + if ($resp) { + if ('' === $resp->getContent()) { + $resp->setContent($content); + } + + return $resp; + } + + return $content; +}; + +$app->get('/{file}', $cbk)->assert('file', '.*'); +$app->post('/{file}', $cbk)->assert('file', '.*'); + +$app['debug'] = true; +$app['exception_handler']->disable(); +$app['session.test'] = true; + +return $app; diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/404.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/404.php new file mode 100644 index 0000000..2ae1ff9 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/404.php @@ -0,0 +1,3 @@ + + + + Advanced form save + + + +request->all(); +$FILES = $request->files->all(); + +if (isset($POST['select_multiple_numbers']) && false !== strpos($POST['select_multiple_numbers'][0], ',')) { + $POST['select_multiple_numbers'] = explode(',', $POST['select_multiple_numbers'][0]); +} + +// checkbox can have any value and will be successful in case "on" +// http://www.w3.org/TR/html401/interact/forms.html#checkbox +$POST['agreement'] = isset($POST['agreement']) ? 'on' : 'off'; +ksort($POST); +echo str_replace('>', '', var_export($POST, true)) . "\n"; +if (isset($FILES['about']) && file_exists($FILES['about']->getPathname())) { + echo $FILES['about']->getClientOriginalName() . "\n"; + echo file_get_contents($FILES['about']->getPathname()); +} else { + echo "no file"; +} +?> + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_auth.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_auth.php new file mode 100644 index 0000000..48132b6 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_auth.php @@ -0,0 +1,15 @@ +server->all(); + +$username = isset($SERVER['PHP_AUTH_USER']) ? $SERVER['PHP_AUTH_USER'] : false; +$password = isset($SERVER['PHP_AUTH_PW']) ? $SERVER['PHP_AUTH_PW'] : false; + +if ($username == 'mink-user' && $password == 'mink-password') { + echo 'is authenticated'; +} else { + $resp = new \Symfony\Component\HttpFoundation\Response(); + $resp->setStatusCode(401); + $resp->headers->set('WWW-Authenticate', 'Basic realm="Mink Testing Area"'); + + echo 'is not authenticated'; +} diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_form_post.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_form_post.php new file mode 100644 index 0000000..1efe45e --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_form_post.php @@ -0,0 +1,14 @@ + + + + + Basic Form Saving + + + +

Anket for request->get('first_name') ?>

+ + Firstname: request->get('first_name') ?> + Lastname: request->get('last_name') ?> + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_get_form.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_get_form.php new file mode 100644 index 0000000..fd2817d --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/basic_get_form.php @@ -0,0 +1,23 @@ + + + + Basic Get Form + + + +

Basic Get Form Page

+ +
+ query->all(); + echo isset($GET['q']) && $GET['q'] ? $GET['q'] : 'No search query' + ?> +
+ +
+ + + +
+ + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page1.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page1.php new file mode 100644 index 0000000..a928b2f --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page1.php @@ -0,0 +1,17 @@ +headers->setCookie($cook); +?> + + + + basic form + + + + + basic page with cookie set from server side + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page2.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page2.php new file mode 100644 index 0000000..ab54243 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page2.php @@ -0,0 +1,14 @@ + + + + Basic Form + + + + + Previous cookie: cookies->has('srvr_cookie') ? $app['request']->cookies->get('srvr_cookie') : 'NO'; + ?> + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page3.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page3.php new file mode 100644 index 0000000..f24d587 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/cookie_page3.php @@ -0,0 +1,20 @@ +cookies->has('foo'); +$resp = new Symfony\Component\HttpFoundation\Response(); +$cook = new Symfony\Component\HttpFoundation\Cookie('foo', 'bar'); +$resp->headers->setCookie($cook); + +?> + + + + HttpOnly Cookie Test + + + + + + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/headers.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/headers.php new file mode 100644 index 0000000..b829425 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/headers.php @@ -0,0 +1,10 @@ + + + + Headers page + + + + server->all()); ?> + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue130.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue130.php new file mode 100644 index 0000000..2079673 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue130.php @@ -0,0 +1,14 @@ + + + + + + query->get('p')) { + echo 'Go to 2'; + } else { + echo ''.$app['request']->headers->get('referer').''; + } + ?> + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue140.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue140.php new file mode 100644 index 0000000..42d8437 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/issue140.php @@ -0,0 +1,20 @@ + + + + + +isMethod('POST')) { + $resp = new Symfony\Component\HttpFoundation\Response(); + $cook = new Symfony\Component\HttpFoundation\Cookie('tc', $app['request']->request->get('cookie_value')); + $resp->headers->setCookie($cook); +} elseif ($app['request']->query->has('show_value')) { + echo $app['request']->cookies->get('tc'); + return; +} +?> +
+ + +
+ diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/print_cookies.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/print_cookies.php new file mode 100644 index 0000000..ac6f078 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/print_cookies.php @@ -0,0 +1,25 @@ + + + + Cookies page + + + + cookies->all(); + unset($cookies['MOCKSESSID']); + + if (isset($cookies['srvr_cookie'])) { + $srvrCookie = $cookies['srvr_cookie']; + unset($cookies['srvr_cookie']); + $cookies['_SESS'] = ''; + $cookies['srvr_cookie'] = $srvrCookie; + } + + foreach ($cookies as $name => $val) { + $cookies[$name] = (string)$val; + } + echo str_replace(array('>'), '', var_export($cookies, true)); + ?> + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/redirector.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/redirector.php new file mode 100644 index 0000000..39e8a53 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/redirector.php @@ -0,0 +1,3 @@ +headers->set('X-Mink-Test', 'response-headers'); +?> + + + + Response headers + + + +

Response headers

+ + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/session_test.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/session_test.php new file mode 100644 index 0000000..58576e3 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/session_test.php @@ -0,0 +1,19 @@ +getSession(); + +if ($app['request']->query->has('login')) { + $session->migrate(); +} +?> + + + + Session Test + + + + +
getId() ?>
+ + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page1.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page1.php new file mode 100644 index 0000000..807c23e --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page1.php @@ -0,0 +1,18 @@ +server->get('REQUEST_URI'); + $resp = new Symfony\Component\HttpFoundation\Response(); + $cook = new Symfony\Component\HttpFoundation\Cookie('srvr_cookie', 'srv_var_is_set_sub_folder', 0, dirname($requestUri)); + $resp->headers->setCookie($cook); +?> + + + + basic form + + + + + basic page with cookie set from server side + + diff --git a/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page2.php b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page2.php new file mode 100644 index 0000000..22a7dab --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/tests/web-fixtures/sub-folder/cookie_page2.php @@ -0,0 +1,18 @@ + + + + Basic Form + + + + + Previous cookie: cookies->has('srvr_cookie')) { + echo $app['request']->cookies->get('srvr_cookie'); + } else { + echo 'NO'; + } + ?> + + diff --git a/core/vendor/behat/mink-goutte-driver/.gitignore b/core/vendor/behat/mink-goutte-driver/.gitignore new file mode 100644 index 0000000..1d034f4 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/.gitignore @@ -0,0 +1,4 @@ +vendor +composer.phar +composer.lock +phpunit.xml diff --git a/core/vendor/behat/mink-goutte-driver/.travis.yml b/core/vendor/behat/mink-goutte-driver/.travis.yml new file mode 100644 index 0000000..b09e457 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: [5.3, 5.4, 5.5, 5.6, hhvm] + +before_script: + - export WEB_FIXTURES_HOST=http://localhost + + - composer install --dev --prefer-source + + - sudo apt-get update + - sudo apt-get install -y --force-yes apache2 libapache2-mod-php5 + - sudo sed -i -e "s,/var/www,$(pwd)/vendor/behat/mink/driver-testsuite/web-fixtures,g" /etc/apache2/sites-available/default + - sudo /etc/init.d/apache2 restart + +script: phpunit -v --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/core/vendor/behat/mink-goutte-driver/CHANGELOG.md b/core/vendor/behat/mink-goutte-driver/CHANGELOG.md new file mode 100644 index 0000000..fcaa7f0 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/CHANGELOG.md @@ -0,0 +1,20 @@ +1.1.0 / 2014-10-09 +================== + +The driver now relies on BrowserKitDriver 1.2.x, so all changes of this driver are relevant. +The changes below only describe the changes related to GoutteDriver specifically. + +New features: + +* Added the possibility to use a normal Goutte client instead of requiring to use an extended one +* Added the support of Goutte 2.0 as well + +Bug fixes: + +* Fixed the support of disabling basic auth +* Fixed the resetting of the driver to reset the basic auth + +Testing: + +* Updated the testsuite to use the new Mink 1.6 driver testsuite +* Added testing on HHVM diff --git a/core/vendor/behat/mink-goutte-driver/LICENSE b/core/vendor/behat/mink-goutte-driver/LICENSE new file mode 100644 index 0000000..3365ae6 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012-2013 Konstantin Kudryashov + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/vendor/behat/mink-goutte-driver/README.md b/core/vendor/behat/mink-goutte-driver/README.md new file mode 100755 index 0000000..c7877c5 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/README.md @@ -0,0 +1,53 @@ +Mink Goutte Driver +================== + +[![Latest Stable Version](https://poser.pugx.org/behat/mink-goutte-driver/v/stable.svg)](https://packagist.org/packages/behat/mink-goutte-driver) +[![Latest Unstable Version](https://poser.pugx.org/behat/mink-goutte-driver/v/unstable.svg)](https://packagist.org/packages/behat/mink-goutte-driver) +[![Total Downloads](https://poser.pugx.org/behat/mink-goutte-driver/downloads.svg)](https://packagist.org/packages/behat/mink-goutte-driver) +[![Build Status](https://travis-ci.org/Behat/MinkGoutteDriver.svg?branch=master)](https://travis-ci.org/Behat/MinkGoutteDriver) +[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/badges/quality-score.png?s=ca141bb2cad18e74cf3d3b132b1a6aa0f3f004a5)](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/) +[![Code Coverage](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/badges/coverage.png?s=ca2d17a948660bfaeb4a95bf1a709644305c54f3)](https://scrutinizer-ci.com/g/Behat/MinkGoutteDriver/) +[![License](https://poser.pugx.org/behat/mink-goutte-driver/license.svg)](https://packagist.org/packages/behat/mink-goutte-driver) + +Usage Example +------------- + +``` php + new Session(new GoutteDriver(new GoutteClient($startUrl))), +)); + +$mink->getSession('goutte')->getPage()->findLink('Chat')->click(); +``` + +Installation +------------ + +``` json +{ + "require": { + "behat/mink": "~1.5", + "behat/mink-goutte-driver": "~1.0" + } +} +``` + +``` bash +$> curl -sS https://getcomposer.org/installer | php +$> php composer.phar install +``` + +Maintainers +----------- + +* Konstantin Kudryashov [everzet](http://github.com/everzet) +* Other [awesome developers](https://github.com/Behat/MinkGoutteDriver/graphs/contributors) diff --git a/core/vendor/behat/mink-goutte-driver/composer.json b/core/vendor/behat/mink-goutte-driver/composer.json new file mode 100644 index 0000000..7444a50 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/composer.json @@ -0,0 +1,41 @@ +{ + "name": "behat/mink-goutte-driver", + "description": "Goutte driver for Mink framework", + "keywords": ["goutte", "headless", "testing", "browser"], + "homepage": "http://mink.behat.org/", + "type": "mink-driver", + "license": "MIT", + + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + + "require": { + "php": ">=5.3.1", + "behat/mink": "~1.6@dev", + "behat/mink-browserkit-driver": "~1.2@dev", + "fabpot/goutte": "~1.0.4|~2.0" + }, + + "autoload": { + "psr-0": { + "Behat\\Mink\\Driver": "src/" + } + }, + + "autoload-dev": { + "psr-4": { + "Behat\\Mink\\Tests\\Driver\\": "tests" + } + }, + + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} diff --git a/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist b/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist new file mode 100644 index 0000000..f374c53 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/phpunit.xml.dist @@ -0,0 +1,23 @@ + + + + + + tests + vendor/behat/mink/driver-testsuite/tests/Basic + vendor/behat/mink/driver-testsuite/tests/Form + + + + + + + + + + + + ./src/Behat/Mink/Driver + + + diff --git a/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/Goutte/Client.php b/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/Goutte/Client.php new file mode 100644 index 0000000..9d64be7 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/Goutte/Client.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Driver\Goutte; + +use Goutte\Client as BaseClient; +use Symfony\Component\BrowserKit\Response; + +/** + * Client overrides to support Mink functionality. + */ +class Client extends BaseClient +{ + /** + * Reads response meta tags to guess content-type charset. + * + * @param Response $response + * + * @return Response + */ + protected function filterResponse($response) + { + $contentType = $response->getHeader('Content-Type'); + + if (!$contentType || false === strpos($contentType, 'charset=')) { + if (preg_match('/\]+charset *= *["\']?([a-zA-Z\-0-9]+)/i', $response->getContent(), $matches)) { + $headers = $response->getHeaders(); + $headers['Content-Type'] = $contentType.';charset='.$matches[1]; + + $response = new Response($response->getContent(), $response->getStatus(), $headers); + } + } + + return parent::filterResponse($response); + } +} diff --git a/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/GoutteDriver.php b/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/GoutteDriver.php new file mode 100644 index 0000000..a42d5a2 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/src/Behat/Mink/Driver/GoutteDriver.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Driver; + +use Behat\Mink\Driver\Goutte\Client as ExtendedClient; +use Goutte\Client; + +/** + * Goutte driver. + * + * @author Konstantin Kudryashov + */ +class GoutteDriver extends BrowserKitDriver +{ + /** + * Initializes Goutte driver. + * + * @param Client $client Goutte client instance + */ + public function __construct(Client $client = null) + { + parent::__construct($client ?: new ExtendedClient()); + } + + /** + * {@inheritdoc} + */ + public function setBasicAuth($user, $password) + { + if (false === $user) { + $this->getClient()->resetAuth(); + + return; + } + + $this->getClient()->setAuth($user, $password); + } + + /** + * Gets the Goutte client. + * + * The method is overwritten only to provide the appropriate return type hint. + * + * @return Client + */ + public function getClient() + { + return parent::getClient(); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + parent::reset(); + $this->getClient()->resetAuth(); + } + + /** + * {@inheritdoc} + */ + protected function prepareUrl($url) + { + return $url; + } +} diff --git a/core/vendor/behat/mink-goutte-driver/tests/Custom/InstantiationTest.php b/core/vendor/behat/mink-goutte-driver/tests/Custom/InstantiationTest.php new file mode 100644 index 0000000..db75a18 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/tests/Custom/InstantiationTest.php @@ -0,0 +1,27 @@ +getMockBuilder('Goutte\Client')->disableOriginalConstructor()->getMock(); + $client->expects($this->once()) + ->method('followRedirects') + ->with(true); + + $driver = new GoutteDriver($client); + + $this->assertSame($client, $driver->getClient()); + } + + public function testInstantiateWithoutClient() + { + $driver = new GoutteDriver(); + + $this->assertInstanceOf('Behat\Mink\Driver\Goutte\Client', $driver->getClient()); + } +} diff --git a/core/vendor/behat/mink-goutte-driver/tests/GoutteConfig.php b/core/vendor/behat/mink-goutte-driver/tests/GoutteConfig.php new file mode 100644 index 0000000..bbe90f7 --- /dev/null +++ b/core/vendor/behat/mink-goutte-driver/tests/GoutteConfig.php @@ -0,0 +1,26 @@ +` is selected + * Added `NodeElement::submitForm` to allow submitting a form without using a button + * Added assertions about the value of an attribute + * Added the anchor in the assertion on the URL in `WebAssert` + +1.5.0 / 2013-04-14 +================== + + * Add `CoreDriver` to simplify future drivers improvements + * Add `Mink::isSessionStarted()` method + * Fix multibite string `preg_replace` bugs + * Fix handling of whitespaces in `WebAssert::pageText...()` methods + +1.4.3 / 2013-03-02 +================== + + * Bump dependencies constraints + +1.4.2 / 2013-02-13 +================== + + * Fix wrong test case to ensure that core drivers work as expected + +1.4.1 / 2013-02-10 +================== + + * Update dependencies + * Add ElementException to element actions + * Rel attribute support for named selectors + * Add hasClass() helper to traversable elements + * Add getScreenshot() method to session + * Name attr support in named selector for button + * Fix for bunch of bugs + +1.4.0 / 2012-05-40 +================== + + * New `Session::selectWindow()` and `Session::selectIFrame()` methods + * New built-in `WebAssert` class + * Fixed DocBlocks (autocompletion in any IDE now should just work) + * Moved Behat-related code into `Behat\MinkExtension` + * Removed PHPUnit test case class + * Updated composer dependencies to not require custom repository anymore + * All drivers moved into separate packages + +1.3.3 / 2012-03-23 +================== + + * Prevent exceptions in `__toString()` + * Added couple of useful step definitions for Behat + * Fixed issues #168, #211, #212, #208 + * Lot of small bug fixes and improvements + * Fixed dependencies and composer installation routine + +1.3.2 / 2011-12-21 +================== + + * Fixed webdriver registration in MinkContext + +1.3.1 / 2011-12-21 +================== + + * Fixed Composer package + +1.3.0 / 2011-12-21 +================== + + * Brand new Selenium2Driver (webdriver session) + * Multiselect bugfixes + * ZombieDriver back in the business + * Composer now manages dependencies + * Some MinkContext steps got fixes + * Lots of bug fixes and cleanup + +1.2.0 / 2011-11-04 +================== + + * Brand new SeleniumDriver (thanks @alexandresalome) + * Multiselect support (multiple options selection), including new Behat steps + * Ability to select option by it's text (in addition to value) + * ZombieDriver updates + * Use SuiteHooks to populate parameters (no need to call parent __construct anymore) + * Updated Goutte and all vendors + * Lot of bugfixes and new tests + +1.1.1 / 2011-08-12 +================== + + * Fixed Zombie.js server termination on Linux + * Fixed base_url usage for external URLs + +1.1.0 / 2011-08-08 +================== + + * Added Zombie.js driver (thanks @b00giZm) + * Added pt translation (thanks Daniel Gomes) + * Refactored MinkContext and MinkTestCase + +1.0.3 / 2011-08-02 +================== + + * File uploads for empty fields fixed (GoutteDriver) + * Lazy sessions restart + * `show_tmp_dir` option in MinkContext + * Updated to stable Symfony2 components + * SahiClient connection limit bumped to 60 seconds + * Dutch language support + +1.0.2 / 2011-07-22 +================== + + * ElementHtmlException fixed (thanks @Stof) + +1.0.1 / 2011-07-21 +================== + + * Fixed buggy assertions in MinkContext + +1.0.0 / 2011-07-20 +================== + + * Added missing tests for almost everything + * Hude speedup for SahiDriver + * Support for Behat 2.0 contexts + * Bundled PHPUnit TestCase + * Deep element traversing + * Correct behavior of getText() method + * New getHtml() method + * Basic HTTP auth support + * Soft and hard session resetting + * Cookies management + * Browser history interactions (reload(), back(), forward()) + * Weaverryan'd exception messages + * Huge amount of bugfixes and small additions + +0.3.2 / 2011-06-20 +================== + + * Fixed file uploads in Goutte driver + * Fixed setting of long texts into fields + * Added getPlainText() (returns text without tags and whitespaces) method to the element's API + * Start_url is now optional parameter + * Default session (if needed) name now need to be always specified by hands with setDefaultSessionName() + * default_driver => default_session + * Updated Symfony Components + +0.3.1 / 2011-05-17 +================== + + * Small SahiClient update (it generates SID now if no provided) + * setActiveSessionName => setDefaultSessionName method rename + +0.3.0 / 2011-05-17 +================== + + * Rewritten from scratch Mink drivers handler. Now it's sessions handler. And Mink now + sessions-centric tool. See examples in readme. Much cleaner API now. + +0.2.4 / 2011-05-12 +================== + + * Fixed wrong url locator function + * Fixed wrong regex in `should see` step + * Fixed delimiters use in `should see` step + * Added url-match step for checking urls against regex + +0.2.3 / 2011-05-01 +================== + + * Updated SahiClient with new version, which is faster and cleaner with it's exceptions + +0.2.2 / 2011-05-01 +================== + + * Ability to use already started browser as SahiDriver aim + * Added japanese translation for bundled steps (thanks @hidenorigoto) + * 10 seconds limit for browser connection in SahiDriver + +0.2.1 / 2011-04-21 +================== + + * Fixed some bundled step definitions + +0.2.0 / 2011-04-21 +================== + + * Additional step definitions + * Support for extended drivers configuration through behat.yml environment parameters + * Lots of new named selectors + * Bug fixes + * Small improvements + +0.1.2 / 2011-04-08 +================== + + * Fixed Sahi url escaping + +0.1.1 / 2011-04-06 +================== + + * Fixed should/should_not steps + * Added spanish translation + * Fixed forms to use element + * Fixed small UnsupportedByDriverException issue + +0.1.0 / 2011-04-04 +================== + + * Initial release diff --git a/core/vendor/behat/mink/CONTRIBUTING.md b/core/vendor/behat/mink/CONTRIBUTING.md new file mode 100644 index 0000000..5db4d85 --- /dev/null +++ b/core/vendor/behat/mink/CONTRIBUTING.md @@ -0,0 +1,22 @@ +# Contributing +Mink is an open source, community-driven project. If you'd like to contribute, feel free to do this, but remember to follow these few simple rules: + +## Submitting an issues +- __Driver-related__ issues must be reported in the corresponding driver repository +- A reproducible example is required for every bug report, otherwise it will most probably be __closed without warning__ +- If you are going to make a big, substantial change, let's discuss it first + +## Working with Pull Requests +1. Create your feature addition or a bug fix branch based on `master` branch in your repository's fork. +2. Make necessary changes, but __don't mix__ code reformatting with code changes on topic. +3. Add tests for those changes (please look into `tests/` folder for some examples). This is important so we don't break it in a future version unintentionally. +4. Commit your code, but do not mess with `CHANGES.md`. +5. Squash your commits by topic to preserve a clean and readable log. +6. Create Pull Request. + +# Running tests +Make sure that you don't break anything with your changes by running: + +```bash +$> phpunit +``` diff --git a/core/vendor/behat/mink/LICENSE b/core/vendor/behat/mink/LICENSE new file mode 100644 index 0000000..14f15e8 --- /dev/null +++ b/core/vendor/behat/mink/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2011-2013 Konstantin Kudryashov + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/vendor/behat/mink/README.md b/core/vendor/behat/mink/README.md new file mode 100644 index 0000000..d87cf3a --- /dev/null +++ b/core/vendor/behat/mink/README.md @@ -0,0 +1,72 @@ +Mink +==== +[![Latest Stable Version](https://poser.pugx.org/behat/mink/v/stable.svg)](https://packagist.org/packages/behat/mink) +[![Latest Unstable Version](https://poser.pugx.org/behat/mink/v/unstable.svg)](https://packagist.org/packages/behat/mink) +[![Total Downloads](https://poser.pugx.org/behat/mink/downloads.svg)](https://packagist.org/packages/behat/mink) +[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Behat/Mink/badges/quality-score.png?s=d4faf469d6b399df121deed6070390800722ada0)](https://scrutinizer-ci.com/g/Behat/Mink/) +[![Code Coverage](https://scrutinizer-ci.com/g/Behat/Mink/badges/coverage.png?s=88ab1cee4e131f4ef595f17ae4837001ef2aec3b)](https://scrutinizer-ci.com/g/Behat/Mink/) +[![Build Status](https://travis-ci.org/Behat/Mink.svg?branch=master)](https://travis-ci.org/Behat/Mink) +[![SensioLabsInsight](https://insight.sensiolabs.com/projects/5bb8fab0-978f-428a-ae23-44ee4e129fbc/mini.png)](https://insight.sensiolabs.com/projects/5bb8fab0-978f-428a-ae23-44ee4e129fbc) +[![License](https://poser.pugx.org/behat/mink/license.svg)](https://packagist.org/packages/behat/mink) + + +Useful Links +------------ + +- The main website with documentation is at [http://mink.behat.org](http://mink.behat.org) +- Official Google Group is at [http://groups.google.com/group/behat](http://groups.google.com/group/behat) +- IRC channel on [#freenode](http://freenode.net/) is `#behat` +- [Note on Patches/Pull Requests](CONTRIBUTING.md) + +Usage Example +------------- + +``` php + new Session(new GoutteDriver(new GoutteClient())), + 'goutte2' => new Session(new GoutteDriver(new GoutteClient())), + 'custom' => new Session(new MyCustomDriver($startUrl)) +)); + +// set the default session name +$mink->setDefaultSessionName('goutte2'); + +// visit a page +$mink->getSession()->visit($startUrl); + +// call to getSession() without argument will always return a default session if has one (goutte2 here) +$mink->getSession()->getPage()->findLink('Downloads')->click(); +echo $mink->getSession()->getPage()->getContent(); + +// call to getSession() with argument will return session by its name +$mink->getSession('custom')->getPage()->findLink('Downloads')->click(); +echo $mink->getSession('custom')->getPage()->getContent(); + +// this all is done to make possible mixing sessions +$mink->getSession('goutte1')->getPage()->findLink('Chat')->click(); +$mink->getSession('goutte2')->getPage()->findLink('Chat')->click(); +``` + +Install Dependencies +-------------------- + +``` bash +$> curl -sS https://getcomposer.org/installer | php +$> php composer.phar install +``` + +Contributors +------------ + +* Konstantin Kudryashov [everzet](http://github.com/everzet) [lead developer] +* Other [awesome developers](https://github.com/Behat/Mink/graphs/contributors) diff --git a/core/vendor/behat/mink/composer.json b/core/vendor/behat/mink/composer.json new file mode 100644 index 0000000..0aeee7b --- /dev/null +++ b/core/vendor/behat/mink/composer.json @@ -0,0 +1,46 @@ +{ + "name": "behat/mink", + "description": "Web acceptance testing framework for PHP 5.3", + "keywords": ["web", "testing", "browser"], + "homepage": "http://mink.behat.org/", + "type": "library", + "license": "MIT", + + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + + "require": { + "php": ">=5.3.1", + "symfony/css-selector": "~2.0" + }, + + "suggest": { + "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)", + "behat/mink-goutte-driver": "fast headless driver for any app without JS emulation", + "behat/mink-selenium2-driver": "slow, but JS-enabled driver for any app (requires Selenium2)", + "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)" + }, + + "autoload": { + "psr-0": { + "Behat\\Mink": "src/" + } + }, + + "autoload-dev": { + "psr-4": { + "Behat\\Mink\\Tests\\": "tests" + } + }, + + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/README.md b/core/vendor/behat/mink/driver-testsuite/README.md new file mode 100644 index 0000000..d21a747 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/README.md @@ -0,0 +1,93 @@ +Mink Driver testsuite +===================== + +This is the common testsuite for Mink drivers to ensure consistency among implementations. + +Usage +----- + +The testsuite of a driver should be based as follow: + +```json +{ + "require": { + "behat/mink": "~1.6@dev" + }, + + "autoload-dev": { + "psr-4": { + "Acme\MyDriver\Tests\\": "tests" + } + } +} +``` + +```xml + + + + + + + + + + + + + ./tests/ + vendor/behat/mink/driver-testsuite/tests + + + + + + ./src/Acme/MyDriver + + + +``` + +Then create the driver config for the testsuite: + +```php +// tests/Config.php + +namespace Acme\MyDriver\Tests; + +use Behat\Mink\Tests\Driver\AbstractConfig; + +class Config extends AbstractConfig +{ + /** + * Creates an instance of the config. + * + * This is the callable registered as a php variable in the phpunit.xml config file. + * It could be outside the class but this is convenient. + */ + public static function getInstance() + { + return new self(); + } + + /** + * Creates driver instance. + * + * @return \Behat\Mink\Driver\DriverInterface + */ + public function createDriver() + { + return new \Acme\MyDriver\MyDriver(); + } +} +``` + +Some other methods are available in the AbstractConfig which can be overwritten to adapt the testsuite to +the needs of the driver (skipping some tests for instance). + +Adding Driver-specific Tests +---------------------------- + +When adding extra test cases specific to the driver, either use your own namespace or put them in the +``Behat\Mink\Tests\Driver\Custom`` subnamespace to ensure that you will not create conflicts with test cases +added in the driver testsuite in the future. diff --git a/core/vendor/behat/mink/driver-testsuite/bootstrap.php b/core/vendor/behat/mink/driver-testsuite/bootstrap.php new file mode 100644 index 0000000..f69a45b --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/bootstrap.php @@ -0,0 +1,28 @@ +addPsr4('Behat\Mink\Tests\Driver\\', __DIR__.'/tests'); + +// Clean the global variables +unset($file); +unset($loader); + +// Check the definition of the driverLoaderFactory + +if (!isset($GLOBALS['driver_config_factory'])) { + echo PHP_EOL.'The "driver_config_factory" global variable must be set.'.PHP_EOL; + exit(1); +} +if (!is_callable($GLOBALS['driver_config_factory'])) { + echo PHP_EOL.'The "driver_config_factory" global variable must be a callable.'.PHP_EOL; + exit(1); +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/AbstractConfig.php b/core/vendor/behat/mink/driver-testsuite/tests/AbstractConfig.php new file mode 100644 index 0000000..5d02df2 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/AbstractConfig.php @@ -0,0 +1,83 @@ +supportsCss() && 0 === strpos($testCase, 'Behat\Mink\Tests\Driver\Css\\')) { + return 'This driver does not support CSS.'; + } + + if (!$this->supportsJs() && 0 === strpos($testCase, 'Behat\Mink\Tests\Driver\Js\\')) { + return 'This driver does not support JavaScript.'; + } + + return null; + } + + /** + * Whether the JS tests should run or no. + * + * @return bool + */ + protected function supportsJs() + { + return true; + } + + /** + * Whether the CSS tests should run or no. + * + * @return bool + */ + protected function supportsCss() + { + return false; + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/BasicAuthTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/BasicAuthTest.php new file mode 100644 index 0000000..40d932d --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/BasicAuthTest.php @@ -0,0 +1,64 @@ +getSession(); + + $session->setBasicAuth($user, $pass); + + $session->visit($this->pathTo('/basic_auth.php')); + + $this->assertContains($pageText, $session->getPage()->getContent()); + } + + public function setBasicAuthDataProvider() + { + return array( + array('mink-user', 'mink-password', 'is authenticated'), + array('', '', 'is not authenticated'), + ); + } + + public function testResetBasicAuth() + { + $session = $this->getSession(); + + $session->setBasicAuth('mink-user', 'mink-password'); + + $session->visit($this->pathTo('/basic_auth.php')); + + $this->assertContains('is authenticated', $session->getPage()->getContent()); + + $session->setBasicAuth(false); + + $session->visit($this->pathTo('/headers.php')); + + $this->assertNotContains('PHP_AUTH_USER', $session->getPage()->getContent()); + } + + public function testResetWithBasicAuth() + { + $session = $this->getSession(); + + $session->setBasicAuth('mink-user', 'mink-password'); + + $session->visit($this->pathTo('/basic_auth.php')); + + $this->assertContains('is authenticated', $session->getPage()->getContent()); + + $session->reset(); + + $session->visit($this->pathTo('/headers.php')); + + $this->assertNotContains('PHP_AUTH_USER', $session->getPage()->getContent()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/ContentTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ContentTest.php new file mode 100644 index 0000000..e68e8d0 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ContentTest.php @@ -0,0 +1,74 @@ +getSession()->visit($this->pathTo('/index.html')); + + $element = $this->getAssertSession()->elementExists('css', '.travers'); + + $this->assertEquals( + "
\n
el1
\n". + "
el2
\n
\n". + " some deep url\n". + "
\n
", + $element->getOuterHtml() + ); + } + + /** + * @dataProvider getAttributeDataProvider + */ + public function testGetAttribute($attributeName, $attributeValue) + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $element = $this->getSession()->getPage()->findById('attr-elem[' . $attributeName . ']'); + + $this->assertNotNull($element); + $this->assertSame($attributeValue, $element->getAttribute($attributeName)); + } + + public function getAttributeDataProvider() + { + return array( + array('with-value', 'some-value'), + array('without-value', ''), + array('with-empty-value', ''), + array('with-missing', null), + ); + } + + public function testJson() + { + $this->getSession()->visit($this->pathTo('/json.php')); + $this->assertContains( + '{"key1":"val1","key2":234,"key3":[1,2,3]}', + $this->getSession()->getPage()->getContent() + ); + } + + public function testHtmlDecodingNotPerformed() + { + $session = $this->getSession(); + $webAssert = $this->getAssertSession(); + $session->visit($this->pathTo('/html_decoding.html')); + $page = $session->getPage(); + + $span = $webAssert->elementExists('css', 'span'); + $input = $webAssert->elementExists('css', 'input'); + + $expectedHtml = 'some text'; + $this->assertContains($expectedHtml, $page->getHtml(), '.innerHTML is returned as-is'); + $this->assertContains($expectedHtml, $page->getContent(), '.outerHTML is returned as-is'); + + $this->assertEquals('&', $span->getAttribute('custom-attr'), '.getAttribute value is decoded'); + $this->assertEquals('&', $input->getAttribute('value'), '.getAttribute value is decoded'); + $this->assertEquals('&', $input->getValue(), 'node value is decoded'); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/CookieTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/CookieTest.php new file mode 100644 index 0000000..355dd71 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/CookieTest.php @@ -0,0 +1,167 @@ +getSession()->visit($this->pathTo('/issue140.php')); + + $this->getSession()->getPage()->fillField('cookie_value', 'some:value;'); + $this->getSession()->getPage()->pressButton('Set cookie'); + + $this->getSession()->visit($this->pathTo('/issue140.php?show_value')); + $this->assertEquals('some:value;', $this->getSession()->getCookie('tc')); + $this->assertEquals('some:value;', $this->getSession()->getPage()->getText()); + } + + public function testCookie() + { + $this->getSession()->visit($this->pathTo('/cookie_page2.php')); + $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText()); + $this->assertNull($this->getSession()->getCookie('srvr_cookie')); + + $this->getSession()->setCookie('srvr_cookie', 'client cookie set'); + $this->getSession()->reload(); + $this->assertContains('Previous cookie: client cookie set', $this->getSession()->getPage()->getText()); + $this->assertEquals('client cookie set', $this->getSession()->getCookie('srvr_cookie')); + + $this->getSession()->setCookie('srvr_cookie', null); + $this->getSession()->reload(); + $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText()); + + $this->getSession()->visit($this->pathTo('/cookie_page1.php')); + $this->getSession()->visit($this->pathTo('/cookie_page2.php')); + + $this->assertContains('Previous cookie: srv_var_is_set', $this->getSession()->getPage()->getText()); + $this->getSession()->setCookie('srvr_cookie', null); + $this->getSession()->reload(); + $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText()); + } + + /** + * @dataProvider cookieWithPathsDataProvider + */ + public function testCookieWithPaths($cookieRemovalMode) + { + // start clean + $session = $this->getSession(); + $session->visit($this->pathTo('/sub-folder/cookie_page2.php')); + $this->assertContains('Previous cookie: NO', $session->getPage()->getText()); + + // cookie from root path is accessible in sub-folder + $session->visit($this->pathTo('/cookie_page1.php')); + $session->visit($this->pathTo('/sub-folder/cookie_page2.php')); + $this->assertContains('Previous cookie: srv_var_is_set', $session->getPage()->getText()); + + // cookie from sub-folder overrides cookie from root path + $session->visit($this->pathTo('/sub-folder/cookie_page1.php')); + $session->visit($this->pathTo('/sub-folder/cookie_page2.php')); + $this->assertContains('Previous cookie: srv_var_is_set_sub_folder', $session->getPage()->getText()); + + if ($cookieRemovalMode == 'session_reset') { + $session->reset(); + } elseif ($cookieRemovalMode == 'cookie_delete') { + $session->setCookie('srvr_cookie', null); + } + + // cookie is removed from all paths + $session->visit($this->pathTo('/sub-folder/cookie_page2.php')); + $this->assertContains('Previous cookie: NO', $session->getPage()->getText()); + } + + public function cookieWithPathsDataProvider() + { + return array( + array('session_reset'), + array('cookie_delete'), + ); + } + + public function testReset() + { + $this->getSession()->visit($this->pathTo('/cookie_page1.php')); + $this->getSession()->visit($this->pathTo('/cookie_page2.php')); + $this->assertContains('Previous cookie: srv_var_is_set', $this->getSession()->getPage()->getText()); + + $this->getSession()->reset(); + $this->getSession()->visit($this->pathTo('/cookie_page2.php')); + + $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText()); + + $this->getSession()->setCookie('srvr_cookie', 'test_cookie'); + $this->getSession()->visit($this->pathTo('/cookie_page2.php')); + $this->assertContains('Previous cookie: test_cookie', $this->getSession()->getPage()->getText()); + $this->getSession()->reset(); + $this->getSession()->visit($this->pathTo('/cookie_page2.php')); + $this->assertContains('Previous cookie: NO', $this->getSession()->getPage()->getText()); + + $this->getSession()->setCookie('client_cookie1', 'some_val'); + $this->getSession()->setCookie('client_cookie2', 123); + $this->getSession()->visit($this->pathTo('/session_test.php')); + $this->getSession()->visit($this->pathTo('/cookie_page1.php')); + + $this->getSession()->visit($this->pathTo('/print_cookies.php')); + $this->assertContains( + "'client_cookie1' = 'some_val'", + $this->getSession()->getPage()->getText() + ); + $this->assertContains( + "'client_cookie2' = '123'", + $this->getSession()->getPage()->getText() + ); + $this->assertContains( + "_SESS' = ", + $this->getSession()->getPage()->getText() + ); + $this->assertContains( + " 'srvr_cookie' = 'srv_var_is_set'", + $this->getSession()->getPage()->getText() + ); + + $this->getSession()->reset(); + $this->getSession()->visit($this->pathTo('/print_cookies.php')); + $this->assertContains('array ( )', $this->getSession()->getPage()->getText()); + } + + public function testHttpOnlyCookieIsDeleted() + { + $this->getSession()->restart(); + $this->getSession()->visit($this->pathTo('/cookie_page3.php')); + $this->assertEquals('Has Cookie: false', $this->findById('cookie-status')->getText()); + + $this->getSession()->reload(); + $this->assertEquals('Has Cookie: true', $this->findById('cookie-status')->getText()); + + $this->getSession()->restart(); + $this->getSession()->visit($this->pathTo('/cookie_page3.php')); + $this->assertEquals('Has Cookie: false', $this->findById('cookie-status')->getText()); + } + + public function testSessionPersistsBetweenRequests() + { + $this->getSession()->visit($this->pathTo('/session_test.php')); + $webAssert = $this->getAssertSession(); + $node = $webAssert->elementExists('css', '#session-id'); + $sessionId = $node->getText(); + + $this->getSession()->visit($this->pathTo('/session_test.php')); + $node = $webAssert->elementExists('css', '#session-id'); + $this->assertEquals($sessionId, $node->getText()); + + $this->getSession()->visit($this->pathTo('/session_test.php?login')); + $node = $webAssert->elementExists('css', '#session-id'); + $this->assertNotEquals($sessionId, $newSessionId = $node->getText()); + + $this->getSession()->visit($this->pathTo('/session_test.php')); + $node = $webAssert->elementExists('css', '#session-id'); + $this->assertEquals($newSessionId, $node->getText()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/ErrorHandlingTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ErrorHandlingTest.php new file mode 100644 index 0000000..440585a --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ErrorHandlingTest.php @@ -0,0 +1,262 @@ +getSession()->visit($this->pathTo('/500.php')); + + $this->assertContains( + 'Sorry, a server error happened', + $this->getSession()->getPage()->getContent(), + 'Drivers allow loading pages with a 500 status code' + ); + } + + public function testCheckInvalidElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + $element = $this->findById('user-name'); + + $this->setExpectedException(self::INVALID_EXCEPTION); + $this->getSession()->getDriver()->check($element->getXpath()); + } + + public function testCheckNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->check(self::NOT_FOUND_XPATH); + } + + public function testUncheckInvalidElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + $element = $this->findById('user-name'); + + $this->setExpectedException(self::INVALID_EXCEPTION); + $this->getSession()->getDriver()->uncheck($element->getXpath()); + } + + public function testUncheckNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->uncheck(self::NOT_FOUND_XPATH); + } + + public function testSelectOptionInvalidElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + $element = $this->findById('user-name'); + + $this->setExpectedException(self::INVALID_EXCEPTION); + $this->getSession()->getDriver()->selectOption($element->getXpath(), 'test'); + } + + public function testSelectOptionNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->selectOption(self::NOT_FOUND_XPATH, 'test'); + } + + public function testAttachFileInvalidElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + $element = $this->findById('user-name'); + + $this->setExpectedException(self::INVALID_EXCEPTION); + $this->getSession()->getDriver()->attachFile($element->getXpath(), __FILE__); + } + + public function testAttachFileNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->attachFile(self::NOT_FOUND_XPATH, __FILE__); + } + + public function testSubmitFormInvalidElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + $element = $this->findById('core'); + + $this->setExpectedException(self::INVALID_EXCEPTION); + $this->getSession()->getDriver()->submitForm($element->getXpath()); + } + + public function testSubmitFormNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->submitForm(self::NOT_FOUND_XPATH); + } + + public function testGetTagNameNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->getTagName(self::NOT_FOUND_XPATH); + } + + public function testGetTextNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->getText(self::NOT_FOUND_XPATH); + } + + public function testGetHtmlNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->getHtml(self::NOT_FOUND_XPATH); + } + + public function testGetOuterHtmlNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->getOuterHtml(self::NOT_FOUND_XPATH); + } + + public function testGetValueNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->getValue(self::NOT_FOUND_XPATH); + } + + public function testSetValueNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->setValue(self::NOT_FOUND_XPATH, 'test'); + } + + public function testIsSelectedNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->isSelected(self::NOT_FOUND_XPATH); + } + + public function testIsCheckedNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->isChecked(self::NOT_FOUND_XPATH); + } + + public function testIsVisibleNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->isVisible(self::NOT_FOUND_XPATH); + } + + public function testClickNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->click(self::NOT_FOUND_XPATH); + } + + public function testDoubleClickNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->doubleClick(self::NOT_FOUND_XPATH); + } + + public function testRightClickNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->rightClick(self::NOT_FOUND_XPATH); + } + + public function testGetAttributeNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->getAttribute(self::NOT_FOUND_XPATH, 'id'); + } + + public function testMouseOverNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->mouseOver(self::NOT_FOUND_XPATH); + } + + public function testFocusNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->focus(self::NOT_FOUND_XPATH); + } + + public function testBlurNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->blur(self::NOT_FOUND_XPATH); + } + + public function testKeyPressNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->keyPress(self::NOT_FOUND_XPATH, 'a'); + } + + public function testKeyDownNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->keyDown(self::NOT_FOUND_XPATH, 'a'); + } + + public function testKeyUpNotFoundElement() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $this->setExpectedException(self::NOT_FOUND_EXCEPTION); + $this->getSession()->getDriver()->keyUp(self::NOT_FOUND_XPATH, 'a'); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/HeaderTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/HeaderTest.php new file mode 100644 index 0000000..0288ff1 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/HeaderTest.php @@ -0,0 +1,77 @@ +getSession()->visit($this->pathTo('/issue130.php?p=1')); + $page = $this->getSession()->getPage(); + + $page->clickLink('Go to 2'); + $this->assertEquals($this->pathTo('/issue130.php?p=1'), $page->getText()); + } + + public function testHeaders() + { + $this->getSession()->setRequestHeader('Accept-Language', 'fr'); + $this->getSession()->visit($this->pathTo('/headers.php')); + + $this->assertContains('[HTTP_ACCEPT_LANGUAGE] => fr', $this->getSession()->getPage()->getContent()); + } + + public function testSetUserAgent() + { + $session = $this->getSession(); + + $session->setRequestHeader('user-agent', 'foo bar'); + $session->visit($this->pathTo('/headers.php')); + $this->assertContains('[HTTP_USER_AGENT] => foo bar', $session->getPage()->getContent()); + } + + public function testResetHeaders() + { + $session = $this->getSession(); + + $session->setRequestHeader('X-Mink-Test', 'test'); + $session->visit($this->pathTo('/headers.php')); + + $this->assertContains( + '[HTTP_X_MINK_TEST] => test', + $session->getPage()->getContent(), + 'The custom header should be sent', + true + ); + + $session->reset(); + $session->visit($this->pathTo('/headers.php')); + + $this->assertNotContains( + '[HTTP_X_MINK_TEST] => test', + $session->getPage()->getContent(), + 'The custom header should not be sent after resetting', + true + ); + } + + public function testResponseHeaders() + { + $this->getSession()->visit($this->pathTo('/response_headers.php')); + + $headers = $this->getSession()->getResponseHeaders(); + + $lowercasedHeaders = array(); + foreach ($headers as $name => $value) { + $lowercasedHeaders[str_replace('_', '-', strtolower($name))] = $value; + } + + $this->assertArrayHasKey('x-mink-test', $lowercasedHeaders); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/IFrameTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/IFrameTest.php new file mode 100644 index 0000000..0ed0f9e --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/IFrameTest.php @@ -0,0 +1,27 @@ +getSession()->visit($this->pathTo('/iframe.html')); + $webAssert = $this->getAssertSession(); + + $el = $webAssert->elementExists('css', '#text'); + $this->assertSame('Main window div text', $el->getText()); + + $this->getSession()->switchToIFrame('subframe'); + + $el = $webAssert->elementExists('css', '#text'); + $this->assertSame('iFrame div text', $el->getText()); + + $this->getSession()->switchToIFrame(); + + $el = $webAssert->elementExists('css', '#text'); + $this->assertSame('Main window div text', $el->getText()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/NavigationTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/NavigationTest.php new file mode 100644 index 0000000..8ecb139 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/NavigationTest.php @@ -0,0 +1,69 @@ +getSession()->visit($this->pathTo('/redirector.php')); + $this->assertEquals($this->pathTo('/redirect_destination.html'), $this->getSession()->getCurrentUrl()); + } + + public function testPageControlls() + { + $this->getSession()->visit($this->pathTo('/randomizer.php')); + $number1 = $this->getSession()->getPage()->find('css', '#number')->getText(); + + $this->getSession()->reload(); + $number2 = $this->getSession()->getPage()->find('css', '#number')->getText(); + + $this->assertNotEquals($number1, $number2); + + $this->getSession()->visit($this->pathTo('/links.html')); + $this->getSession()->getPage()->clickLink('Random number page'); + + $this->assertEquals($this->pathTo('/randomizer.php'), $this->getSession()->getCurrentUrl()); + + $this->getSession()->back(); + $this->assertEquals($this->pathTo('/links.html'), $this->getSession()->getCurrentUrl()); + + $this->getSession()->forward(); + $this->assertEquals($this->pathTo('/randomizer.php'), $this->getSession()->getCurrentUrl()); + } + + public function testLinks() + { + $this->getSession()->visit($this->pathTo('/links.html')); + $page = $this->getSession()->getPage(); + $link = $page->findLink('Redirect me to'); + + $this->assertNotNull($link); + $this->assertRegExp('/redirector\.php$/', $link->getAttribute('href')); + $link->click(); + + $this->assertEquals($this->pathTo('/redirect_destination.html'), $this->getSession()->getCurrentUrl()); + + $this->getSession()->visit($this->pathTo('/links.html')); + $page = $this->getSession()->getPage(); + $link = $page->findLink('basic form image'); + + $this->assertNotNull($link); + $this->assertRegExp('/basic_form\.html$/', $link->getAttribute('href')); + $link->click(); + + $this->assertEquals($this->pathTo('/basic_form.html'), $this->getSession()->getCurrentUrl()); + + $this->getSession()->visit($this->pathTo('/links.html')); + $page = $this->getSession()->getPage(); + $link = $page->findLink("Link with a "); + + $this->assertNotNull($link); + $this->assertRegExp('/links\.html\?quoted$/', $link->getAttribute('href')); + $link->click(); + + $this->assertEquals($this->pathTo('/links.html?quoted'), $this->getSession()->getCurrentUrl()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/ScreenshotTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ScreenshotTest.php new file mode 100644 index 0000000..329f2d3 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/ScreenshotTest.php @@ -0,0 +1,30 @@ +markTestSkipped('Testing screenshots requires the GD extension'); + } + + $this->getSession()->visit($this->pathTo('/index.html')); + + $screenShot = $this->getSession()->getScreenshot(); + + $this->assertInternalType('string', $screenShot); + $this->assertFalse(base64_decode($screenShot, true), 'The returned screenshot should not be base64-encoded'); + + $img = imagecreatefromstring($screenShot); + + if (false === $img) { + $this->fail('The screenshot should be a valid image'); + } + + imagedestroy($img); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/StatusCodeTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/StatusCodeTest.php new file mode 100644 index 0000000..34ca7f0 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/StatusCodeTest.php @@ -0,0 +1,22 @@ +getSession()->visit($this->pathTo('/index.html')); + + $this->assertEquals(200, $this->getSession()->getStatusCode()); + $this->assertEquals($this->pathTo('/index.html'), $this->getSession()->getCurrentUrl()); + + $this->getSession()->visit($this->pathTo('/404.php')); + + $this->assertEquals($this->pathTo('/404.php'), $this->getSession()->getCurrentUrl()); + $this->assertEquals(404, $this->getSession()->getStatusCode()); + $this->assertEquals('Sorry, page not found', $this->getSession()->getPage()->getContent()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/TraversingTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/TraversingTest.php new file mode 100644 index 0000000..b31f7fc --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/TraversingTest.php @@ -0,0 +1,129 @@ +getSession()->visit($this->pathTo('/issue211.html')); + $field = $this->getSession()->getPage()->findField('Téléphone'); + + $this->assertNotNull($field); + } + + public function testElementsTraversing() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $page = $this->getSession()->getPage(); + + $this->assertNotNull($page->find('css', 'h1')); + $this->assertEquals('Extremely useless page', $page->find('css', 'h1')->getText()); + $this->assertEquals('h1', $page->find('css', 'h1')->getTagName()); + + $this->assertNotNull($page->find('xpath', '//div/strong[3]')); + $this->assertEquals('pariatur', $page->find('xpath', '//div/strong[3]')->getText()); + $this->assertEquals('super-duper', $page->find('xpath', '//div/strong[3]')->getAttribute('class')); + $this->assertTrue($page->find('xpath', '//div/strong[3]')->hasAttribute('class')); + + $this->assertNotNull($page->find('xpath', '//div/strong[2]')); + $this->assertEquals('veniam', $page->find('xpath', '//div/strong[2]')->getText()); + $this->assertEquals('strong', $page->find('xpath', '//div/strong[2]')->getTagName()); + $this->assertNull($page->find('xpath', '//div/strong[2]')->getAttribute('class')); + $this->assertFalse($page->find('xpath', '//div/strong[2]')->hasAttribute('class')); + + $strongs = $page->findAll('css', 'div#core > strong'); + $this->assertCount(3, $strongs); + $this->assertEquals('Lorem', $strongs[0]->getText()); + $this->assertEquals('pariatur', $strongs[2]->getText()); + + $element = $page->find('css', '#some-element'); + + $this->assertEquals('some very interesting text', $element->getText()); + $this->assertEquals( + "\n some
very\n
\n". + "interesting text\n ", + $element->getHtml() + ); + + $this->assertTrue($element->hasAttribute('data-href')); + $this->assertFalse($element->hasAttribute('data-url')); + $this->assertEquals('http://mink.behat.org', $element->getAttribute('data-href')); + $this->assertNull($element->getAttribute('data-url')); + $this->assertEquals('div', $element->getTagName()); + } + + public function testVeryDeepElementsTraversing() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $page = $this->getSession()->getPage(); + + $footer = $page->find('css', 'footer'); + $this->assertNotNull($footer); + + $searchForm = $footer->find('css', 'form#search-form'); + $this->assertNotNull($searchForm); + $this->assertEquals('search-form', $searchForm->getAttribute('id')); + + $searchInput = $searchForm->findField('Search site...'); + $this->assertNotNull($searchInput); + $this->assertEquals('text', $searchInput->getAttribute('type')); + + $searchInput = $searchForm->findField('Search site...'); + $this->assertNotNull($searchInput); + $this->assertEquals('text', $searchInput->getAttribute('type')); + + $profileForm = $footer->find('css', '#profile'); + $this->assertNotNull($profileForm); + + $profileFormDiv = $profileForm->find('css', 'div'); + $this->assertNotNull($profileFormDiv); + + $profileFormDivLabel = $profileFormDiv->find('css', 'label'); + $this->assertNotNull($profileFormDivLabel); + + $profileFormDivParent = $profileFormDivLabel->getParent(); + $this->assertNotNull($profileFormDivParent); + + $profileFormDivParent = $profileFormDivLabel->getParent(); + $this->assertEquals('something', $profileFormDivParent->getAttribute('data-custom')); + + $profileFormInput = $profileFormDivLabel->findField('user-name'); + $this->assertNotNull($profileFormInput); + $this->assertEquals('username', $profileFormInput->getAttribute('name')); + } + + public function testDeepTraversing() + { + $this->getSession()->visit($this->pathTo('/index.html')); + + $traversDiv = $this->getSession()->getPage()->findAll('css', 'div.travers'); + + $this->assertCount(1, $traversDiv); + $traversDiv = $traversDiv[0]; + + $subDivs = $traversDiv->findAll('css', 'div.sub'); + $this->assertCount(3, $subDivs); + + $this->assertTrue($subDivs[2]->hasLink('some deep url')); + $this->assertFalse($subDivs[2]->hasLink('come deep url')); + $subUrl = $subDivs[2]->findLink('some deep url'); + $this->assertNotNull($subUrl); + + $this->assertRegExp('/some_url$/', $subUrl->getAttribute('href')); + $this->assertEquals('some deep url', $subUrl->getText()); + $this->assertEquals('some deep url', $subUrl->getHtml()); + + $this->assertTrue($subUrl->has('css', 'strong')); + $this->assertFalse($subUrl->has('css', 'em')); + $this->assertEquals('deep', $subUrl->find('css', 'strong')->getText()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Basic/VisibilityTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Basic/VisibilityTest.php new file mode 100644 index 0000000..b3daf31 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Basic/VisibilityTest.php @@ -0,0 +1,20 @@ +getSession()->visit($this->pathTo('/js_test.html')); + $webAssert = $this->getAssertSession(); + + $clicker = $webAssert->elementExists('css', '.elements div#clicker'); + $invisible = $webAssert->elementExists('css', '#invisible'); + + $this->assertFalse($invisible->isVisible()); + $this->assertTrue($clicker->isVisible()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Css/HoverTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Css/HoverTest.php new file mode 100644 index 0000000..87041d4 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Css/HoverTest.php @@ -0,0 +1,76 @@ +getSession()->visit($this->pathTo('/css_mouse_events.html')); + + $this->findById('reset-square')->mouseOver(); + $this->assertActionSquareHeight(100); + + $this->findById('action-square')->mouseOver(); + $this->assertActionSquareHeight(200); + } + + /** + * @group mouse-events + * @depends testMouseOverHover + */ + public function testClickHover() + { + $this->getSession()->visit($this->pathTo('/css_mouse_events.html')); + + $this->findById('reset-square')->mouseOver(); + $this->assertActionSquareHeight(100); + + $this->findById('action-square')->click(); + $this->assertActionSquareHeight(200); + } + + /** + * @group mouse-events + * @depends testMouseOverHover + */ + public function testDoubleClickHover() + { + $this->getSession()->visit($this->pathTo('/css_mouse_events.html')); + + $this->findById('reset-square')->mouseOver(); + $this->assertActionSquareHeight(100); + + $this->findById('action-square')->doubleClick(); + $this->assertActionSquareHeight(200); + } + + /** + * @group mouse-events + * @depends testMouseOverHover + */ + public function testRightClickHover() + { + $this->getSession()->visit($this->pathTo('/css_mouse_events.html')); + + $this->findById('reset-square')->mouseOver(); + $this->assertActionSquareHeight(100); + + $this->findById('action-square')->rightClick(); + $this->assertActionSquareHeight(200); + } + + private function assertActionSquareHeight($expected) + { + $this->assertEquals( + $expected, + $this->getSession()->evaluateScript("return window.$('#action-square').height();"), + 'Mouse is located over the object when mouse-related action is performed' + ); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/CheckboxTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/CheckboxTest.php new file mode 100644 index 0000000..e1ce4ab --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/CheckboxTest.php @@ -0,0 +1,73 @@ +getSession()->visit($this->pathTo('advanced_form.html')); + + $checkbox = $this->getAssertSession()->fieldExists('agreement'); + + $this->assertNull($checkbox->getValue()); + $this->assertFalse($checkbox->isChecked()); + + $checkbox->check(); + + $this->assertEquals('yes', $checkbox->getValue()); + $this->assertTrue($checkbox->isChecked()); + + $checkbox->uncheck(); + + $this->assertNull($checkbox->getValue()); + $this->assertFalse($checkbox->isChecked()); + } + + public function testSetValue() + { + $this->getSession()->visit($this->pathTo('advanced_form.html')); + + $checkbox = $this->getAssertSession()->fieldExists('agreement'); + + $this->assertNull($checkbox->getValue()); + $this->assertFalse($checkbox->isChecked()); + + $checkbox->setValue(true); + + $this->assertEquals('yes', $checkbox->getValue()); + $this->assertTrue($checkbox->isChecked()); + + $checkbox->setValue(false); + + $this->assertNull($checkbox->getValue()); + $this->assertFalse($checkbox->isChecked()); + } + + public function testCheckboxMultiple() + { + $this->getSession()->visit($this->pathTo('/multicheckbox_form.html')); + $webAssert = $this->getAssertSession(); + + $this->assertEquals('Multicheckbox Test', $webAssert->elementExists('css', 'h1')->getText()); + + $updateMail = $webAssert->elementExists('css', '[name="mail_types[]"][value="update"]'); + $spamMail = $webAssert->elementExists('css', '[name="mail_types[]"][value="spam"]'); + + $this->assertEquals('update', $updateMail->getValue()); + $this->assertNull($spamMail->getValue()); + + $this->assertTrue($updateMail->isChecked()); + $this->assertFalse($spamMail->isChecked()); + + $updateMail->uncheck(); + $this->assertFalse($updateMail->isChecked()); + $this->assertFalse($spamMail->isChecked()); + + $spamMail->check(); + $this->assertFalse($updateMail->isChecked()); + $this->assertTrue($spamMail->isChecked()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/GeneralTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/GeneralTest.php new file mode 100644 index 0000000..263be70 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/GeneralTest.php @@ -0,0 +1,312 @@ +getSession(); + + $session->visit($this->pathTo('/issue212.html')); + + $field = $this->findById('poney-button'); + $this->assertEquals('poney', $field->getValue()); + } + + public function testBasicForm() + { + $this->getSession()->visit($this->pathTo('/basic_form.html')); + + $webAssert = $this->getAssertSession(); + $page = $this->getSession()->getPage(); + $this->assertEquals('Basic Form Page', $webAssert->elementExists('css', 'h1')->getText()); + + $firstname = $webAssert->fieldExists('first_name'); + $lastname = $webAssert->fieldExists('lastn'); + + $this->assertEquals('Firstname', $firstname->getValue()); + $this->assertEquals('Lastname', $lastname->getValue()); + + $firstname->setValue('Konstantin'); + $page->fillField('last_name', 'Kudryashov'); + + $this->assertEquals('Konstantin', $firstname->getValue()); + $this->assertEquals('Kudryashov', $lastname->getValue()); + + $page->findButton('Reset')->click(); + + $this->assertEquals('Firstname', $firstname->getValue()); + $this->assertEquals('Lastname', $lastname->getValue()); + + $firstname->setValue('Konstantin'); + $page->fillField('last_name', 'Kudryashov'); + + $page->findButton('Save')->click(); + + if ($this->safePageWait(5000, 'document.getElementById("first") !== null')) { + $this->assertEquals('Anket for Konstantin', $webAssert->elementExists('css', 'h1')->getText()); + $this->assertEquals('Firstname: Konstantin', $webAssert->elementExists('css', '#first')->getText()); + $this->assertEquals('Lastname: Kudryashov', $webAssert->elementExists('css', '#last')->getText()); + } + } + + /** + * @dataProvider formSubmitWaysDataProvider + */ + public function testFormSubmitWays($submitVia) + { + $session = $this->getSession(); + $session->visit($this->pathTo('/basic_form.html')); + $page = $session->getPage(); + $webAssert = $this->getAssertSession(); + + $firstname = $webAssert->fieldExists('first_name'); + $firstname->setValue('Konstantin'); + + $page->findButton($submitVia)->click(); + + if ($this->safePageWait(5000, 'document.getElementById("first") !== null')) { + $this->assertEquals('Firstname: Konstantin', $webAssert->elementExists('css', '#first')->getText()); + } else { + $this->fail('Form was never submitted'); + } + } + + public function formSubmitWaysDataProvider() + { + return array( + array('Save'), + array('input-type-image'), + array('button-without-type'), + array('button-type-submit'), + ); + } + + public function testFormSubmit() + { + $session = $this->getSession(); + $session->visit($this->pathTo('/basic_form.html')); + + $webAssert = $this->getAssertSession(); + $webAssert->fieldExists('first_name')->setValue('Konstantin'); + + $webAssert->elementExists('xpath', 'descendant-or-self::form[1]')->submit(); + + if ($this->safePageWait(5000, 'document.getElementById("first") !== null')) { + $this->assertEquals('Firstname: Konstantin', $webAssert->elementExists('css', '#first')->getText()); + }; + } + + public function testFormSubmitWithoutButton() + { + $session = $this->getSession(); + $session->visit($this->pathTo('/form_without_button.html')); + + $webAssert = $this->getAssertSession(); + $webAssert->fieldExists('first_name')->setValue('Konstantin'); + + $webAssert->elementExists('xpath', 'descendant-or-self::form[1]')->submit(); + + if ($this->safePageWait(5000, 'document.getElementById("first") !== null')) { + $this->assertEquals('Firstname: Konstantin', $webAssert->elementExists('css', '#first')->getText()); + }; + } + + public function testBasicGetForm() + { + $this->getSession()->visit($this->pathTo('/basic_get_form.php')); + $webAssert = $this->getAssertSession(); + + $page = $this->getSession()->getPage(); + $this->assertEquals('Basic Get Form Page', $webAssert->elementExists('css', 'h1')->getText()); + + $search = $webAssert->fieldExists('q'); + $search->setValue('some#query'); + $page->pressButton('Find'); + + $div = $webAssert->elementExists('css', 'div'); + $this->assertEquals('some#query', $div->getText()); + } + + public function testAdvancedForm() + { + $this->getSession()->visit($this->pathTo('/advanced_form.html')); + $page = $this->getSession()->getPage(); + + $page->fillField('first_name', 'ever'); + $page->fillField('last_name', 'zet'); + + $page->pressButton('Register'); + + $this->assertContains('no file', $page->getContent()); + + $this->getSession()->visit($this->pathTo('/advanced_form.html')); + + $webAssert = $this->getAssertSession(); + $page = $this->getSession()->getPage(); + $this->assertEquals('ADvanced Form Page', $webAssert->elementExists('css', 'h1')->getText()); + + $firstname = $webAssert->fieldExists('first_name'); + $lastname = $webAssert->fieldExists('lastn'); + $email = $webAssert->fieldExists('Your email:'); + $select = $webAssert->fieldExists('select_number'); + $sex = $webAssert->fieldExists('sex'); + $maillist = $webAssert->fieldExists('mail_list'); + $agreement = $webAssert->fieldExists('agreement'); + $notes = $webAssert->fieldExists('notes'); + $about = $webAssert->fieldExists('about'); + + $this->assertEquals('Firstname', $firstname->getValue()); + $this->assertEquals('Lastname', $lastname->getValue()); + $this->assertEquals('your@email.com', $email->getValue()); + $this->assertEquals('20', $select->getValue()); + $this->assertEquals('w', $sex->getValue()); + $this->assertEquals('original notes', $notes->getValue()); + + $this->assertEquals('on', $maillist->getValue()); + $this->assertNull($agreement->getValue()); + + $this->assertTrue($maillist->isChecked()); + $this->assertFalse($agreement->isChecked()); + + $agreement->check(); + $this->assertTrue($agreement->isChecked()); + + $maillist->uncheck(); + $this->assertFalse($maillist->isChecked()); + + $select->selectOption('thirty'); + $this->assertEquals('30', $select->getValue()); + + $sex->selectOption('m'); + $this->assertEquals('m', $sex->getValue()); + + $notes->setValue('new notes'); + $this->assertEquals('new notes', $notes->getValue()); + + $about->attachFile($this->mapRemoteFilePath(__DIR__ . '/../../web-fixtures/some_file.txt')); + + $button = $page->findButton('Register'); + $this->assertNotNull($button); + + $page->fillField('first_name', 'Foo "item"'); + $page->fillField('last_name', 'Bar'); + $page->fillField('Your email:', 'ever.zet@gmail.com'); + + $this->assertEquals('Foo "item"', $firstname->getValue()); + $this->assertEquals('Bar', $lastname->getValue()); + + $button->press(); + + if ($this->safePageWait(5000, 'document.getElementsByTagName("title") !== null')) { + $out = <<assertContains($out, $page->getContent()); + } + } + + public function testMultiInput() + { + $this->getSession()->visit($this->pathTo('/multi_input_form.html')); + $page = $this->getSession()->getPage(); + $webAssert = $this->getAssertSession(); + $this->assertEquals('Multi input Test', $webAssert->elementExists('css', 'h1')->getText()); + + $first = $webAssert->fieldExists('First'); + $second = $webAssert->fieldExists('Second'); + $third = $webAssert->fieldExists('Third'); + + $this->assertEquals('tag1', $first->getValue()); + $this->assertSame('tag2', $second->getValue()); + $this->assertEquals('tag1', $third->getValue()); + + $first->setValue('tag2'); + $this->assertEquals('tag2', $first->getValue()); + $this->assertSame('tag2', $second->getValue()); + $this->assertEquals('tag1', $third->getValue()); + + $second->setValue('one'); + + $this->assertEquals('tag2', $first->getValue()); + $this->assertSame('one', $second->getValue()); + + $third->setValue('tag3'); + + $this->assertEquals('tag2', $first->getValue()); + $this->assertSame('one', $second->getValue()); + $this->assertEquals('tag3', $third->getValue()); + + $button = $page->findButton('Register'); + $this->assertNotNull($button); + $button->press(); + + $space = ' '; + $out = <<assertContains($out, $page->getContent()); + } + + public function testAdvancedFormSecondSubmit() + { + $this->getSession()->visit($this->pathTo('/advanced_form.html')); + $page = $this->getSession()->getPage(); + + $button = $page->findButton('Login'); + $this->assertNotNull($button); + $button->press(); + + $toSearch = array( + "'agreement' = 'off',", + "'submit' = 'Login',", + 'no file', + ); + + $pageContent = $page->getContent(); + + foreach ($toSearch as $searchString) { + $this->assertContains($searchString, $pageContent); + } + } + + public function testSubmitEmptyTextarea() + { + $this->getSession()->visit($this->pathTo('/empty_textarea.html')); + $page = $this->getSession()->getPage(); + + $page->pressButton('Save'); + + $toSearch = array( + "'textarea' = '',", + "'submit' = 'Save',", + 'no file', + ); + + $pageContent = $page->getContent(); + + foreach ($toSearch as $searchString) { + $this->assertContains($searchString, $pageContent); + } + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/Html5Test.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/Html5Test.php new file mode 100644 index 0000000..b7f4716 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/Html5Test.php @@ -0,0 +1,128 @@ +getSession()->visit($this->pathTo('/html5_form.html')); + $page = $this->getSession()->getPage(); + $webAssert = $this->getAssertSession(); + + $firstName = $webAssert->fieldExists('first_name'); + $lastName = $webAssert->fieldExists('last_name'); + + $this->assertEquals('not set', $lastName->getValue()); + $firstName->setValue('John'); + $lastName->setValue('Doe'); + + $this->assertEquals('Doe', $lastName->getValue()); + + $page->pressButton('Submit in form'); + + if ($this->safePageWait(5000, 'document.getElementsByTagName("title") !== null')) { + $out = <<assertContains($out, $page->getContent()); + $this->assertNotContains('other_field', $page->getContent()); + } + } + + public function testHtml5FormRadioAttribute() + { + $this->getSession()->visit($this->pathTo('html5_radio.html')); + $page = $this->getSession()->getPage(); + + $radio = $this->findById('sex_f'); + $otherRadio = $this->findById('sex_invalid'); + + $this->assertEquals('f', $radio->getValue()); + $this->assertEquals('invalid', $otherRadio->getValue()); + + $radio->selectOption('m'); + + $this->assertEquals('m', $radio->getValue()); + $this->assertEquals('invalid', $otherRadio->getValue()); + + $page->pressButton('Submit in form'); + + $out = <<assertContains($out, $page->getContent()); + } + + public function testHtml5FormButtonAttribute() + { + $this->getSession()->visit($this->pathTo('/html5_form.html')); + $page = $this->getSession()->getPage(); + $webAssert = $this->getAssertSession(); + + $firstName = $webAssert->fieldExists('first_name'); + $lastName = $webAssert->fieldExists('last_name'); + + $firstName->setValue('John'); + $lastName->setValue('Doe'); + + $page->pressButton('Submit outside form'); + + if ($this->safePageWait(5000, 'document.getElementsByTagName("title") !== null')) { + $out = <<assertContains($out, $page->getContent()); + } + } + + public function testHtml5FormOutside() + { + $this->getSession()->visit($this->pathTo('/html5_form.html')); + $page = $this->getSession()->getPage(); + + $page->fillField('other_field', 'hello'); + + $page->pressButton('Submit separate form'); + + if ($this->safePageWait(5000, 'document.getElementsByTagName("title") !== null')) { + $out = <<assertContains($out, $page->getContent()); + $this->assertNotContains('first_name', $page->getContent()); + } + } + + public function testHtml5Types() + { + $this->getSession()->visit($this->pathTo('html5_types.html')); + $page = $this->getSession()->getPage(); + + $page->fillField('url', 'http://mink.behat.org/'); + $page->fillField('email', 'mink@example.org'); + $page->fillField('number', '6'); + $page->fillField('search', 'mink'); + $page->fillField('date', '2014-05-19'); + $page->fillField('color', '#ff00aa'); + + $page->pressButton('Submit'); + + $out = <<assertContains($out, $page->getContent()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/RadioTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/RadioTest.php new file mode 100644 index 0000000..39cd743 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/RadioTest.php @@ -0,0 +1,84 @@ +getSession()->visit($this->pathTo('radio.html')); + } + + public function testIsChecked() + { + $option = $this->findById('first'); + $option2 = $this->findById('second'); + + $this->assertTrue($option->isChecked()); + $this->assertFalse($option2->isChecked()); + + $option2->selectOption('updated'); + + $this->assertFalse($option->isChecked()); + $this->assertTrue($option2->isChecked()); + } + + public function testSelectOption() + { + $option = $this->findById('first'); + + $this->assertEquals('set', $option->getValue()); + + $option->selectOption('updated'); + + $this->assertEquals('updated', $option->getValue()); + + $option->selectOption('set'); + + $this->assertEquals('set', $option->getValue()); + } + + public function testSetValue() + { + $option = $this->findById('first'); + + $this->assertEquals('set', $option->getValue()); + + $option->setValue('updated'); + + $this->assertEquals('updated', $option->getValue()); + $this->assertFalse($option->isChecked()); + } + + public function testSameNameInMultipleForms() + { + $option1 = $this->findById('reused_form1'); + $option2 = $this->findById('reused_form2'); + + $this->assertEquals('test2', $option1->getValue()); + $this->assertEquals('test3', $option2->getValue()); + + $option1->selectOption('test'); + + $this->assertEquals('test', $option1->getValue()); + $this->assertEquals('test3', $option2->getValue()); + } + + /** + * @see https://github.com/Behat/MinkSahiDriver/issues/32 + */ + public function testSetValueXPathEscaping() + { + $session = $this->getSession(); + $session->visit($this->pathTo('/advanced_form.html')); + $page = $session->getPage(); + + $sex = $page->find('xpath', '//*[@name = "sex"]' . "\n|\n" . '//*[@id = "sex"]'); + $this->assertNotNull($sex, 'xpath with line ending works'); + + $sex->setValue('m'); + $this->assertEquals('m', $sex->getValue(), 'no double xpath escaping during radio button value change'); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Form/SelectTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Form/SelectTest.php new file mode 100644 index 0000000..492625a --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Form/SelectTest.php @@ -0,0 +1,135 @@ +getSession()->visit($this->pathTo('/multiselect_form.html')); + $webAssert = $this->getAssertSession(); + $page = $this->getSession()->getPage(); + $this->assertEquals('Multiselect Test', $webAssert->elementExists('css', 'h1')->getText()); + + $select = $webAssert->fieldExists('select_number'); + $multiSelect = $webAssert->fieldExists('select_multiple_numbers[]'); + $secondMultiSelect = $webAssert->fieldExists('select_multiple_values[]'); + + $this->assertEquals('20', $select->getValue()); + $this->assertSame(array(), $multiSelect->getValue()); + $this->assertSame(array('2', '3'), $secondMultiSelect->getValue()); + + $select->selectOption('thirty'); + $this->assertEquals('30', $select->getValue()); + + $multiSelect->selectOption('one', true); + + $this->assertSame(array('1'), $multiSelect->getValue()); + + $multiSelect->selectOption('three', true); + + $this->assertEquals(array('1', '3'), $multiSelect->getValue()); + + $secondMultiSelect->selectOption('two'); + $this->assertSame(array('2'), $secondMultiSelect->getValue()); + + $button = $page->findButton('Register'); + $this->assertNotNull($button); + $button->press(); + + $space = ' '; + $out = <<assertContains($out, $page->getContent()); + } + + /** + * @dataProvider testElementSelectedStateCheckDataProvider + */ + public function testElementSelectedStateCheck($selectName, $optionValue, $optionText) + { + $session = $this->getSession(); + $webAssert = $this->getAssertSession(); + $session->visit($this->pathTo('/multiselect_form.html')); + $select = $webAssert->fieldExists($selectName); + + $optionValueEscaped = $session->getSelectorsHandler()->xpathLiteral($optionValue); + $option = $webAssert->elementExists('named', array('option', $optionValueEscaped)); + + $this->assertFalse($option->isSelected()); + $select->selectOption($optionText); + $this->assertTrue($option->isSelected()); + } + + public function testElementSelectedStateCheckDataProvider() + { + return array( + array('select_number', '30', 'thirty'), + array('select_multiple_numbers[]', '2', 'two'), + ); + } + + public function testSetValueSingleSelect() + { + $session = $this->getSession(); + $session->visit($this->pathTo('/multiselect_form.html')); + $select = $this->getAssertSession()->fieldExists('select_number'); + + $select->setValue('10'); + $this->assertEquals('10', $select->getValue()); + } + + public function testSetValueMultiSelect() + { + $session = $this->getSession(); + $session->visit($this->pathTo('/multiselect_form.html')); + $select = $this->getAssertSession()->fieldExists('select_multiple_values[]'); + + $select->setValue(array('1', '2')); + $this->assertEquals(array('1', '2'), $select->getValue()); + } + + /** + * @see https://github.com/Behat/Mink/issues/193 + */ + public function testOptionWithoutValue() + { + $session = $this->getSession(); + $session->visit($this->pathTo('/issue193.html')); + + $session->getPage()->selectFieldOption('options-without-values', 'Two'); + $this->assertEquals('Two', $this->findById('options-without-values')->getValue()); + + $this->assertTrue($this->findById('two')->isSelected()); + $this->assertFalse($this->findById('one')->isSelected()); + + $session->getPage()->selectFieldOption('options-with-values', 'two'); + $this->assertEquals('two', $this->findById('options-with-values')->getValue()); + } + + /** + * @see https://github.com/Behat/Mink/issues/131 + */ + public function testAccentuatedOption() + { + $this->getSession()->visit($this->pathTo('/issue131.html')); + $page = $this->getSession()->getPage(); + + $page->selectFieldOption('foobar', 'Gimme some accentués characters'); + + $this->assertEquals('1', $page->findField('foobar')->getValue()); + } +} diff --git a/core/vendor/behat/mink/driver-testsuite/tests/Js/ChangeEventTest.php b/core/vendor/behat/mink/driver-testsuite/tests/Js/ChangeEventTest.php new file mode 100644 index 0000000..d1fd3f1 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/tests/Js/ChangeEventTest.php @@ -0,0 +1,152 @@ + in a + + + + + + + + + + + + + + + + + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form_post.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form_post.php new file mode 100644 index 0000000..755806d --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/advanced_form_post.php @@ -0,0 +1,26 @@ + + + + Advanced form save + + + +', '', var_export($_POST, true)) . "\n"; +if (isset($_FILES['about']) && file_exists($_FILES['about']['tmp_name'])) { + echo $_FILES['about']['name'] . "\n"; + echo file_get_contents($_FILES['about']['tmp_name']); +} else { + echo "no file"; +} +?> + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/aria_roles.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/aria_roles.html new file mode 100644 index 0000000..7074e85 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/aria_roles.html @@ -0,0 +1,30 @@ + + + + ARIA roles test + + + + This page tests selected ARIA roles
+ (see http://www.w3.org/TR/wai-aria/) + +
Toggle
+ + + + + + + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_auth.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_auth.php new file mode 100644 index 0000000..9620a0e --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_auth.php @@ -0,0 +1,12 @@ + + + + Basic Form + + + +

Basic Form Page

+ +
+ + + + + + + + + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form_post.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form_post.php new file mode 100644 index 0000000..8a5e340 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_form_post.php @@ -0,0 +1,13 @@ + + + + Basic Form Saving + + + +

Anket for

+ + Firstname: + Lastname: + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_get_form.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_get_form.php new file mode 100644 index 0000000..a0b3516 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/basic_get_form.php @@ -0,0 +1,20 @@ + + + + Basic Get Form + + + +

Basic Get Form Page

+ +
+ +
+ +
+ + + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page1.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page1.php new file mode 100644 index 0000000..7f128ca --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page1.php @@ -0,0 +1,21 @@ + + + + + Basic Form + + + + Basic Page With Cookie Set from Server Side + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page2.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page2.php new file mode 100644 index 0000000..22bcd1b --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page2.php @@ -0,0 +1,10 @@ + + + + Basic Form + + + + Previous cookie: + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page3.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page3.php new file mode 100644 index 0000000..caa28bc --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/cookie_page3.php @@ -0,0 +1,16 @@ + + + + + HttpOnly Cookie Test + + + + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/css_mouse_events.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/css_mouse_events.html new file mode 100644 index 0000000..750ca54 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/css_mouse_events.html @@ -0,0 +1,36 @@ + + + + CSS Mouse Events Testing + + + + + + +
+
reset
+
mouse action
+
+ + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/element_change_detector.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/element_change_detector.html new file mode 100644 index 0000000..af3032a --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/element_change_detector.html @@ -0,0 +1,65 @@ + + + + ADvanced Form + + + + +

ADvanced Form Page

+ +
+ + + + + + + + + + + + + + +
+ +
    +
  • for easy element location
  • +
+ + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/empty_textarea.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/empty_textarea.html new file mode 100644 index 0000000..f0779ee --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/empty_textarea.html @@ -0,0 +1,14 @@ + + + + Empty textarea submission + + + +

Empty textarea submission

+
+ + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/form_without_button.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/form_without_button.html new file mode 100644 index 0000000..8063026 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/form_without_button.html @@ -0,0 +1,13 @@ + + + + + Form submission without button test + + +
+ + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/headers.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/headers.php new file mode 100644 index 0000000..25d9b90 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/headers.php @@ -0,0 +1,10 @@ + + + + Headers page + + + + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_form.html new file mode 100644 index 0000000..9070353 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_form.html @@ -0,0 +1,18 @@ + + + + + HTML5 form attribute test + + +
+ + + + +
+ + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_radio.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_radio.html new file mode 100644 index 0000000..fd54c2d --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_radio.html @@ -0,0 +1,16 @@ + + + + + HTML5 form attribute test + + +
+ + + +
+ +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_types.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_types.html new file mode 100644 index 0000000..bd46cfa --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html5_types.html @@ -0,0 +1,18 @@ + + + + + HTML5 form attribute test + + +
+ + + + + + + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/html_decoding.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html_decoding.html new file mode 100644 index 0000000..341f226 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/html_decoding.html @@ -0,0 +1,17 @@ + + + + HTML Decoding Test + + +
+ some text + +
+ + + +
+
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe.html new file mode 100644 index 0000000..c54797f --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe.html @@ -0,0 +1,12 @@ + + + + + + +
+ Main window div text +
+ + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe_inner.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe_inner.html new file mode 100644 index 0000000..512f058 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/iframe_inner.html @@ -0,0 +1,10 @@ + + + + +
+ iFrame div text +
+ + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/index.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/index.html new file mode 100644 index 0000000..6bb379c --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/index.html @@ -0,0 +1,51 @@ + + + + Index page + + + +

Extremely useless page

+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +
+ some
very +
+interesting text +
+ +
+ + + + +
+ +
+
el1
+
el2
+ +
+ +
el4
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue130.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue130.php new file mode 100644 index 0000000..201d982 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue130.php @@ -0,0 +1,11 @@ + + + + Go to 2'; + } else { + echo ''.$_SERVER['HTTP_REFERER'].''; + } + ?> + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue131.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue131.html new file mode 100644 index 0000000..fa3427a --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue131.html @@ -0,0 +1,17 @@ + + + + Issue 131 + + + +

There is a non breaking space

+
Some accentués characters
+
+ + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue140.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue140.php new file mode 100644 index 0000000..04a4caf --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue140.php @@ -0,0 +1,16 @@ + + + + +
+ + +
+ diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue178.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue178.html new file mode 100644 index 0000000..3efc743 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue178.html @@ -0,0 +1,19 @@ + + + + + Index page + + + +
+
+ +
+
+ +
+ + + \ No newline at end of file diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue193.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue193.html new file mode 100644 index 0000000..e722a43 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue193.html @@ -0,0 +1,26 @@ + + + + Index page + + + + +
+ + + +
+ + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue211.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue211.html new file mode 100644 index 0000000..bb977ec --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue211.html @@ -0,0 +1,23 @@ + + + + Index page + + + + +
+

+ + +

+

+ + +

+ + +
+ + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue212.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue212.html new file mode 100644 index 0000000..24ae62f --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue212.html @@ -0,0 +1,9 @@ + + + +
+ + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue215.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue215.html new file mode 100644 index 0000000..adff3fb --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue215.html @@ -0,0 +1,17 @@ + + + + + Index page + + + +
+ +
+ + + \ No newline at end of file diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue225.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue225.html new file mode 100644 index 0000000..2178bba --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue225.html @@ -0,0 +1,20 @@ + + + + Index page + + + + + + + +
+ + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue255.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue255.html new file mode 100644 index 0000000..d56a427 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/issue255.html @@ -0,0 +1,33 @@ + + + + Issue 255 + + + + +
+ + + + +

+

+
+ + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-1.6.2-min.js b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-1.6.2-min.js new file mode 100644 index 0000000..8cdc80e --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-1.6.2-min.js @@ -0,0 +1,18 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i. +shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j +)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-ui-1.8.14.custom.min.js b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-ui-1.8.14.custom.min.js new file mode 100644 index 0000000..1764e11 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/js/jquery-ui-1.8.14.custom.min.js @@ -0,0 +1,127 @@ +/*! + * jQuery UI 1.8.14 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.14", +keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus(); +b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this, +"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection", +function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth, +outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,"tabindex"),d=isNaN(b); +return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e= +0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted= +false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); +;/* + * jQuery UI Draggable 1.8.14 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== +"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= +this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;this.helper= +this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}); +this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true}, +_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b= +false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration, +10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle|| +!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&& +a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent= +this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"), +10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"), +10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top, +(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!= +"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"), +10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+ +this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&& +!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.leftg[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.topg[3])?h:!(h-this.offset.click.topg[2])?e:!(e-this.offset.click.left=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>= +i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f + + + JS elements test + + + + + +
+
not clicked
+
no mouse action detected
+ + + + + +
+
+ +
+ +
+

Drop here

+
+ +
+ + + + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/json.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/json.php new file mode 100644 index 0000000..173d358 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/json.php @@ -0,0 +1,7 @@ + 'val1', + 'key2' => 234, + 'key3' => array(1, 2, 3) +)); diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/links.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/links.html new file mode 100644 index 0000000..ec626bf --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/links.html @@ -0,0 +1,15 @@ + + + + Links page + + + + Redirect me to + Random number page + Link with a ' + + basic form image + + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/multi_input_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multi_input_form.html new file mode 100644 index 0000000..600a500 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multi_input_form.html @@ -0,0 +1,27 @@ + + + + Multi input Test + + + +

Multi input Test

+ +
+ + + + + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/multicheckbox_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multicheckbox_form.html new file mode 100644 index 0000000..a2ae375 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multicheckbox_form.html @@ -0,0 +1,17 @@ + + + + Multicheckbox Test + + + +

Multicheckbox Test

+ +
+ + + + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/multiselect_form.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multiselect_form.html new file mode 100644 index 0000000..0d28986 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/multiselect_form.html @@ -0,0 +1,32 @@ + + + + Multiselect Test + + + +

Multiselect Test

+ +
+ + + + + + + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup1.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup1.html new file mode 100644 index 0000000..b8e3fe2 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup1.html @@ -0,0 +1,13 @@ + + + + popup_1 + + + +
+ Popup#1 div text +
+ + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup2.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup2.html new file mode 100644 index 0000000..dae1932 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/popup2.html @@ -0,0 +1,13 @@ + + + + popup_2 + + + +
+ Popup#2 div text +
+ + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/print_cookies.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/print_cookies.php new file mode 100644 index 0000000..eef496e --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/print_cookies.php @@ -0,0 +1,10 @@ + + + + Cookies page + + + + ', '', var_export($_COOKIE, true)); ?> + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/radio.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/radio.html new file mode 100644 index 0000000..69a916a --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/radio.html @@ -0,0 +1,21 @@ + + + + + Radio group + + +
+ + + + + +
+ +
+ + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/randomizer.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/randomizer.php new file mode 100644 index 0000000..07a73ec --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/randomizer.php @@ -0,0 +1,12 @@ + + + + Index page + + + + +

+ + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirect_destination.html b/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirect_destination.html new file mode 100644 index 0000000..3de7481 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirect_destination.html @@ -0,0 +1,10 @@ + + + + Redirect destination + + + + You were redirected! + + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirector.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirector.php new file mode 100644 index 0000000..44ac8f3 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/redirector.php @@ -0,0 +1,3 @@ + + + + + Response headers + + + +

Response headers

+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/session_test.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/session_test.php new file mode 100644 index 0000000..df1af6f --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/session_test.php @@ -0,0 +1,18 @@ + + + + + Session Test + + + +
+ + diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/some_file.txt b/core/vendor/behat/mink/driver-testsuite/web-fixtures/some_file.txt new file mode 100644 index 0000000..d515a02 --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/some_file.txt @@ -0,0 +1 @@ +1 uploaded file diff --git a/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page1.php b/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page1.php new file mode 100644 index 0000000..750249b --- /dev/null +++ b/core/vendor/behat/mink/driver-testsuite/web-fixtures/sub-folder/cookie_page1.php @@ -0,0 +1,4 @@ + + + + + + + + Popup #1 + + + + Popup #2 + + +
+ Main window div text +
+ + + diff --git a/core/vendor/behat/mink/phpdoc.ini.dist b/core/vendor/behat/mink/phpdoc.ini.dist new file mode 100644 index 0000000..3fef75d --- /dev/null +++ b/core/vendor/behat/mink/phpdoc.ini.dist @@ -0,0 +1,125 @@ +; Default configuration file for PHPDoctor + +; This config file will cause PHPDoctor to generate API documentation of +; itself. + + +; PHPDoctor settings +; ----------------------------------------------------------------------------- + +; Names of files to parse. This can be a single filename, or a comma separated +; list of filenames. Wildcards are allowed. + +files = "*.php" + +; Names of files or directories to ignore. This can be a single filename, or a +; comma separated list of filenames. Wildcards are NOT allowed. + +ignore = "CVS, .svn, .git, _compiled" + +; The directory to look for files in, if not used the PHPDoctor will look in +; the current directory (the directory it is run from). + +source_path = "./src" + +; If you do not want PHPDoctor to look in each sub directory for files +; uncomment this line. + +;subdirs = off + +; Set how loud PHPDoctor is as it runs. Quiet mode suppresses all output other +; than warnings and errors. Verbose mode outputs additional messages during +; execution. + +;quiet = on +;verbose = on + +; Select the doclet to use for generating output. + +doclet = standard +;doclet = debug + +; The directory to find the doclet in. Doclets are expected to be in a +; directory named after themselves at the location given. + +;doclet_path = ./doclets + +; The directory to find taglets in. Taglets allow you to make PHPDoctor handle +; new tags and to alter the behavour of existing tags and their output. + +;taglet_path = ./taglets + +; If the code you are parsing does not use package tags or not all elements +; have package tags, use this setting to place unbound elements into a +; particular package. + +default_package = "Behat\Mink" + +; Specifies the name of a HTML file containing text for the overview +; documentation to be placed on the overview page. The path is relative to +; "source_path" unless an absolute path is given. + +overview = readme.html + +; Package comments will be looked for in a file named package.html in the same +; directory as the first source file parsed in that package or in the directory +; given below. If package comments are placed in the directory given below then +; they should be named ".html". + +package_comment_dir = ./ + +; Parse out global variables and/or global constants? + +;globals = off +;constants = off + +; Generate documentation for all class members + +;private = on + +; Generate documentation for public and protected class members + +;protected = on + +; Generate documentation for only public class members + +public = on + +; Use the PEAR compatible handling of the docblock first sentence + +;pear_compat = on + +; Standard doclet settings +; ----------------------------------------------------------------------------- + +; The directory to place generated documentation in. If the given path is +; relative to it will be relative to "source_path". + +d = "api" + +; Specifies the title to be placed in the HTML tag. + +windowtitle = "Behat\Mink" + +; Specifies the title to be placed near the top of the overview summary file. + +doctitle = "Behat\Mink: browser emulators abstraction library for PHP" + +; Specifies the header text to be placed at the top of each output file. The +; header will be placed to the right of the upper navigation bar. + +header = "Behat\Mink" + +; Specifies the footer text to be placed at the bottom of each output file. The +; footer will be placed to the right of the lower navigation bar. + +footer = "Behat\Mink" + +; Specifies the text to be placed at the bottom of each output file. The text +; will be placed at the bottom of the page, below the lower navigation bar. + +;bottom = "This document was generated by <a href="http://phpdoctor.sourceforge.net/">PHPDoctor: The PHP Documentation Creator</a>" + +; Create a class tree? + +tree = on diff --git a/core/vendor/behat/mink/phpunit.xml.dist b/core/vendor/behat/mink/phpunit.xml.dist new file mode 100644 index 0000000..bf73a69 --- /dev/null +++ b/core/vendor/behat/mink/phpunit.xml.dist @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit colors="true" bootstrap="vendor/autoload.php"> + <testsuites> + <testsuite name="Behat Mink test suite"> + <directory>tests</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory>./src/Behat/Mink/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/core/vendor/behat/mink/src/Behat/Mink/Driver/CoreDriver.php b/core/vendor/behat/mink/src/Behat/Mink/Driver/CoreDriver.php new file mode 100644 index 0000000..248f291 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Driver/CoreDriver.php @@ -0,0 +1,447 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Driver; + +use Behat\Mink\Exception\UnsupportedDriverActionException; +use Behat\Mink\Session; + +/** + * Core driver. + * All other drivers should extend this class for future compatibility. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +abstract class CoreDriver implements DriverInterface +{ + /** + * {@inheritdoc} + */ + public function setSession(Session $session) + { + throw new UnsupportedDriverActionException('Setting the session is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function start() + { + throw new UnsupportedDriverActionException('Starting the driver is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function isStarted() + { + throw new UnsupportedDriverActionException('Checking the driver state is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function stop() + { + throw new UnsupportedDriverActionException('Stopping the driver is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + throw new UnsupportedDriverActionException('Resetting the driver is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function visit($url) + { + throw new UnsupportedDriverActionException('Visiting an url is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getCurrentUrl() + { + throw new UnsupportedDriverActionException('Getting the current url is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getContent() + { + throw new UnsupportedDriverActionException('Getting the page content is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function find($xpath) + { + throw new UnsupportedDriverActionException('Finding elements is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getTagName($xpath) + { + throw new UnsupportedDriverActionException('Getting the tag name is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getText($xpath) + { + throw new UnsupportedDriverActionException('Getting the element text is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getHtml($xpath) + { + throw new UnsupportedDriverActionException('Getting the element inner HTML is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getOuterHtml($xpath) + { + throw new UnsupportedDriverActionException('Getting the element outer HTML is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getAttribute($xpath, $name) + { + throw new UnsupportedDriverActionException('Getting the element attribute is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getValue($xpath) + { + throw new UnsupportedDriverActionException('Getting the field value is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function setValue($xpath, $value) + { + throw new UnsupportedDriverActionException('Setting the field value is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function check($xpath) + { + throw new UnsupportedDriverActionException('Checking a checkbox is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function uncheck($xpath) + { + throw new UnsupportedDriverActionException('Unchecking a checkbox is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function isChecked($xpath) + { + throw new UnsupportedDriverActionException('Getting the state of a checkbox is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function selectOption($xpath, $value, $multiple = false) + { + throw new UnsupportedDriverActionException('Selecting an option is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function click($xpath) + { + throw new UnsupportedDriverActionException('Clicking on an element is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function attachFile($xpath, $path) + { + throw new UnsupportedDriverActionException('Attaching a file in an input is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function reload() + { + throw new UnsupportedDriverActionException('Page reloading is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function forward() + { + throw new UnsupportedDriverActionException('Forward action is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function back() + { + throw new UnsupportedDriverActionException('Backward action is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function setBasicAuth($user, $password) + { + throw new UnsupportedDriverActionException('Basic auth setup is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function switchToWindow($name = null) + { + throw new UnsupportedDriverActionException('Windows management is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function switchToIFrame($name = null) + { + throw new UnsupportedDriverActionException('iFrames management is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function setRequestHeader($name, $value) + { + throw new UnsupportedDriverActionException('Request headers manipulation is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getResponseHeaders() + { + throw new UnsupportedDriverActionException('Response headers are not available from %s', $this); + } + + /** + * {@inheritdoc} + */ + public function setCookie($name, $value = null) + { + throw new UnsupportedDriverActionException('Cookies manipulation is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getCookie($name) + { + throw new UnsupportedDriverActionException('Cookies are not available from %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getStatusCode() + { + throw new UnsupportedDriverActionException('Status code is not available from %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getScreenshot() + { + throw new UnsupportedDriverActionException('Screenshots are not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getWindowNames() + { + throw new UnsupportedDriverActionException('Listing all window names is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function getWindowName() + { + throw new UnsupportedDriverActionException('Listing this window name is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function doubleClick($xpath) + { + throw new UnsupportedDriverActionException('Double-clicking is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function rightClick($xpath) + { + throw new UnsupportedDriverActionException('Right-clicking is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function isVisible($xpath) + { + throw new UnsupportedDriverActionException('Element visibility check is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function isSelected($xpath) + { + throw new UnsupportedDriverActionException('Element selection check is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function mouseOver($xpath) + { + throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function focus($xpath) + { + throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function blur($xpath) + { + throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function keyPress($xpath, $char, $modifier = null) + { + throw new UnsupportedDriverActionException('Keyboard manipulations are not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function keyDown($xpath, $char, $modifier = null) + { + throw new UnsupportedDriverActionException('Keyboard manipulations are not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function keyUp($xpath, $char, $modifier = null) + { + throw new UnsupportedDriverActionException('Keyboard manipulations are not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function dragTo($sourceXpath, $destinationXpath) + { + throw new UnsupportedDriverActionException('Mouse manipulations are not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function executeScript($script) + { + throw new UnsupportedDriverActionException('JS is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function evaluateScript($script) + { + throw new UnsupportedDriverActionException('JS is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function wait($timeout, $condition) + { + throw new UnsupportedDriverActionException('JS is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function resizeWindow($width, $height, $name = null) + { + throw new UnsupportedDriverActionException('Window resizing is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function maximizeWindow($name = null) + { + throw new UnsupportedDriverActionException('Window maximize is not supported by %s', $this); + } + + /** + * {@inheritdoc} + */ + public function submitForm($xpath) + { + throw new UnsupportedDriverActionException('Form submission is not supported by %s', $this); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Driver/DriverInterface.php b/core/vendor/behat/mink/src/Behat/Mink/Driver/DriverInterface.php new file mode 100644 index 0000000..831d408 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Driver/DriverInterface.php @@ -0,0 +1,637 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Driver; + +use Behat\Mink\Element\NodeElement; +use Behat\Mink\Exception\DriverException; +use Behat\Mink\Exception\UnsupportedDriverActionException; +use Behat\Mink\Session; + +/** + * Driver interface. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +interface DriverInterface +{ + /** + * Sets driver's current session. + * + * @param Session $session + */ + public function setSession(Session $session); + + /** + * Starts driver. + * + * Once started, the driver should be ready to visit a page. + * + * Calling any action before visiting a page is an undefined behavior. + * The only supported method calls on a fresh driver are + * - visit() + * - setRequestHeader() + * - setBasicAuth() + * - reset() + * - stop() + * + * Calling start on a started driver is an undefined behavior. Driver + * implementations are free to handle it silently or to fail with an + * exception. + * + * @throws DriverException When the driver cannot be started + */ + public function start(); + + /** + * Checks whether driver is started. + * + * @return Boolean + */ + public function isStarted(); + + /** + * Stops driver. + * + * Once stopped, the driver should be started again before using it again. + * + * Calling any action on a stopped driver is an undefined behavior. + * The only supported method call after stopping a driver is starting it again. + * + * Calling stop on a stopped driver is an undefined behavior. Driver + * implementations are free to handle it silently or to fail with an + * exception. + * + * @throws DriverException When the driver cannot be closed + */ + public function stop(); + + /** + * Resets driver state. + * + * This should reset cookies, request headers and basic authentication. + * When possible, the history should be reset as well, but this is not enforced + * as some implementations may not be able to reset it without restarting the + * driver entirely. Consumers requiring a clean history should restart the driver + * to enforce it. + * + * Once reset, the driver should be ready to visit a page. + * Calling any action before visiting a page is an undefined behavior. + * The only supported method calls on a fresh driver are + * - visit() + * - setRequestHeader() + * - setBasicAuth() + * - reset() + * - stop() + * + * Calling reset on a stopped driver is an undefined behavior. + */ + public function reset(); + + /** + * Visit specified URL. + * + * @param string $url url of the page + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function visit($url); + + /** + * Returns current URL address. + * + * @return string + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getCurrentUrl(); + + /** + * Reloads current page. + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function reload(); + + /** + * Moves browser forward 1 page. + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function forward(); + + /** + * Moves browser backward 1 page. + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function back(); + + /** + * Sets HTTP Basic authentication parameters + * + * @param string|Boolean $user user name or false to disable authentication + * @param string $password password + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function setBasicAuth($user, $password); + + /** + * Switches to specific browser window. + * + * @param string $name window name (null for switching back to main window) + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function switchToWindow($name = null); + + /** + * Switches to specific iFrame. + * + * @param string $name iframe name (null for switching back) + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function switchToIFrame($name = null); + + /** + * Sets specific request header on client. + * + * @param string $name + * @param string $value + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function setRequestHeader($name, $value); + + /** + * Returns last response headers. + * + * @return array + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getResponseHeaders(); + + /** + * Sets cookie. + * + * @param string $name + * @param string $value + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function setCookie($name, $value = null); + + /** + * Returns cookie by name. + * + * @param string $name + * + * @return string|null + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getCookie($name); + + /** + * Returns last response status code. + * + * @return integer + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getStatusCode(); + + /** + * Returns last response content. + * + * @return string + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getContent(); + + /** + * Capture a screenshot of the current window. + * + * @return string screenshot of MIME type image/* depending + * on driver (e.g., image/png, image/jpeg) + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getScreenshot(); + + /** + * Return the names of all open windows. + * + * @return array array of all open windows + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getWindowNames(); + + /** + * Return the name of the currently active window. + * + * @return string the name of the current window + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getWindowName(); + + /** + * Finds elements with specified XPath query. + * + * @param string $xpath + * + * @return NodeElement[] + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function find($xpath); + + /** + * Returns element's tag name by it's XPath query. + * + * @param string $xpath + * + * @return string + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getTagName($xpath); + + /** + * Returns element's text by it's XPath query. + * + * @param string $xpath + * + * @return string + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getText($xpath); + + /** + * Returns element's inner html by it's XPath query. + * + * @param string $xpath + * + * @return string + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getHtml($xpath); + + /** + * Returns element's outer html by it's XPath query. + * + * @param string $xpath + * + * @return string + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getOuterHtml($xpath); + + /** + * Returns element's attribute by it's XPath query. + * + * @param string $xpath + * @param string $name + * + * @return string|null + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function getAttribute($xpath, $name); + + /** + * Returns element's value by it's XPath query. + * + * @param string $xpath + * + * @return string|bool|array + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::getValue + */ + public function getValue($xpath); + + /** + * Sets element's value by it's XPath query. + * + * @param string $xpath + * @param string|bool|array $value + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::setValue + */ + public function setValue($xpath, $value); + + /** + * Checks checkbox by it's XPath query. + * + * @param string $xpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::check + */ + public function check($xpath); + + /** + * Unchecks checkbox by it's XPath query. + * + * @param string $xpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::uncheck + */ + public function uncheck($xpath); + + /** + * Checks whether checkbox or radio button located by it's XPath query is checked. + * + * @param string $xpath + * + * @return Boolean + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::isChecked + */ + public function isChecked($xpath); + + /** + * Selects option from select field or value in radio group located by it's XPath query. + * + * @param string $xpath + * @param string $value + * @param Boolean $multiple + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::selectOption + */ + public function selectOption($xpath, $value, $multiple = false); + + /** + * Checks whether select option, located by it's XPath query, is selected. + * + * @param string $xpath + * + * @return Boolean + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::isSelected + */ + public function isSelected($xpath); + + /** + * Clicks button or link located by it's XPath query. + * + * @param string $xpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function click($xpath); + + /** + * Double-clicks button or link located by it's XPath query. + * + * @param string $xpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function doubleClick($xpath); + + /** + * Right-clicks button or link located by it's XPath query. + * + * @param string $xpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function rightClick($xpath); + + /** + * Attaches file path to file field located by it's XPath query. + * + * @param string $xpath + * @param string $path + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::attachFile + */ + public function attachFile($xpath, $path); + + /** + * Checks whether element visible located by it's XPath query. + * + * @param string $xpath + * + * @return Boolean + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function isVisible($xpath); + + /** + * Simulates a mouse over on the element. + * + * @param string $xpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function mouseOver($xpath); + + /** + * Brings focus to element. + * + * @param string $xpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function focus($xpath); + + /** + * Removes focus from element. + * + * @param string $xpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function blur($xpath); + + /** + * Presses specific keyboard key. + * + * @param string $xpath + * @param string|integer $char could be either char ('b') or char-code (98) + * @param string $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta') + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function keyPress($xpath, $char, $modifier = null); + + /** + * Pressed down specific keyboard key. + * + * @param string $xpath + * @param string|integer $char could be either char ('b') or char-code (98) + * @param string $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta') + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function keyDown($xpath, $char, $modifier = null); + + /** + * Pressed up specific keyboard key. + * + * @param string $xpath + * @param string|integer $char could be either char ('b') or char-code (98) + * @param string $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta') + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function keyUp($xpath, $char, $modifier = null); + + /** + * Drag one element onto another. + * + * @param string $sourceXpath + * @param string $destinationXpath + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function dragTo($sourceXpath, $destinationXpath); + + /** + * Executes JS script. + * + * @param string $script + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function executeScript($script); + + /** + * Evaluates JS script. + * + * The "return" keyword is optional in the script passed as argument. Driver implementations + * must accept the expression both with and without the keyword. + * + * @param string $script + * + * @return mixed + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function evaluateScript($script); + + /** + * Waits some time or until JS condition turns true. + * + * @param integer $timeout timeout in milliseconds + * @param string $condition JS condition + * + * @return boolean + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function wait($timeout, $condition); + + /** + * Set the dimensions of the window. + * + * @param integer $width set the window width, measured in pixels + * @param integer $height set the window height, measured in pixels + * @param string $name window name (null for the main window) + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function resizeWindow($width, $height, $name = null); + + /** + * Maximize the window if it is not maximized already + * + * @param string $name window name (null for the main window) + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + */ + public function maximizeWindow($name = null); + + /** + * Submits the form. + * + * @param string $xpath Xpath. + * + * @throws UnsupportedDriverActionException When operation not supported by the driver + * @throws DriverException When the operation cannot be done + * + * @see \Behat\Mink\Element\NodeElement::submitForm + */ + public function submitForm($xpath); +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/DocumentElement.php b/core/vendor/behat/mink/src/Behat/Mink/Element/DocumentElement.php new file mode 100644 index 0000000..dc6bc6e --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Element/DocumentElement.php @@ -0,0 +1,53 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Element; + +/** + * Document element. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class DocumentElement extends TraversableElement +{ + /** + * Returns XPath for handled element. + * + * @return string + */ + public function getXpath() + { + return '//html'; + } + + /** + * Returns document content. + * + * @return string + */ + public function getContent() + { + return trim($this->getDriver()->getContent()); + } + + /** + * Check whether document has specified content. + * + * @param string $content + * + * @return Boolean + */ + public function hasContent($content) + { + return $this->has('named', array( + 'content', $this->getSelectorsHandler()->xpathLiteral($content) + )); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/Element.php b/core/vendor/behat/mink/src/Behat/Mink/Element/Element.php new file mode 100644 index 0000000..07e1e11 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Element/Element.php @@ -0,0 +1,207 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Element; + +use Behat\Mink\Driver\DriverInterface; +use Behat\Mink\Exception\ElementNotFoundException; +use Behat\Mink\Selector\SelectorsHandler; +use Behat\Mink\Selector\Xpath\Manipulator; +use Behat\Mink\Session; + +/** + * Base element. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +abstract class Element implements ElementInterface +{ + /** + * @var Session + */ + private $session; + + /** + * Driver. + * + * @var DriverInterface + */ + private $driver; + + /** + * @var SelectorsHandler + */ + private $selectorsHandler; + + /** + * @var Manipulator + */ + private $xpathManipulator; + + /** + * Initialize element. + * + * @param Session $session + */ + public function __construct(Session $session) + { + $this->xpathManipulator = new Manipulator(); + $this->session = $session; + + $this->driver = $session->getDriver(); + $this->selectorsHandler = $session->getSelectorsHandler(); + } + + /** + * Returns element session. + * + * @return Session + * + * @deprecated Accessing the session from the element is deprecated as of 1.6 and will be impossible in 2.0. + */ + public function getSession() + { + return $this->session; + } + + /** + * Returns element's driver. + * + * @return DriverInterface + */ + protected function getDriver() + { + return $this->driver; + } + + /** + * Returns selectors handler. + * + * @return SelectorsHandler + */ + protected function getSelectorsHandler() + { + return $this->selectorsHandler; + } + + /** + * {@inheritdoc} + */ + public function has($selector, $locator) + { + return null !== $this->find($selector, $locator); + } + + /** + * {@inheritdoc} + */ + public function isValid() + { + return 1 === count($this->getDriver()->find($this->getXpath())); + } + + /** + * {@inheritdoc} + */ + public function waitFor($timeout, $callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Given callback is not a valid callable'); + } + + $start = microtime(true); + $end = $start + $timeout; + + do { + $result = call_user_func($callback, $this); + + if ($result) { + break; + } + + usleep(100000); + } while (microtime(true) < $end); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function find($selector, $locator) + { + $items = $this->findAll($selector, $locator); + + return count($items) ? current($items) : null; + } + + /** + * {@inheritdoc} + */ + public function findAll($selector, $locator) + { + if ('named' === $selector) { + $items = $this->findAll('named_exact', $locator); + if (empty($items)) { + $items = $this->findAll('named_partial', $locator); + } + + return $items; + } + + $xpath = $this->getSelectorsHandler()->selectorToXpath($selector, $locator); + $xpath = $this->xpathManipulator->prepend($xpath, $this->getXpath()); + + return $this->getDriver()->find($xpath); + } + + /** + * {@inheritdoc} + */ + public function getText() + { + return $this->getDriver()->getText($this->getXpath()); + } + + /** + * {@inheritdoc} + */ + public function getHtml() + { + return $this->getDriver()->getHtml($this->getXpath()); + } + + /** + * Returns element outer html. + * + * @return string + */ + public function getOuterHtml() + { + return $this->getDriver()->getOuterHtml($this->getXpath()); + } + + /** + * Builds an ElementNotFoundException + * + * This is an helper to build the ElementNotFoundException without + * needing to use the deprecated getSession accessor in child classes. + * + * @param string $type + * @param string|null $selector + * @param string|null $locator + * + * @return ElementNotFoundException + */ + protected function elementNotFound($type, $selector = null, $locator = null) + { + return new ElementNotFoundException($this->session, $type, $selector, $locator); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/ElementInterface.php b/core/vendor/behat/mink/src/Behat/Mink/Element/ElementInterface.php new file mode 100644 index 0000000..8c134b9 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Element/ElementInterface.php @@ -0,0 +1,114 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Element; + +use Behat\Mink\Session; + +/** + * Element interface. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +interface ElementInterface +{ + /** + * Returns XPath for handled element. + * + * @return string + */ + public function getXpath(); + + /** + * Returns element's session. + * + * @return Session + * + * @deprecated Accessing the session from the element is deprecated as of 1.6 and will be impossible in 2.0. + */ + public function getSession(); + + /** + * Checks whether element with specified selector exists inside the current element. + * + * @param string $selector selector engine name + * @param string|array $locator selector locator + * + * @return Boolean + * + * @see ElementInterface::findAll for the supported selectors + */ + public function has($selector, $locator); + + /** + * Checks if an element still exists in the DOM. + * + * @return boolean + */ + public function isValid(); + + /** + * Waits for an element(-s) to appear and returns it. + * + * @param int|float $timeout Maximal allowed waiting time in seconds. + * @param callable $callback Callback, which result is both used as waiting condition and returned. + * Will receive reference to `this element` as first argument. + * + * @return mixed + * @throws \InvalidArgumentException When invalid callback given. + */ + public function waitFor($timeout, $callback); + + /** + * Finds first element with specified selector inside the current element. + * + * @param string $selector selector engine name + * @param string|array $locator selector locator + * + * @return NodeElement|null + * + * @see ElementInterface::findAll for the supported selectors + */ + public function find($selector, $locator); + + /** + * Finds all elements with specified selector inside the current element. + * + * Valid selector engines are named, xpath, css, named_partial and named_exact. + * + * 'named' is a pseudo selector engine which prefers an exact match but + * will return a partial match if no exact match is found. + * 'xpath' is a pseudo selector engine supported by SelectorsHandler. + * + * More selector engines can be registered in the SelectorsHandler. + * + * @param string $selector selector engine name + * @param string|array $locator selector locator + * + * @return NodeElement[] + * + * @see NamedSelector for the locators supported by the named selectors + */ + public function findAll($selector, $locator); + + /** + * Returns element text (inside tag). + * + * @return string + */ + public function getText(); + + /** + * Returns element inner html. + * + * @return string + */ + public function getHtml(); +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/NodeElement.php b/core/vendor/behat/mink/src/Behat/Mink/Element/NodeElement.php new file mode 100644 index 0000000..1f867ed --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Element/NodeElement.php @@ -0,0 +1,352 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Element; + +use Behat\Mink\Session; +use Behat\Mink\Exception\ElementNotFoundException; + +/** + * Page element node. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class NodeElement extends TraversableElement +{ + private $xpath; + + /** + * Initializes node element. + * + * @param string $xpath element xpath + * @param Session $session session instance + */ + public function __construct($xpath, Session $session) + { + $this->xpath = $xpath; + + parent::__construct($session); + } + + /** + * Returns XPath for handled element. + * + * @return string + */ + public function getXpath() + { + return $this->xpath; + } + + /** + * Returns parent element to the current one. + * + * @return NodeElement + */ + public function getParent() + { + return $this->find('xpath', '..'); + } + + /** + * Returns current node tag name. + * + * The value is always returned in lowercase to allow an easy comparison. + * + * @return string + */ + public function getTagName() + { + return strtolower($this->getDriver()->getTagName($this->getXpath())); + } + + /** + * Returns the value of the form field or option element. + * + * For checkbox fields, the value is a boolean indicating whether the checkbox is checked. + * For radio buttons, the value is the value of the selected button in the radio group + * or null if no button is selected. + * For single select boxes, the value is the value of the selected option. + * For multiple select boxes, the value is an array of selected option values. + * for file inputs, the return value is undefined given that browsers don't allow accessing + * the value of file inputs for security reasons. Some drivers may allow accessing the + * path of the file set in the field, but this is not required if it cannot be implemented. + * For textarea elements and all textual fields, the value is the content of the field. + * Form option elements, the value is the value of the option (the value attribute or the text + * content if the attribute is not set). + * + * Calling this method on other elements than form fields or option elements is not allowed. + * + * @return string|bool|array + */ + public function getValue() + { + return $this->getDriver()->getValue($this->getXpath()); + } + + /** + * Sets the value of the form field. + * + * Calling this method on other elements than form fields is not allowed. + * + * @param string|bool|array $value + * + * @see NodeElement::getValue for the format of the value for each type of field + */ + public function setValue($value) + { + $this->getDriver()->setValue($this->getXpath(), $value); + } + + /** + * Checks whether element has attribute with specified name. + * + * @param string $name + * + * @return Boolean + */ + public function hasAttribute($name) + { + return null !== $this->getDriver()->getAttribute($this->getXpath(), $name); + } + + /** + * Returns specified attribute value. + * + * @param string $name + * + * @return string|null + */ + public function getAttribute($name) + { + return $this->getDriver()->getAttribute($this->getXpath(), $name); + } + + /** + * Checks whether an element has a named CSS class + * + * @param string $className Name of the class + * + * @return boolean + */ + public function hasClass($className) + { + if ($this->hasAttribute('class')) { + return in_array($className, explode(' ', $this->getAttribute('class'))); + } + + return false; + } + + /** + * Clicks current node. + */ + public function click() + { + $this->getDriver()->click($this->getXpath()); + } + + /** + * Presses current button. + */ + public function press() + { + $this->click(); + } + + /** + * Double-clicks current node. + */ + public function doubleClick() + { + $this->getDriver()->doubleClick($this->getXpath()); + } + + /** + * Right-clicks current node. + */ + public function rightClick() + { + $this->getDriver()->rightClick($this->getXpath()); + } + + /** + * Checks current node if it's a checkbox field. + */ + public function check() + { + $this->getDriver()->check($this->getXpath()); + } + + /** + * Unchecks current node if it's a checkbox field. + */ + public function uncheck() + { + $this->getDriver()->uncheck($this->getXpath()); + } + + /** + * Checks whether current node is checked if it's a checkbox or radio field. + * + * Calling this method on any other elements is not allowed. + * + * @return Boolean + */ + public function isChecked() + { + return (Boolean) $this->getDriver()->isChecked($this->getXpath()); + } + + /** + * Selects specified option for select field or specified radio button in the group + * + * If the current node is a select box, this selects the option found by its value or + * its text. + * If the current node is a radio button, this selects the radio button with the given + * value in the radio button group of the current node. + * + * Calling this method on any other elements is not allowed. + * + * @param string $option + * @param Boolean $multiple whether the option should be added to the selection for multiple selects + * + * @throws ElementNotFoundException when the option is not found in the select box + */ + public function selectOption($option, $multiple = false) + { + if ('select' !== $this->getTagName()) { + $this->getDriver()->selectOption($this->getXpath(), $option, $multiple); + + return; + } + + $opt = $this->find('named', array( + 'option', $this->getSelectorsHandler()->xpathLiteral($option) + )); + + if (null === $opt) { + throw $this->elementNotFound('select option', 'value|text', $option); + } + + $this->getDriver()->selectOption($this->getXpath(), $opt->getValue(), $multiple); + } + + /** + * Checks whether current node is selected if it's a option field. + * + * Calling this method on any other elements is not allowed. + * + * @return Boolean + */ + public function isSelected() + { + return (Boolean) $this->getDriver()->isSelected($this->getXpath()); + } + + /** + * Attach file to current node if it's a file input. + * + * Calling this method on any other elements than file input is not allowed. + * + * @param string $path path to file (local) + */ + public function attachFile($path) + { + $this->getDriver()->attachFile($this->getXpath(), $path); + } + + /** + * Checks whether current node is visible on page. + * + * @return Boolean + */ + public function isVisible() + { + return (Boolean) $this->getDriver()->isVisible($this->getXpath()); + } + + /** + * Simulates a mouse over on the element. + */ + public function mouseOver() + { + $this->getDriver()->mouseOver($this->getXpath()); + } + + /** + * Drags current node onto other node. + * + * @param ElementInterface $destination other node + */ + public function dragTo(ElementInterface $destination) + { + $this->getDriver()->dragTo($this->getXpath(), $destination->getXpath()); + } + + /** + * Brings focus to element. + */ + public function focus() + { + $this->getDriver()->focus($this->getXpath()); + } + + /** + * Removes focus from element. + */ + public function blur() + { + $this->getDriver()->blur($this->getXpath()); + } + + /** + * Presses specific keyboard key. + * + * @param string|integer $char could be either char ('b') or char-code (98) + * @param string $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta') + */ + public function keyPress($char, $modifier = null) + { + $this->getDriver()->keyPress($this->getXpath(), $char, $modifier); + } + + /** + * Pressed down specific keyboard key. + * + * @param string|integer $char could be either char ('b') or char-code (98) + * @param string $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta') + */ + public function keyDown($char, $modifier = null) + { + $this->getDriver()->keyDown($this->getXpath(), $char, $modifier); + } + + /** + * Pressed up specific keyboard key. + * + * @param string|integer $char could be either char ('b') or char-code (98) + * @param string $modifier keyboard modifier (could be 'ctrl', 'alt', 'shift' or 'meta') + */ + public function keyUp($char, $modifier = null) + { + $this->getDriver()->keyUp($this->getXpath(), $char, $modifier); + } + + /** + * Submits the form. + * + * Calling this method on anything else than form elements is not allowed. + */ + public function submit() + { + $this->getDriver()->submitForm($this->getXpath()); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Element/TraversableElement.php b/core/vendor/behat/mink/src/Behat/Mink/Element/TraversableElement.php new file mode 100644 index 0000000..27843d2 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Element/TraversableElement.php @@ -0,0 +1,309 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Element; + +use Behat\Mink\Exception\ElementNotFoundException; + +/** + * Traversable element. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +abstract class TraversableElement extends Element +{ + /** + * Finds element by its id. + * + * @param string $id element id + * + * @return NodeElement|null + */ + public function findById($id) + { + $id = $this->getSelectorsHandler()->xpathLiteral($id); + + return $this->find('named', array('id', $id)); + } + + /** + * Checks whether element has a link with specified locator. + * + * @param string $locator link id, title, text or image alt + * + * @return Boolean + */ + public function hasLink($locator) + { + return null !== $this->findLink($locator); + } + + /** + * Finds link with specified locator. + * + * @param string $locator link id, title, text or image alt + * + * @return NodeElement|null + */ + public function findLink($locator) + { + return $this->find('named', array( + 'link', $this->getSelectorsHandler()->xpathLiteral($locator) + )); + } + + /** + * Clicks link with specified locator. + * + * @param string $locator link id, title, text or image alt + * + * @throws ElementNotFoundException + */ + public function clickLink($locator) + { + $link = $this->findLink($locator); + + if (null === $link) { + throw $this->elementNotFound('link', 'id|title|alt|text', $locator); + } + + $link->click(); + } + + /** + * Checks whether element has a button (input[type=submit|image|button|reset], button) with specified locator. + * + * @param string $locator button id, value or alt + * + * @return Boolean + */ + public function hasButton($locator) + { + return null !== $this->findButton($locator); + } + + /** + * Finds button (input[type=submit|image|button|reset], button) with specified locator. + * + * @param string $locator button id, value or alt + * + * @return NodeElement|null + */ + public function findButton($locator) + { + return $this->find('named', array( + 'button', $this->getSelectorsHandler()->xpathLiteral($locator) + )); + } + + /** + * Presses button (input[type=submit|image|button|reset], button) with specified locator. + * + * @param string $locator button id, value or alt + * + * @throws ElementNotFoundException + */ + public function pressButton($locator) + { + $button = $this->findButton($locator); + + if (null === $button) { + throw $this->elementNotFound('button', 'id|name|title|alt|value', $locator); + } + + $button->press(); + } + + /** + * Checks whether element has a field (input, textarea, select) with specified locator. + * + * @param string $locator input id, name or label + * + * @return Boolean + */ + public function hasField($locator) + { + return null !== $this->findField($locator); + } + + /** + * Finds field (input, textarea, select) with specified locator. + * + * @param string $locator input id, name or label + * + * @return NodeElement|null + */ + public function findField($locator) + { + return $this->find('named', array( + 'field', $this->getSelectorsHandler()->xpathLiteral($locator) + )); + } + + /** + * Fills in field (input, textarea, select) with specified locator. + * + * @param string $locator input id, name or label + * @param string $value value + * + * @throws ElementNotFoundException + * + * @see NodeElement::setValue + */ + public function fillField($locator, $value) + { + $field = $this->findField($locator); + + if (null === $field) { + throw $this->elementNotFound('form field', 'id|name|label|value', $locator); + } + + $field->setValue($value); + } + + /** + * Checks whether element has a checkbox with specified locator, which is checked. + * + * @param string $locator input id, name or label + * + * @return Boolean + * + * @see NodeElement::isChecked + */ + public function hasCheckedField($locator) + { + $field = $this->findField($locator); + + return null !== $field && $field->isChecked(); + } + + /** + * Checks whether element has a checkbox with specified locator, which is unchecked. + * + * @param string $locator input id, name or label + * + * @return Boolean + * + * @see NodeElement::isChecked + */ + public function hasUncheckedField($locator) + { + $field = $this->findField($locator); + + return null !== $field && !$field->isChecked(); + } + + /** + * Checks checkbox with specified locator. + * + * @param string $locator input id, name or label + * + * @throws ElementNotFoundException + */ + public function checkField($locator) + { + $field = $this->findField($locator); + + if (null === $field) { + throw $this->elementNotFound('form field', 'id|name|label|value', $locator); + } + + $field->check(); + } + + /** + * Unchecks checkbox with specified locator. + * + * @param string $locator input id, name or label + * + * @throws ElementNotFoundException + */ + public function uncheckField($locator) + { + $field = $this->findField($locator); + + if (null === $field) { + throw $this->elementNotFound('form field', 'id|name|label|value', $locator); + } + + $field->uncheck(); + } + + /** + * Checks whether element has a select field with specified locator. + * + * @param string $locator select id, name or label + * + * @return Boolean + */ + public function hasSelect($locator) + { + return $this->has('named', array( + 'select', $this->getSelectorsHandler()->xpathLiteral($locator) + )); + } + + /** + * Selects option from select field with specified locator. + * + * @param string $locator input id, name or label + * @param string $value option value + * @param Boolean $multiple select multiple options + * + * @throws ElementNotFoundException + * + * @see NodeElement::selectOption + */ + public function selectFieldOption($locator, $value, $multiple = false) + { + $field = $this->findField($locator); + + if (null === $field) { + throw $this->elementNotFound('form field', 'id|name|label|value', $locator); + } + + $field->selectOption($value, $multiple); + } + + /** + * Checks whether element has a table with specified locator. + * + * @param string $locator table id or caption + * + * @return Boolean + */ + public function hasTable($locator) + { + return $this->has('named', array( + 'table', $this->getSelectorsHandler()->xpathLiteral($locator) + )); + } + + /** + * Attach file to file field with specified locator. + * + * @param string $locator input id, name or label + * @param string $path path to file + * + * @throws ElementNotFoundException + * + * @see NodeElement::attachFile + */ + public function attachFileToField($locator, $path) + { + $field = $this->findField($locator); + + if (null === $field) { + throw $this->elementNotFound('form field', 'id|name|label|value', $locator); + } + + $field->attachFile($path); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/DriverException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/DriverException.php new file mode 100644 index 0000000..840f8bd --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/DriverException.php @@ -0,0 +1,31 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +/** + * Exception thrown by drivers when they fail to perform an action. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class DriverException extends Exception +{ + /** + * Initializes exception. + * + * @param string $message + * @param int $code + * @param \Exception|null $previous + */ + public function __construct($message, $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementException.php new file mode 100644 index 0000000..49d5c6c --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementException.php @@ -0,0 +1,58 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +use Behat\Mink\Element\Element; + +/** + * A standard way for elements to re-throw exceptions + * + * @deprecated This exception class is not used anymore in Mink 1.6 and will be removed in 2.0 + * + * @author Chris Worfolk <xmeltrut@gmail.com> + */ +class ElementException extends Exception +{ + private $element; + + /** + * Initialises exception. + * + * @param Element $element optional message + * @param \Exception $exception exception + */ + public function __construct(Element $element, \Exception $exception) + { + $this->element = $element; + + parent::__construct(sprintf("Exception thrown by %s\n%s", $element->getXpath(), $exception->getMessage())); + } + + /** + * Override default toString so we don't send a full backtrace in verbose mode. + * + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } + + /** + * Get the element that caused the exception + * + * @return Element + */ + public function getElement() + { + return $this->element; + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementHtmlException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementHtmlException.php new file mode 100644 index 0000000..99e2ea6 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementHtmlException.php @@ -0,0 +1,49 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +use Behat\Mink\Session; +use Behat\Mink\Element\Element; + +/** + * Exception thrown when an expectation on the HTML of an element fails. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class ElementHtmlException extends ExpectationException +{ + /** + * Element instance. + * + * @var Element + */ + protected $element; + + /** + * Initializes exception. + * + * @param string $message optional message + * @param Session $session session instance + * @param Element $element element + * @param \Exception $exception expectation exception + */ + public function __construct($message, Session $session, Element $element, \Exception $exception = null) + { + $this->element = $element; + + parent::__construct($message, $session, $exception); + } + + protected function getContext() + { + return $this->element->getOuterHtml(); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementNotFoundException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementNotFoundException.php new file mode 100644 index 0000000..2d379a8 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementNotFoundException.php @@ -0,0 +1,53 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +use Behat\Mink\Session; + +/** + * Exception thrown when an expected element is not found. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class ElementNotFoundException extends ExpectationException +{ + /** + * Initializes exception. + * + * @param Session $session session instance + * @param string $type element type + * @param string $selector element selector type + * @param string $locator element locator + */ + public function __construct(Session $session, $type = null, $selector = null, $locator = null) + { + $message = ''; + + if (null !== $type) { + $message .= ucfirst($type); + } else { + $message .= 'Tag'; + } + + if (null !== $locator) { + if (null === $selector || in_array($selector, array('css', 'xpath'))) { + $selector = 'matching '.($selector ?: 'locator'); + } else { + $selector = 'with '.$selector; + } + $message .= ' '.$selector.' "' . $locator . '"'; + } + + $message .= ' not found.'; + + parent::__construct($message, $session); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementTextException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementTextException.php new file mode 100644 index 0000000..5efff0f --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ElementTextException.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +/** + * Exception thrown when an expectation on the text of an element fails. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class ElementTextException extends ElementHtmlException +{ + protected function getContext() + { + return $this->element->getText(); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/Exception.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/Exception.php new file mode 100644 index 0000000..4662a5b --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/Exception.php @@ -0,0 +1,20 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +/** + * Mink base exception class. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +abstract class Exception extends \Exception +{ +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ExpectationException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ExpectationException.php new file mode 100644 index 0000000..da1723c --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ExpectationException.php @@ -0,0 +1,145 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +use Behat\Mink\Session; + +/** + * Exception thrown for failed expectations. + * + * Some specialized child classes are available to customize the error rendering. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class ExpectationException extends Exception +{ + private $session; + + /** + * Initializes exception. + * + * @param string $message optional message + * @param Session $session session instance + * @param \Exception $exception expectation exception + */ + public function __construct($message, Session $session, \Exception $exception = null) + { + $this->session = $session; + + if (!$message && null !== $exception) { + $message = $exception->getMessage(); + } + + parent::__construct($message, 0, $exception); + } + + /** + * Returns exception message with additional context info. + * + * @return string + */ + public function __toString() + { + try { + $pageText = $this->pipeString($this->trimString($this->getContext()) . "\n"); + $string = sprintf("%s\n\n%s%s", $this->getMessage(), $this->getResponseInfo(), $pageText); + } catch (\Exception $e) { + return $this->getMessage(); + } + + return $string; + } + + /** + * Gets the context rendered for this exception + * + * @return string + */ + protected function getContext() + { + return $this->trimBody($this->getSession()->getPage()->getContent()); + } + + /** + * Returns exception session. + * + * @return Session + */ + protected function getSession() + { + return $this->session; + } + + /** + * Prepends every line in a string with pipe (|). + * + * @param string $string + * + * @return string + */ + protected function pipeString($string) + { + return '| ' . strtr($string, array("\n" => "\n| ")); + } + + /** + * Removes response header/footer, letting only <body /> content. + * + * @param string $string response content + * + * @return string + */ + protected function trimBody($string) + { + $string = preg_replace(array('/^.*<body>/s', '/<\/body>.*$/s'), array('<body>', '</body>'), $string); + + return $string; + } + + /** + * Trims string to specified number of chars. + * + * @param string $string response content + * @param integer $count trim count + * + * @return string + */ + protected function trimString($string, $count = 1000) + { + $string = trim($string); + + if ($count < mb_strlen($string)) { + return mb_substr($string, 0, $count - 3) . '...'; + } + + return $string; + } + + /** + * Returns response information string. + * + * @return string + */ + protected function getResponseInfo() + { + $driver = basename(str_replace('\\', '/', get_class($this->session->getDriver()))); + + $info = '+--[ '; + try { + $info .= 'HTTP/1.1 '.$this->session->getStatusCode().' | '; + } catch (UnsupportedDriverActionException $e) { + // Ignore the status code when not supported + } + $info .= $this->session->getCurrentUrl().' | '.$driver." ]\n|\n"; + + return $info; + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/ResponseTextException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/ResponseTextException.php new file mode 100644 index 0000000..145ed6d --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/ResponseTextException.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +/** + * Exception thrown when an expectation on the response text fails. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class ResponseTextException extends ExpectationException +{ + protected function getContext() + { + return $this->getSession()->getPage()->getText(); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Exception/UnsupportedDriverActionException.php b/core/vendor/behat/mink/src/Behat/Mink/Exception/UnsupportedDriverActionException.php new file mode 100644 index 0000000..d02969b --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Exception/UnsupportedDriverActionException.php @@ -0,0 +1,35 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Exception; + +use Behat\Mink\Driver\DriverInterface; + +/** + * Exception thrown by drivers when they don't support the requested action. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class UnsupportedDriverActionException extends DriverException +{ + /** + * Initializes exception. + * + * @param string $template what is unsupported? + * @param DriverInterface $driver driver instance + * @param \Exception $previous previous exception + */ + public function __construct($template, DriverInterface $driver, \Exception $previous = null) + { + $message = sprintf($template, get_class($driver)); + + parent::__construct($message, 0, $previous); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Mink.php b/core/vendor/behat/mink/src/Behat/Mink/Mink.php new file mode 100644 index 0000000..d1621b1 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Mink.php @@ -0,0 +1,216 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink; + +/** + * Mink sessions manager. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class Mink +{ + private $defaultSessionName; + + /** + * Sessions. + * + * @var Session[] + */ + private $sessions = array(); + + /** + * Initializes manager. + * + * @param Session[] $sessions + */ + public function __construct(array $sessions = array()) + { + foreach ($sessions as $name => $session) { + $this->registerSession($name, $session); + } + } + + /** + * Stops all started sessions. + */ + public function __destruct() + { + $this->stopSessions(); + } + + /** + * Registers new session. + * + * @param string $name + * @param Session $session + */ + public function registerSession($name, Session $session) + { + $name = strtolower($name); + + $this->sessions[$name] = $session; + } + + /** + * Checks whether session with specified name is registered. + * + * @param string $name + * + * @return Boolean + */ + public function hasSession($name) + { + return isset($this->sessions[strtolower($name)]); + } + + /** + * Sets default session name to use. + * + * @param string $name name of the registered session + * + * @throws \InvalidArgumentException + */ + public function setDefaultSessionName($name) + { + $name = strtolower($name); + + if (!isset($this->sessions[$name])) { + throw new \InvalidArgumentException(sprintf('Session "%s" is not registered.', $name)); + } + + $this->defaultSessionName = $name; + } + + /** + * Returns default session name or null if none. + * + * @return null|string + */ + public function getDefaultSessionName() + { + return $this->defaultSessionName; + } + + /** + * Returns registered session by it's name or active one and automatically starts it if required. + * + * @param string $name session name + * + * @return Session + * + * @throws \InvalidArgumentException If the named session is not registered + */ + public function getSession($name = null) + { + $session = $this->locateSession($name); + + // start session if needed + if (!$session->isStarted()) { + $session->start(); + } + + return $session; + } + + /** + * Checks whether a named session (or the default session) has already been started + * + * @param string $name session name - if null then the default session will be checked + * + * @return bool whether the session has been started + * + * @throws \InvalidArgumentException If the named session is not registered + */ + public function isSessionStarted($name = null) + { + $session = $this->locateSession($name); + + return $session->isStarted(); + } + + /** + * Returns session asserter. + * + * @param Session|string $session session object or name + * + * @return WebAssert + */ + public function assertSession($session = null) + { + if (!($session instanceof Session)) { + $session = $this->getSession($session); + } + + return new WebAssert($session); + } + + /** + * Resets all started sessions. + */ + public function resetSessions() + { + foreach ($this->sessions as $session) { + if ($session->isStarted()) { + $session->reset(); + } + } + } + + /** + * Restarts all started sessions. + */ + public function restartSessions() + { + foreach ($this->sessions as $session) { + if ($session->isStarted()) { + $session->restart(); + } + } + } + + /** + * Stops all started sessions. + */ + public function stopSessions() + { + foreach ($this->sessions as $session) { + if ($session->isStarted()) { + $session->stop(); + } + } + } + + /** + * Returns the named or default session without starting it. + * + * @param string $name session name + * + * @return Session + * + * @throws \InvalidArgumentException If the named session is not registered + */ + protected function locateSession($name = null) + { + $name = strtolower($name) ?: $this->defaultSessionName; + + if (null === $name) { + throw new \InvalidArgumentException('Specify session name to get'); + } + + if (!isset($this->sessions[$name])) { + throw new \InvalidArgumentException(sprintf('Session "%s" is not registered.', $name)); + } + + $session = $this->sessions[$name]; + + return $session; + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/CssSelector.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/CssSelector.php new file mode 100644 index 0000000..3636586 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/CssSelector.php @@ -0,0 +1,37 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Selector; + +use Symfony\Component\CssSelector\CssSelector as CSS; + +/** + * CSS selector engine. Transforms CSS to XPath. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class CssSelector implements SelectorInterface +{ + /** + * Translates CSS into XPath. + * + * @param string|array $locator current selector locator + * + * @return string + */ + public function translateToXPath($locator) + { + if (!is_string($locator)) { + throw new \InvalidArgumentException('The CssSelector expects to get a string as locator'); + } + + return CSS::toXPath($locator); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/ExactNamedSelector.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/ExactNamedSelector.php new file mode 100644 index 0000000..03315a9 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/ExactNamedSelector.php @@ -0,0 +1,29 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Selector; + +/** + * Exact match selector engine. Like the Named selector engine but ignores partial matches. + */ +class ExactNamedSelector extends NamedSelector +{ + public function __construct() + { + $this->registerReplacement('%tagTextMatch%', 'normalize-space(string(.)) = %locator%'); + $this->registerReplacement('%valueMatch%', './@value = %locator%'); + $this->registerReplacement('%titleMatch%', './@title = %locator%'); + $this->registerReplacement('%altMatch%', './@alt = %locator%'); + $this->registerReplacement('%relMatch%', './@rel = %locator%'); + $this->registerReplacement('%labelAttributeMatch%', './@label = %locator%'); + + parent::__construct(); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/NamedSelector.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/NamedSelector.php new file mode 100644 index 0000000..4845f99 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/NamedSelector.php @@ -0,0 +1,238 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Selector; + +/** + * Named selectors engine. Uses registered XPath selectors to create new expressions. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class NamedSelector implements SelectorInterface +{ + private $replacements = array( + // simple replacements + '%lowercaseType%' => "translate(./@type, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')", + '%lowercaseRole%' => "translate(./@role, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')", + '%tagTextMatch%' => 'contains(normalize-space(string(.)), %locator%)', + '%labelTextMatch%' => './@id = //label[%tagTextMatch%]/@for', + '%idMatch%' => './@id = %locator%', + '%valueMatch%' => 'contains(./@value, %locator%)', + '%idOrValueMatch%' => '(%idMatch% or %valueMatch%)', + '%idOrNameMatch%' => '(%idMatch% or ./@name = %locator%)', + '%placeholderMatch%' => './@placeholder = %locator%', + '%titleMatch%' => 'contains(./@title, %locator%)', + '%altMatch%' => 'contains(./@alt, %locator%)', + '%relMatch%' => 'contains(./@rel, %locator%)', + '%labelAttributeMatch%' => 'contains(./@label, %locator%)', + + // complex replacements + '%inputTypeWithoutPlaceholderFilter%' => "%lowercaseType% = 'radio' or %lowercaseType% = 'checkbox' or %lowercaseType% = 'file'", + '%fieldFilterWithPlaceholder%' => 'self::input[not(%inputTypeWithoutPlaceholderFilter%)] | self::textarea', + '%fieldMatchWithPlaceholder%' => '(%idOrNameMatch% or %labelTextMatch% or %placeholderMatch%)', + '%fieldMatchWithoutPlaceholder%' => '(%idOrNameMatch% or %labelTextMatch%)', + '%fieldFilterWithoutPlaceholder%' => 'self::input[%inputTypeWithoutPlaceholderFilter%] | self::select', + '%buttonTypeFilter%' => "%lowercaseType% = 'submit' or %lowercaseType% = 'image' or %lowercaseType% = 'button' or %lowercaseType% = 'reset'", + '%notFieldTypeFilter%' => "not(%buttonTypeFilter% or %lowercaseType% = 'hidden')", + '%buttonMatch%' => '%idOrNameMatch% or %valueMatch% or %titleMatch%', + '%linkMatch%' => '(%idMatch% or %tagTextMatch% or %titleMatch% or %relMatch%)', + '%imgAltMatch%' => './/img[%altMatch%]', + ); + + private $selectors = array( + 'fieldset' => <<<XPATH +.//fieldset +[(%idMatch% or .//legend[%tagTextMatch%])] +XPATH + + ,'field' => <<<XPATH +.//* +[%fieldFilterWithPlaceholder%][%notFieldTypeFilter%][%fieldMatchWithPlaceholder%] +| +.//label[%tagTextMatch%]//.//*[%fieldFilterWithPlaceholder%][%notFieldTypeFilter%] +| +.//* +[%fieldFilterWithoutPlaceholder%][%notFieldTypeFilter%][%fieldMatchWithoutPlaceholder%] +| +.//label[%tagTextMatch%]//.//*[%fieldFilterWithoutPlaceholder%][%notFieldTypeFilter%] +XPATH + + ,'link' => <<<XPATH +.//a +[./@href][(%linkMatch% or %imgAltMatch%)] +| +.//* +[%lowercaseRole% = 'link'][(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)] +XPATH + + ,'button' => <<<XPATH +.//input +[%buttonTypeFilter%][(%buttonMatch%)] +| +.//input +[%lowercaseType% = 'image'][%altMatch%] +| +.//button +[(%buttonMatch% or %tagTextMatch%)] +| +.//* +[%lowercaseRole% = 'button'][(%buttonMatch% or %tagTextMatch%)] +XPATH + + ,'link_or_button' => <<<XPATH +.//a +[./@href][(%linkMatch% or %imgAltMatch%)] +| +.//input +[%buttonTypeFilter%][(%idOrValueMatch% or %titleMatch%)] +| +.//input +[%lowercaseType% = 'image'][%altMatch%] +| +.//button +[(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)] +| +.//* +[(%lowercaseRole% = 'button' or %lowercaseRole% = 'link')][(%idOrValueMatch% or %titleMatch% or %tagTextMatch%)] +XPATH + + ,'content' => <<<XPATH +./descendant-or-self::* +[%tagTextMatch%] +XPATH + + ,'select' => <<<XPATH +.//select +[%fieldMatchWithoutPlaceholder%] +| +.//label[%tagTextMatch%]//.//select +XPATH + + ,'checkbox' => <<<XPATH +.//input +[%lowercaseType% = 'checkbox'][%fieldMatchWithoutPlaceholder%] +| +.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'checkbox'] +XPATH + + ,'radio' => <<<XPATH +.//input +[%lowercaseType% = 'radio'][%fieldMatchWithoutPlaceholder%] +| +.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'radio'] +XPATH + + ,'file' => <<<XPATH +.//input +[%lowercaseType% = 'file'][%fieldMatchWithoutPlaceholder%] +| +.//label[%tagTextMatch%]//.//input[%lowercaseType% = 'file'] +XPATH + + ,'optgroup' => <<<XPATH +.//optgroup +[%labelAttributeMatch%] +XPATH + + ,'option' => <<<XPATH +.//option +[(./@value = %locator% or %tagTextMatch%)] +XPATH + + ,'table' => <<<XPATH +.//table +[(%idMatch% or .//caption[%tagTextMatch%])] +XPATH + ,'id' => <<<XPATH +.//*[%idMatch%] +XPATH + ,'id_or_name' => <<<XPATH +.//*[%idOrNameMatch%] +XPATH + ); + + /** + * Creates selector instance. + */ + public function __construct() + { + foreach ($this->replacements as $from => $to) { + $this->replacements[$from] = strtr($to, $this->replacements); + } + + foreach ($this->selectors as $alias => $selector) { + $this->selectors[$alias] = strtr($selector, $this->replacements); + } + } + + /** + * Registers new XPath selector with specified name. + * + * @param string $name name for selector + * @param string $xpath xpath expression + */ + public function registerNamedXpath($name, $xpath) + { + $this->selectors[$name] = $xpath; + } + + /** + * Translates provided locator into XPath. + * + * @param string|array $locator selector name or array of (selector_name, locator) + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function translateToXPath($locator) + { + if (2 < count($locator)) { + throw new \InvalidArgumentException('NamedSelector expects array(name, locator) as argument'); + } + + if (2 == count($locator)) { + $selector = $locator[0]; + $locator = $locator[1]; + } else { + $selector = (string) $locator; + $locator = null; + } + + if (!isset($this->selectors[$selector])) { + throw new \InvalidArgumentException(sprintf( + 'Unknown named selector provided: "%s". Expected one of (%s)', + $selector, + implode(', ', array_keys($this->selectors)) + )); + } + + $xpath = $this->selectors[$selector]; + + if (null !== $locator) { + $xpath = strtr($xpath, array('%locator%' => $locator)); + } + + return $xpath; + } + + /** + * Registers a replacement in the list of replacements + * + * This method must be called in the constructor before calling the parent constructor. + * + * @param string $from + * @param string $to + */ + protected function registerReplacement($from, $to) + { + $this->replacements[$from] = $to; + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/PartialNamedSelector.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/PartialNamedSelector.php new file mode 100644 index 0000000..b864a22 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/PartialNamedSelector.php @@ -0,0 +1,31 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Selector; + +/** + * Named selectors engine. Uses registered XPath selectors to create new expressions. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class PartialNamedSelector extends NamedSelector +{ + public function __construct() + { + $this->registerReplacement('%tagTextMatch%', 'contains(normalize-space(string(.)), %locator%)'); + $this->registerReplacement('%valueMatch%', 'contains(./@value, %locator%)'); + $this->registerReplacement('%titleMatch%', 'contains(./@title, %locator%)'); + $this->registerReplacement('%altMatch%', 'contains(./@alt, %locator%)'); + $this->registerReplacement('%relMatch%', 'contains(./@rel, %locator%)'); + $this->registerReplacement('%labelAttributeMatch%', 'contains(./@label, %locator%)'); + + parent::__construct(); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorInterface.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorInterface.php new file mode 100644 index 0000000..e4f5f17 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorInterface.php @@ -0,0 +1,28 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Selector; + +/** + * Mink selector engine interface. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +interface SelectorInterface +{ + /** + * Translates provided locator into XPath. + * + * @param string|array $locator current selector locator + * + * @return string + */ + public function translateToXPath($locator); +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorsHandler.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorsHandler.php new file mode 100644 index 0000000..a19de67 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/SelectorsHandler.php @@ -0,0 +1,125 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Selector; + +use Behat\Mink\Selector\Xpath\Escaper; + +/** + * Selectors handler. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class SelectorsHandler +{ + private $selectors; + private $escaper; + + /** + * Initializes selectors handler. + * + * @param SelectorInterface[] $selectors default selectors to register + */ + public function __construct(array $selectors = array()) + { + $this->escaper = new Escaper(); + + $this->registerSelector('named_partial', new PartialNamedSelector()); + $this->registerSelector('named_exact', new ExactNamedSelector()); + $this->registerSelector('css', new CssSelector()); + + foreach ($selectors as $name => $selector) { + $this->registerSelector($name, $selector); + } + } + + /** + * Registers new selector engine with specified name. + * + * @param string $name selector engine name + * @param SelectorInterface $selector selector engine instance + */ + public function registerSelector($name, SelectorInterface $selector) + { + $this->selectors[$name] = $selector; + } + + /** + * Checks whether selector with specified name is registered on handler. + * + * @param string $name selector engine name + * + * @return Boolean + */ + public function isSelectorRegistered($name) + { + return isset($this->selectors[$name]); + } + + /** + * Returns selector engine with specified name. + * + * @param string $name selector engine name + * + * @return SelectorInterface + * + * @throws \InvalidArgumentException + */ + public function getSelector($name) + { + if ('named' === $name) { + trigger_error( + 'Using the "named" selector directly from the handler is deprecated as of 1.6 and will be removed in 2.0.' + .' Use the "named_partial" or use the "named" selector through the Element API instead.', + E_USER_DEPRECATED + ); + $name = 'named_partial'; + } + + if (!$this->isSelectorRegistered($name)) { + throw new \InvalidArgumentException("Selector \"$name\" is not registered."); + } + + return $this->selectors[$name]; + } + + /** + * Translates selector with specified name to XPath. + * + * @param string $selector selector engine name (registered) + * @param string|array $locator selector locator (an array or a string depending of the selector being used) + * + * @return string + */ + public function selectorToXpath($selector, $locator) + { + if ('xpath' === $selector) { + if (!is_string($locator)) { + throw new \InvalidArgumentException('The xpath selector expects to get a string as locator'); + } + + return $locator; + } + + return $this->getSelector($selector)->translateToXPath($locator); + } + + /** + * Translates string to XPath literal. + * + * @param string $s + * + * @return string + */ + public function xpathLiteral($s) + { + return $this->escaper->escapeLiteral($s); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Escaper.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Escaper.php new file mode 100644 index 0000000..be35000 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Escaper.php @@ -0,0 +1,52 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Selector\Xpath; + +/** + * XPath escaper. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class Escaper +{ + /** + * Escapes the string as a XPath literal. + * + * @param string $s + * + * @return string + */ + public function escapeLiteral($s) + { + if (false === strpos($s, "'")) { + return sprintf("'%s'", $s); + } + + if (false === strpos($s, '"')) { + return sprintf('"%s"', $s); + } + + $string = $s; + $parts = array(); + while (true) { + if (false !== $pos = strpos($string, "'")) { + $parts[] = sprintf("'%s'", substr($string, 0, $pos)); + $parts[] = "\"'\""; + $string = substr($string, $pos + 1); + } else { + $parts[] = "'$string'"; + break; + } + } + + return sprintf("concat(%s)", implode($parts, ',')); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Manipulator.php b/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Manipulator.php new file mode 100644 index 0000000..617b314 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Selector/Xpath/Manipulator.php @@ -0,0 +1,69 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink\Selector\Xpath; + +/** + * XPath manipulation utility. + * + * @author Graham Bates + * @author Christophe Coevoet <stof@notk.org> + */ +class Manipulator +{ + /** + * Regex to find union operators not inside brackets. + */ + const UNION_PATTERN = '/\|(?![^\[]*\])/'; + + /** + * Prepends the XPath prefix to the given XPath. + * + * The returned XPath will match elements matching the XPath inside an element + * matching the prefix. + * + * @param string $xpath + * @param string $prefix + * + * @return string + */ + public function prepend($xpath, $prefix) + { + $expressions = array(); + + // If the xpath prefix contains a union we need to wrap it in parentheses. + if (preg_match(self::UNION_PATTERN, $prefix)) { + $prefix = '(' . $prefix . ')'; + } + + // Split any unions into individual expressions. + foreach (preg_split(self::UNION_PATTERN, $xpath) as $expression) { + $expression = trim($expression); + $parenthesis = ''; + + // If the union is inside some braces, we need to preserve the opening braces and apply + // the prefix only inside it. + if (preg_match('/^[\(\s*]+/', $expression, $matches)) { + $parenthesis = $matches[0]; + $expression = substr($expression, strlen($parenthesis)); + } + + // add prefix before element selector + if (0 === strpos($expression, '/')) { + $expression = $prefix . $expression; + } else { + $expression = $prefix . '/' . $expression; + } + $expressions[] = $parenthesis . $expression; + } + + return implode(' | ', $expressions); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/Session.php b/core/vendor/behat/mink/src/Behat/Mink/Session.php new file mode 100644 index 0000000..c67f7f9 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/Session.php @@ -0,0 +1,352 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink; + +use Behat\Mink\Driver\DriverInterface; +use Behat\Mink\Selector\SelectorsHandler; +use Behat\Mink\Element\DocumentElement; + +/** + * Mink session. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class Session +{ + private $driver; + private $page; + private $selectorsHandler; + + /** + * Initializes session. + * + * @param DriverInterface $driver + * @param SelectorsHandler $selectorsHandler + */ + public function __construct(DriverInterface $driver, SelectorsHandler $selectorsHandler = null) + { + $driver->setSession($this); + + if (null === $selectorsHandler) { + $selectorsHandler = new SelectorsHandler(); + } + + $this->driver = $driver; + $this->selectorsHandler = $selectorsHandler; + $this->page = new DocumentElement($this); + } + + /** + * Checks whether session (driver) was started. + * + * @return Boolean + */ + public function isStarted() + { + return $this->driver->isStarted(); + } + + /** + * Starts session driver. + * + * Calling any action before visiting a page is an undefined behavior. + * The only supported method calls on a fresh driver are + * - visit() + * - setRequestHeader() + * - setBasicAuth() + * - reset() + * - stop() + */ + public function start() + { + $this->driver->start(); + } + + /** + * Stops session driver. + */ + public function stop() + { + $this->driver->stop(); + } + + /** + * Restart session driver. + */ + public function restart() + { + $this->driver->stop(); + $this->driver->start(); + } + + /** + * Reset session driver state. + * + * Calling any action before visiting a page is an undefined behavior. + * The only supported method calls on a fresh driver are + * - visit() + * - setRequestHeader() + * - setBasicAuth() + * - reset() + * - stop() + */ + public function reset() + { + $this->driver->reset(); + } + + /** + * Returns session driver. + * + * @return DriverInterface + */ + public function getDriver() + { + return $this->driver; + } + + /** + * Returns page element. + * + * @return DocumentElement + */ + public function getPage() + { + return $this->page; + } + + /** + * Returns selectors handler. + * + * @return SelectorsHandler + */ + public function getSelectorsHandler() + { + return $this->selectorsHandler; + } + + /** + * Visit specified URL. + * + * @param string $url url of the page + */ + public function visit($url) + { + $this->driver->visit($url); + } + + /** + * Sets HTTP Basic authentication parameters + * + * @param string|Boolean $user user name or false to disable authentication + * @param string $password password + */ + public function setBasicAuth($user, $password = '') + { + $this->driver->setBasicAuth($user, $password); + } + + /** + * Sets specific request header. + * + * @param string $name + * @param string $value + */ + public function setRequestHeader($name, $value) + { + $this->driver->setRequestHeader($name, $value); + } + + /** + * Returns all response headers. + * + * @return array + */ + public function getResponseHeaders() + { + return $this->driver->getResponseHeaders(); + } + + /** + * Sets cookie. + * + * @param string $name + * @param string $value + */ + public function setCookie($name, $value = null) + { + $this->driver->setCookie($name, $value); + } + + /** + * Returns cookie by name. + * + * @param string $name + * + * @return string|null + */ + public function getCookie($name) + { + return $this->driver->getCookie($name); + } + + /** + * Returns response status code. + * + * @return integer + */ + public function getStatusCode() + { + return $this->driver->getStatusCode(); + } + + /** + * Returns current URL address. + * + * @return string + */ + public function getCurrentUrl() + { + return $this->driver->getCurrentUrl(); + } + + /** + * Capture a screenshot of the current window. + * + * @return string screenshot of MIME type image/* depending + * on driver (e.g., image/png, image/jpeg) + */ + public function getScreenshot() + { + return $this->driver->getScreenshot(); + } + + /** + * Return the names of all open windows + * + * @return array Array of all open window's names. + */ + public function getWindowNames() + { + return $this->driver->getWindowNames(); + } + + /** + * Return the name of the currently active window + * + * @return string The name of the current window. + */ + public function getWindowName() + { + return $this->driver->getWindowName(); + } + + /** + * Reloads current session page. + */ + public function reload() + { + $this->driver->reload(); + } + + /** + * Moves backward 1 page in history. + */ + public function back() + { + $this->driver->back(); + } + + /** + * Moves forward 1 page in history. + */ + public function forward() + { + $this->driver->forward(); + } + + /** + * Switches to specific browser window. + * + * @param string $name window name (null for switching back to main window) + */ + public function switchToWindow($name = null) + { + $this->driver->switchToWindow($name); + } + + /** + * Switches to specific iFrame. + * + * @param string $name iframe name (null for switching back) + */ + public function switchToIFrame($name = null) + { + $this->driver->switchToIFrame($name); + } + + /** + * Execute JS in browser. + * + * @param string $script javascript + */ + public function executeScript($script) + { + $this->driver->executeScript($script); + } + + /** + * Execute JS in browser and return it's response. + * + * @param string $script javascript + * + * @return string + */ + public function evaluateScript($script) + { + return $this->driver->evaluateScript($script); + } + + /** + * Waits some time or until JS condition turns true. + * + * @param integer $time time in milliseconds + * @param string $condition JS condition + * + * @return boolean + */ + public function wait($time, $condition = 'false') + { + return $this->driver->wait($time, $condition); + } + + /** + * Set the dimensions of the window. + * + * @param integer $width set the window width, measured in pixels + * @param integer $height set the window height, measured in pixels + * @param string $name window name (null for the main window) + */ + public function resizeWindow($width, $height, $name = null) + { + $this->driver->resizeWindow($width, $height, $name); + } + + /** + * Maximize the window if it is not maximized already + * + * @param string $name window name (null for the main window) + */ + public function maximizeWindow($name = null) + { + $this->driver->maximizeWindow($name); + } +} diff --git a/core/vendor/behat/mink/src/Behat/Mink/WebAssert.php b/core/vendor/behat/mink/src/Behat/Mink/WebAssert.php new file mode 100644 index 0000000..8cb3659 --- /dev/null +++ b/core/vendor/behat/mink/src/Behat/Mink/WebAssert.php @@ -0,0 +1,748 @@ +<?php + +/* + * This file is part of the Mink package. + * (c) Konstantin Kudryashov <ever.zet@gmail.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Mink; + +use Behat\Mink\Element\Element; +use Behat\Mink\Element\ElementInterface; +use Behat\Mink\Element\NodeElement; +use Behat\Mink\Element\TraversableElement; +use Behat\Mink\Exception\ElementNotFoundException; +use Behat\Mink\Exception\ExpectationException; +use Behat\Mink\Exception\ResponseTextException; +use Behat\Mink\Exception\ElementHtmlException; +use Behat\Mink\Exception\ElementTextException; + +/** + * Mink web assertions tool. + * + * @author Konstantin Kudryashov <ever.zet@gmail.com> + */ +class WebAssert +{ + protected $session; + + /** + * Initializes assertion engine. + * + * @param Session $session + */ + public function __construct(Session $session) + { + $this->session = $session; + } + + /** + * Checks that current session address is equals to provided one. + * + * @param string $page + * + * @throws ExpectationException + */ + public function addressEquals($page) + { + $expected = $this->cleanUrl($page); + $actual = $this->getCurrentUrlPath(); + + $this->assert($actual === $expected, sprintf('Current page is "%s", but "%s" expected.', $actual, $expected)); + } + + /** + * Checks that current session address is not equals to provided one. + * + * @param string $page + * + * @throws ExpectationException + */ + public function addressNotEquals($page) + { + $expected = $this->cleanUrl($page); + $actual = $this->getCurrentUrlPath(); + + $this->assert($actual !== $expected, sprintf('Current page is "%s", but should not be.', $actual)); + } + + /** + * Checks that current session address matches regex. + * + * @param string $regex + * + * @throws ExpectationException + */ + public function addressMatches($regex) + { + $actual = $this->getCurrentUrlPath(); + $message = sprintf('Current page "%s" does not match the regex "%s".', $actual, $regex); + + $this->assert((bool) preg_match($regex, $actual), $message); + } + + /** + * Checks that specified cookie exists and its value equals to a given one + * + * @param string $name cookie name + * @param string $value cookie value + * + * @throws ExpectationException + */ + public function cookieEquals($name, $value) + { + $this->cookieExists($name); + + $actualValue = $this->session->getCookie($name); + $message = sprintf('Cookie "%s" value is "%s", but should be "%s".', $name, $actualValue, $value); + + $this->assert($actualValue == $value, $message); + } + + /** + * Checks that specified cookie exists + * + * @param string $name cookie name + * + * @throws ExpectationException + */ + public function cookieExists($name) + { + $message = sprintf('Cookie "%s" is not set, but should be.', $name); + $this->assert($this->session->getCookie($name) !== null, $message); + } + + /** + * Checks that current response code equals to provided one. + * + * @param integer $code + * + * @throws ExpectationException + */ + public function statusCodeEquals($code) + { + $actual = $this->session->getStatusCode(); + $message = sprintf('Current response status code is %d, but %d expected.', $actual, $code); + + $this->assert(intval($code) === intval($actual), $message); + } + + /** + * Checks that current response code not equals to provided one. + * + * @param integer $code + * + * @throws ExpectationException + */ + public function statusCodeNotEquals($code) + { + $actual = $this->session->getStatusCode(); + $message = sprintf('Current response status code is %d, but should not be.', $actual); + + $this->assert(intval($code) !== intval($actual), $message); + } + + /** + * Checks that current page contains text. + * + * @param string $text + * + * @throws ResponseTextException + */ + public function pageTextContains($text) + { + $actual = $this->session->getPage()->getText(); + $actual = preg_replace('/\s+/u', ' ', $actual); + $regex = '/'.preg_quote($text, '/').'/ui'; + $message = sprintf('The text "%s" was not found anywhere in the text of the current page.', $text); + + $this->assertResponseText((bool) preg_match($regex, $actual), $message); + } + + /** + * Checks that current page does not contains text. + * + * @param string $text + * + * @throws ResponseTextException + */ + public function pageTextNotContains($text) + { + $actual = $this->session->getPage()->getText(); + $actual = preg_replace('/\s+/u', ' ', $actual); + $regex = '/'.preg_quote($text, '/').'/ui'; + $message = sprintf('The text "%s" appears in the text of this page, but it should not.', $text); + + $this->assertResponseText(!preg_match($regex, $actual), $message); + } + + /** + * Checks that current page text matches regex. + * + * @param string $regex + * + * @throws ResponseTextException + */ + public function pageTextMatches($regex) + { + $actual = $this->session->getPage()->getText(); + $message = sprintf('The pattern %s was not found anywhere in the text of the current page.', $regex); + + $this->assertResponseText((bool) preg_match($regex, $actual), $message); + } + + /** + * Checks that current page text does not matches regex. + * + * @param string $regex + * + * @throws ResponseTextException + */ + public function pageTextNotMatches($regex) + { + $actual = $this->session->getPage()->getText(); + $message = sprintf('The pattern %s was found in the text of the current page, but it should not.', $regex); + + $this->assertResponseText(!preg_match($regex, $actual), $message); + } + + /** + * Checks that page HTML (response content) contains text. + * + * @param string $text + * + * @throws ExpectationException + */ + public function responseContains($text) + { + $actual = $this->session->getPage()->getContent(); + $regex = '/'.preg_quote($text, '/').'/ui'; + $message = sprintf('The string "%s" was not found anywhere in the HTML response of the current page.', $text); + + $this->assert((bool) preg_match($regex, $actual), $message); + } + + /** + * Checks that page HTML (response content) does not contains text. + * + * @param string $text + * + * @throws ExpectationException + */ + public function responseNotContains($text) + { + $actual = $this->session->getPage()->getContent(); + $regex = '/'.preg_quote($text, '/').'/ui'; + $message = sprintf('The string "%s" appears in the HTML response of this page, but it should not.', $text); + + $this->assert(!preg_match($regex, $actual), $message); + } + + /** + * Checks that page HTML (response content) matches regex. + * + * @param string $regex + * + * @throws ExpectationException + */ + public function responseMatches($regex) + { + $actual = $this->session->getPage()->getContent(); + $message = sprintf('The pattern %s was not found anywhere in the HTML response of the page.', $regex); + + $this->assert((bool) preg_match($regex, $actual), $message); + } + + /** + * Checks that page HTML (response content) does not matches regex. + * + * @param $regex + * + * @throws ExpectationException + */ + public function responseNotMatches($regex) + { + $actual = $this->session->getPage()->getContent(); + $message = sprintf('The pattern %s was found in the HTML response of the page, but it should not.', $regex); + + $this->assert(!preg_match($regex, $actual), $message); + } + + /** + * Checks that there is specified number of specific elements on the page. + * + * @param string $selectorType element selector type (css, xpath) + * @param string|array $selector element selector + * @param integer $count expected count + * @param ElementInterface $container document to check against + * + * @throws ExpectationException + */ + public function elementsCount($selectorType, $selector, $count, ElementInterface $container = null) + { + $container = $container ?: $this->session->getPage(); + $nodes = $container->findAll($selectorType, $selector); + + $message = sprintf( + '%d %s found on the page, but should be %d.', + count($nodes), + $this->getMatchingElementRepresentation($selectorType, $selector, count($nodes) !== 1), + $count + ); + + $this->assert(intval($count) === count($nodes), $message); + } + + /** + * Checks that specific element exists on the current page. + * + * @param string $selectorType element selector type (css, xpath) + * @param string|array $selector element selector + * @param ElementInterface $container document to check against + * + * @return NodeElement + * + * @throws ElementNotFoundException + */ + public function elementExists($selectorType, $selector, ElementInterface $container = null) + { + $container = $container ?: $this->session->getPage(); + $node = $container->find($selectorType, $selector); + + if (null === $node) { + if (is_array($selector)) { + $selector = implode(' ', $selector); + } + + throw new ElementNotFoundException($this->session, 'element', $selectorType, $selector); + } + + return $node; + } + + /** + * Checks that specific element does not exists on the current page. + * + * @param string $selectorType element selector type (css, xpath) + * @param string|array $selector element selector + * @param ElementInterface $container document to check against + * + * @throws ExpectationException + */ + public function elementNotExists($selectorType, $selector, ElementInterface $container = null) + { + $container = $container ?: $this->session->getPage(); + $node = $container->find($selectorType, $selector); + + $message = sprintf( + 'An %s appears on this page, but it should not.', + $this->getMatchingElementRepresentation($selectorType, $selector) + ); + + $this->assert(null === $node, $message); + } + + /** + * Checks that specific element contains text. + * + * @param string $selectorType element selector type (css, xpath) + * @param string|array $selector element selector + * @param string $text expected text + * + * @throws ElementTextException + */ + public function elementTextContains($selectorType, $selector, $text) + { + $element = $this->elementExists($selectorType, $selector); + $actual = $element->getText(); + $regex = '/'.preg_quote($text, '/').'/ui'; + + $message = sprintf( + 'The text "%s" was not found in the text of the %s.', + $text, + $this->getMatchingElementRepresentation($selectorType, $selector) + ); + + $this->assertElementText((bool) preg_match($regex, $actual), $message, $element); + } + + /** + * Checks that specific element does not contains text. + * + * @param string $selectorType element selector type (css, xpath) + * @param string|array $selector element selector + * @param string $text expected text + * + * @throws ElementTextException + */ + public function elementTextNotContains($selectorType, $selector, $text) + { + $element = $this->elementExists($selectorType, $selector); + $actual = $element->getText(); + $regex = '/'.preg_quote($text, '/').'/ui'; + + $message = sprintf( + 'The text "%s" appears in the text of the %s, but it should not.', + $text, + $this->getMatchingElementRepresentation($selectorType, $selector) + ); + + $this->assertElementText(!preg_match($regex, $actual), $message, $element); + } + + /** + * Checks that specific element contains HTML. + * + * @param string $selectorType element selector type (css, xpath) + * @param string|array $selector element selector + * @param string $html expected text + * + * @throws ElementHtmlException + */ + public function elementContains($selectorType, $selector, $html) + { + $element = $this->elementExists($selectorType, $selector); + $actual = $element->getHtml(); + $regex = '/'.preg_quote($html, '/').'/ui'; + + $message = sprintf( + 'The string "%s" was not found in the HTML of the %s.', + $html, + $this->getMatchingElementRepresentation($selectorType, $selector) + ); + + $this->assertElement((bool) preg_match($regex, $actual), $message, $element); + } + + /** + * Checks that specific element does not contains HTML. + * + * @param string $selectorType element selector type (css, xpath) + * @param string|array $selector element selector + * @param string $html expected text + * + * @throws ElementHtmlException + */ + public function elementNotContains($selectorType, $selector, $html) + { + $element = $this->elementExists($selectorType, $selector); + $actual = $element->getHtml(); + $regex = '/'.preg_quote($html, '/').'/ui'; + + $message = sprintf( + 'The string "%s" appears in the HTML of the %s, but it should not.', + $html, + $this->getMatchingElementRepresentation($selectorType, $selector) + ); + + $this->assertElement(!preg_match($regex, $actual), $message, $element); + } + + /** + * Checks that an attribute exists in an element. + * + * @param string $selectorType + * @param string|array $selector + * @param string $attribute + * + * @return NodeElement + * + * @throws ElementHtmlException + */ + public function elementAttributeExists($selectorType, $selector, $attribute) + { + $element = $this->elementExists($selectorType, $selector); + + $message = sprintf( + 'The attribute "%s" was not found in the %s.', + $attribute, + $this->getMatchingElementRepresentation($selectorType, $selector) + ); + + $this->assertElement($element->hasAttribute($attribute), $message, $element); + + return $element; + } + + /** + * Checks that an attribute of a specific elements contains text. + * + * @param string $selectorType + * @param string|array $selector + * @param string $attribute + * @param string $text + * + * @throws ElementHtmlException + */ + public function elementAttributeContains($selectorType, $selector, $attribute, $text) + { + $element = $this->elementAttributeExists($selectorType, $selector, $attribute); + $actual = $element->getAttribute($attribute); + $regex = '/'.preg_quote($text, '/').'/ui'; + + $message = sprintf( + 'The text "%s" was not found in the attribute "%s" of the %s.', + $text, + $attribute, + $this->getMatchingElementRepresentation($selectorType, $selector) + ); + + $this->assertElement((bool) preg_match($regex, $actual), $message, $element); + } + + /** + * Checks that an attribute of a specific elements does not contain text. + * + * @param string $selectorType + * @param string|array $selector + * @param string $attribute + * @param string $text + * + * @throws ElementHtmlException + */ + public function elementAttributeNotContains($selectorType, $selector, $attribute, $text) + { + $element = $this->elementAttributeExists($selectorType, $selector, $attribute); + $actual = $element->getAttribute($attribute); + $regex = '/'.preg_quote($text, '/').'/ui'; + + $message = sprintf( + 'The text "%s" was found in the attribute "%s" of the %s.', + $text, + $attribute, + $this->getMatchingElementRepresentation($selectorType, $selector) + ); + + $this->assertElement(!preg_match($regex, $actual), $message, $element); + } + + /** + * Checks that specific field exists on the current page. + * + * @param string $field field id|name|label|value + * @param TraversableElement $container document to check against + * + * @return NodeElement + * + * @throws ElementNotFoundException + */ + public function fieldExists($field, TraversableElement $container = null) + { + $container = $container ?: $this->session->getPage(); + $node = $container->findField($field); + + if (null === $node) { + throw new ElementNotFoundException($this->session, 'form field', 'id|name|label|value', $field); + } + + return $node; + } + + /** + * Checks that specific field does not exists on the current page. + * + * @param string $field field id|name|label|value + * @param TraversableElement $container document to check against + * + * @throws ExpectationException + */ + public function fieldNotExists($field, TraversableElement $container = null) + { + $container = $container ?: $this->session->getPage(); + $node = $container->findField($field); + + $this->assert(null === $node, sprintf('A field "%s" appears on this page, but it should not.', $field)); + } + + /** + * Checks that specific field have provided value. + * + * @param string $field field id|name|label|value + * @param string $value field value + * @param TraversableElement $container document to check against + * + * @throws ExpectationException + */ + public function fieldValueEquals($field, $value, TraversableElement $container = null) + { + $node = $this->fieldExists($field, $container); + $actual = $node->getValue(); + $regex = '/^'.preg_quote($value, '/').'$/ui'; + + $message = sprintf('The field "%s" value is "%s", but "%s" expected.', $field, $actual, $value); + + $this->assert((bool) preg_match($regex, $actual), $message); + } + + /** + * Checks that specific field have provided value. + * + * @param string $field field id|name|label|value + * @param string $value field value + * @param TraversableElement $container document to check against + * + * @throws ExpectationException + */ + public function fieldValueNotEquals($field, $value, TraversableElement $container = null) + { + $node = $this->fieldExists($field, $container); + $actual = $node->getValue(); + $regex = '/^'.preg_quote($value, '/').'$/ui'; + + $message = sprintf('The field "%s" value is "%s", but it should not be.', $field, $actual); + + $this->assert(!preg_match($regex, $actual), $message); + } + + /** + * Checks that specific checkbox is checked. + * + * @param string $field field id|name|label|value + * @param TraversableElement $container document to check against + * + * @throws ExpectationException + */ + public function checkboxChecked($field, TraversableElement $container = null) + { + $node = $this->fieldExists($field, $container); + + $this->assert($node->isChecked(), sprintf('Checkbox "%s" is not checked, but it should be.', $field)); + } + + /** + * Checks that specific checkbox is unchecked. + * + * @param string $field field id|name|label|value + * @param TraversableElement $container document to check against + * + * @throws ExpectationException + */ + public function checkboxNotChecked($field, TraversableElement $container = null) + { + $node = $this->fieldExists($field, $container); + + $this->assert(!$node->isChecked(), sprintf('Checkbox "%s" is checked, but it should not be.', $field)); + } + + /** + * Gets current url of the page. + * + * @return string + */ + protected function getCurrentUrlPath() + { + return $this->cleanUrl($this->session->getCurrentUrl()); + } + + /** + * Trims scriptname from the URL. + * + * @param string $url + * + * @return string + */ + protected function cleanUrl($url) + { + $parts = parse_url($url); + $fragment = empty($parts['fragment']) ? '' : '#' . $parts['fragment']; + + return preg_replace('/^\/[^\.\/]+\.php/', '', $parts['path']) . $fragment; + } + + /** + * Asserts a condition. + * + * @param bool $condition + * @param string $message Failure message + * + * @throws ExpectationException when the condition is not fulfilled + */ + private function assert($condition, $message) + { + if ($condition) { + return; + } + + throw new ExpectationException($message, $this->session); + } + + /** + * Asserts a condition involving the response text. + * + * @param bool $condition + * @param string $message Failure message + * + * @throws ResponseTextException when the condition is not fulfilled + */ + private function assertResponseText($condition, $message) + { + if ($condition) { + return; + } + + throw new ResponseTextException($message, $this->session); + } + + /** + * Asserts a condition on an element. + * + * @param bool $condition + * @param string $message Failure message + * @param Element $element + * + * @throws ElementHtmlException when the condition is not fulfilled + */ + private function assertElement($condition, $message, Element $element) + { + if ($condition) { + return; + } + + throw new ElementHtmlException($message, $this->session, $element); + } + + /** + * Asserts a condition involving the text of an element. + * + * @param bool $condition + * @param string $message Failure message + * @param Element $element + * + * @throws ElementTextException when the condition is not fulfilled + */ + private function assertElementText($condition, $message, Element $element) + { + if ($condition) { + return; + } + + throw new ElementTextException($message, $this->session, $element); + } + + /** + * @param string $selectorType + * @param string|array $selector + * @param boolean $plural + * + * @return string + */ + private function getMatchingElementRepresentation($selectorType, $selector, $plural = false) + { + $pluralization = $plural ? 's' : ''; + + if (in_array($selectorType, array('named', 'named_exact', 'named_partial')) + && is_array($selector) && 2 === count($selector) + ) { + return sprintf('%s%s matching locator "%s"', $selector[0], $pluralization, $selector[1]); + } + + if (is_array($selector)) { + $selector = implode(' ', $selector); + } + + return sprintf('element%s matching %s "%s"', $pluralization, $selectorType, $selector); + } +} diff --git a/core/vendor/behat/mink/tests/Driver/CoreDriverTest.php b/core/vendor/behat/mink/tests/Driver/CoreDriverTest.php new file mode 100644 index 0000000..d99b17f --- /dev/null +++ b/core/vendor/behat/mink/tests/Driver/CoreDriverTest.php @@ -0,0 +1,76 @@ +<?php + +namespace Behat\Mink\Tests\Driver; + +class CoreDriverTest extends \PHPUnit_Framework_TestCase +{ + public function testNoExtraMethods() + { + $interfaceRef = new \ReflectionClass('Behat\Mink\Driver\DriverInterface'); + $coreDriverRef = new \ReflectionClass('Behat\Mink\Driver\CoreDriver'); + + foreach ($coreDriverRef->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + $this->assertTrue( + $interfaceRef->hasMethod($method->getName()), + sprintf('CoreDriver should not implement methods which are not part of the DriverInterface but %s found', $method->getName()) + ); + } + } + + /** + * @dataProvider getDriverInterfaceMethods + */ + public function testInterfaceMethods(\ReflectionMethod $method) + { + $refl = new \ReflectionClass('Behat\Mink\Driver\CoreDriver'); + + $this->assertFalse( + $refl->getMethod($method->getName())->isAbstract(), + sprintf('CoreDriver should implement a dummy %s method', $method->getName()) + ); + + $driver = $this->getMockForAbstractClass('Behat\Mink\Driver\CoreDriver'); + + $this->setExpectedException('Behat\Mink\Exception\UnsupportedDriverActionException'); + call_user_func_array(array($driver, $method->getName()), $this->getArguments($method)); + } + + public function getDriverInterfaceMethods() + { + $ref = new \ReflectionClass('Behat\Mink\Driver\DriverInterface'); + + return array_map(function ($method) { + return array($method); + }, $ref->getMethods()); + } + + private function getArguments(\ReflectionMethod $method) + { + $arguments = array(); + + foreach ($method->getParameters() as $parameter) { + $arguments[] = $this->getArgument($parameter); + } + + return $arguments; + } + + private function getArgument(\ReflectionParameter $argument) + { + if ($argument->isOptional()) { + return $argument->getDefaultValue(); + } + + if ($argument->allowsNull()) { + return null; + } + + if ($argument->getClass()) { + return $this->getMockBuilder($argument->getClass()->getName()) + ->disableOriginalConstructor() + ->getMock(); + } + + return null; + } +} diff --git a/core/vendor/behat/mink/tests/Element/DocumentElementTest.php b/core/vendor/behat/mink/tests/Element/DocumentElementTest.php new file mode 100644 index 0000000..bb42b68 --- /dev/null +++ b/core/vendor/behat/mink/tests/Element/DocumentElementTest.php @@ -0,0 +1,446 @@ +<?php + +namespace Behat\Mink\Tests\Element; + +use Behat\Mink\Element\DocumentElement; + +class DocumentElementTest extends ElementTest +{ + /** + * Page. + * + * @var DocumentElement + */ + private $document; + + protected function setUp() + { + parent::setUp(); + $this->document = new DocumentElement($this->session); + } + + public function testGetSession() + { + $this->assertEquals($this->session, $this->document->getSession()); + } + + public function testFindAll() + { + $xpath = 'h3[a]'; + $css = 'h3 > a'; + + $this->driver + ->expects($this->exactly(2)) + ->method('find') + ->will($this->returnValueMap(array( + array('//html/' . $xpath, array(2, 3, 4)), + array('//html/' . $css, array(1, 2)), + ))); + + $this->selectors + ->expects($this->exactly(2)) + ->method('selectorToXpath') + ->will($this->returnValueMap(array( + array('xpath', $xpath, $xpath), + array('css', $css, $css), + ))); + + $this->assertEquals(3, count($this->document->findAll('xpath', $xpath))); + $this->assertEquals(2, count($this->document->findAll('css', $css))); + } + + public function testFind() + { + $this->driver + ->expects($this->exactly(3)) + ->method('find') + ->with('//html/h3[a]') + ->will($this->onConsecutiveCalls(array(2, 3, 4), array(1, 2), array())); + + $xpath = 'h3[a]'; + $css = 'h3 > a'; + + $this->selectors + ->expects($this->exactly(3)) + ->method('selectorToXpath') + ->will($this->returnValueMap(array( + array('xpath', $xpath, $xpath), + array('xpath', $xpath, $xpath), + array('css', $css, $xpath), + ))); + + $this->assertEquals(2, $this->document->find('xpath', $xpath)); + $this->assertEquals(1, $this->document->find('css', $css)); + $this->assertNull($this->document->find('xpath', $xpath)); + } + + public function testFindField() + { + $this->mockNamedFinder( + '//field', + array('field1', 'field2', 'field3'), + array('field', 'some field') + ); + + $this->assertEquals('field1', $this->document->findField('some field')); + $this->assertEquals(null, $this->document->findField('some field')); + } + + public function testFindLink() + { + $this->mockNamedFinder( + '//link', + array('link1', 'link2', 'link3'), + array('link', 'some link') + ); + + $this->assertEquals('link1', $this->document->findLink('some link')); + $this->assertEquals(null, $this->document->findLink('some link')); + } + + public function testFindButton() + { + $this->mockNamedFinder( + '//button', + array('button1', 'button2', 'button3'), + array('button', 'some button') + ); + + $this->assertEquals('button1', $this->document->findButton('some button')); + $this->assertEquals(null, $this->document->findButton('some button')); + } + + public function testFindById() + { + $xpath = '//*[@id=some-item-2]'; + + $this->mockNamedFinder($xpath, array(array('id2', 'id3'), array()), array('id', 'some-item-2')); + + $this->assertEquals('id2', $this->document->findById('some-item-2')); + $this->assertEquals(null, $this->document->findById('some-item-2')); + } + + public function testHasSelector() + { + $this->driver + ->expects($this->exactly(2)) + ->method('find') + ->with('//html/some xpath') + ->will($this->onConsecutiveCalls(array('id2', 'id3'), array())); + + $this->selectors + ->expects($this->exactly(2)) + ->method('selectorToXpath') + ->with('xpath', 'some xpath') + ->will($this->returnValue('some xpath')); + + $this->assertTrue($this->document->has('xpath', 'some xpath')); + $this->assertFalse($this->document->has('xpath', 'some xpath')); + } + + public function testHasContent() + { + $this->mockNamedFinder( + '//some content', + array('item1', 'item2'), + array('content', 'some content') + ); + + $this->assertTrue($this->document->hasContent('some content')); + $this->assertFalse($this->document->hasContent('some content')); + } + + public function testHasLink() + { + $this->mockNamedFinder( + '//link', + array('link1', 'link2', 'link3'), + array('link', 'some link') + ); + + $this->assertTrue($this->document->hasLink('some link')); + $this->assertFalse($this->document->hasLink('some link')); + } + + public function testHasButton() + { + $this->mockNamedFinder( + '//button', + array('button1', 'button2', 'button3'), + array('button', 'some button') + ); + + $this->assertTrue($this->document->hasButton('some button')); + $this->assertFalse($this->document->hasButton('some button')); + } + + public function testHasField() + { + $this->mockNamedFinder( + '//field', + array('field1', 'field2', 'field3'), + array('field', 'some field') + ); + + $this->assertTrue($this->document->hasField('some field')); + $this->assertFalse($this->document->hasField('some field')); + } + + public function testHasCheckedField() + { + $checkbox = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $checkbox + ->expects($this->exactly(2)) + ->method('isChecked') + ->will($this->onConsecutiveCalls(true, false)); + + $this->mockNamedFinder( + '//field', + array(array($checkbox), array(), array($checkbox)), + array('field', 'some checkbox'), + 3 + ); + + $this->assertTrue($this->document->hasCheckedField('some checkbox')); + $this->assertFalse($this->document->hasCheckedField('some checkbox')); + $this->assertFalse($this->document->hasCheckedField('some checkbox')); + } + + public function testHasUncheckedField() + { + $checkbox = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $checkbox + ->expects($this->exactly(2)) + ->method('isChecked') + ->will($this->onConsecutiveCalls(true, false)); + + $this->mockNamedFinder( + '//field', + array(array($checkbox), array(), array($checkbox)), + array('field', 'some checkbox'), + 3 + ); + + $this->assertFalse($this->document->hasUncheckedField('some checkbox')); + $this->assertFalse($this->document->hasUncheckedField('some checkbox')); + $this->assertTrue($this->document->hasUncheckedField('some checkbox')); + } + + public function testHasSelect() + { + $this->mockNamedFinder( + '//select', + array('select'), + array('select', 'some select field') + ); + + $this->assertTrue($this->document->hasSelect('some select field')); + $this->assertFalse($this->document->hasSelect('some select field')); + } + + public function testHasTable() + { + $this->mockNamedFinder( + '//table', + array('table'), + array('table', 'some table') + ); + + $this->assertTrue($this->document->hasTable('some table')); + $this->assertFalse($this->document->hasTable('some table')); + } + + public function testClickLink() + { + $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $node + ->expects($this->once()) + ->method('click'); + + $this->mockNamedFinder( + '//link', + array($node), + array('link', 'some link') + ); + + $this->document->clickLink('some link'); + $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException'); + $this->document->clickLink('some link'); + } + + public function testClickButton() + { + $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $node + ->expects($this->once()) + ->method('press'); + + $this->mockNamedFinder( + '//button', + array($node), + array('button', 'some button') + ); + + $this->document->pressButton('some button'); + $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException'); + $this->document->pressButton('some button'); + } + + public function testFillField() + { + $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $node + ->expects($this->once()) + ->method('setValue') + ->with('some val'); + + $this->mockNamedFinder( + '//field', + array($node), + array('field', 'some field') + ); + + $this->document->fillField('some field', 'some val'); + $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException'); + $this->document->fillField('some field', 'some val'); + } + + public function testCheckField() + { + $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $node + ->expects($this->once()) + ->method('check'); + + $this->mockNamedFinder( + '//field', + array($node), + array('field', 'some field') + ); + + $this->document->checkField('some field'); + $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException'); + $this->document->checkField('some field'); + } + + public function testUncheckField() + { + $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $node + ->expects($this->once()) + ->method('uncheck'); + + $this->mockNamedFinder( + '//field', + array($node), + array('field', 'some field') + ); + + $this->document->uncheckField('some field'); + $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException'); + $this->document->uncheckField('some field'); + } + + public function testSelectField() + { + $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $node + ->expects($this->once()) + ->method('selectOption') + ->with('option2'); + + $this->mockNamedFinder( + '//field', + array($node), + array('field', 'some field') + ); + + $this->document->selectFieldOption('some field', 'option2'); + $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException'); + $this->document->selectFieldOption('some field', 'option2'); + } + + public function testAttachFileToField() + { + $node = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $node + ->expects($this->once()) + ->method('attachFile') + ->with('/path/to/file'); + + $this->mockNamedFinder( + '//field', + array($node), + array('field', 'some field') + ); + + $this->document->attachFileToField('some field', '/path/to/file'); + $this->setExpectedException('Behat\Mink\Exception\ElementNotFoundException'); + $this->document->attachFileToField('some field', '/path/to/file'); + } + + public function testGetContent() + { + $expects = 'page content'; + $this->driver + ->expects($this->once()) + ->method('getContent') + ->will($this->returnValue($expects)); + + $this->assertEquals($expects, $this->document->getContent()); + } + + public function testGetText() + { + $expects = 'val1'; + $this->driver + ->expects($this->once()) + ->method('getText') + ->with('//html') + ->will($this->returnValue($expects)); + + $this->assertEquals($expects, $this->document->getText()); + } + + public function testGetHtml() + { + $expects = 'val1'; + $this->driver + ->expects($this->once()) + ->method('getHtml') + ->with('//html') + ->will($this->returnValue($expects)); + + $this->assertEquals($expects, $this->document->getHtml()); + } + + public function testGetOuterHtml() + { + $expects = 'val1'; + $this->driver + ->expects($this->once()) + ->method('getOuterHtml') + ->with('//html') + ->will($this->returnValue($expects)); + + $this->assertEquals($expects, $this->document->getOuterHtml()); + } +} diff --git a/core/vendor/behat/mink/tests/Element/ElementTest.php b/core/vendor/behat/mink/tests/Element/ElementTest.php new file mode 100644 index 0000000..1f9240a --- /dev/null +++ b/core/vendor/behat/mink/tests/Element/ElementTest.php @@ -0,0 +1,76 @@ +<?php + +namespace Behat\Mink\Tests\Element; + +use Behat\Mink\Driver\DriverInterface; +use Behat\Mink\Session; +use Behat\Mink\Selector\SelectorsHandler; + +abstract class ElementTest extends \PHPUnit_Framework_TestCase +{ + /** + * Session. + * + * @var Session + */ + protected $session; + + /** + * @var DriverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $driver; + + /** + * Selectors. + * + * @var SelectorsHandler|\PHPUnit_Framework_MockObject_MockObject + */ + protected $selectors; + + protected function setUp() + { + $this->driver = $this->getMockBuilder('Behat\Mink\Driver\DriverInterface')->getMock(); + $this->driver + ->expects($this->once()) + ->method('setSession'); + + $this->selectors = $this->getMockBuilder('Behat\Mink\Selector\SelectorsHandler')->getMock(); + $this->session = new Session($this->driver, $this->selectors); + + $this->selectors + ->expects($this->any()) + ->method('xpathLiteral') + ->will($this->returnArgument(0)); + } + + protected function mockNamedFinder($xpath, array $results, $locator, $times = 2) + { + if (!is_array($results[0])) { + $results = array($results, array()); + } + + // In case of empty results, a second call will be done using the partial selector + $processedResults = array(); + foreach ($results as $result) { + $processedResults[] = $result; + if (empty($result)) { + $processedResults[] = $result; + $times++; + } + } + + $returnValue = call_user_func_array(array($this, 'onConsecutiveCalls'), $processedResults); + + $this->driver + ->expects($this->exactly($times)) + ->method('find') + ->with('//html' . $xpath) + ->will($returnValue); + + $this->selectors + ->expects($this->exactly($times)) + ->method('selectorToXpath') + ->with($this->logicalOr('named_exact', 'named_partial'), $locator) + ->will($this->returnValue($xpath)); + } +} diff --git a/core/vendor/behat/mink/tests/Element/NodeElementTest.php b/core/vendor/behat/mink/tests/Element/NodeElementTest.php new file mode 100644 index 0000000..b6a8bde --- /dev/null +++ b/core/vendor/behat/mink/tests/Element/NodeElementTest.php @@ -0,0 +1,593 @@ +<?php + +namespace Behat\Mink\Tests\Element; + +use Behat\Mink\Element\NodeElement; + +class NodeElementTest extends ElementTest +{ + public function testGetXpath() + { + $node = new NodeElement('some custom xpath', $this->session); + + $this->assertEquals('some custom xpath', $node->getXpath()); + $this->assertNotEquals('not some custom xpath', $node->getXpath()); + } + + public function testGetText() + { + $expected = 'val1'; + $node = new NodeElement('text_tag', $this->session); + + $this->driver + ->expects($this->once()) + ->method('getText') + ->with('text_tag') + ->will($this->returnValue($expected)); + + $this->assertEquals($expected, $node->getText()); + } + + public function testGetOuterHtml() + { + $expected = 'val1'; + $node = new NodeElement('text_tag', $this->session); + + $this->driver + ->expects($this->once()) + ->method('getOuterHtml') + ->with('text_tag') + ->will($this->returnValue($expected)); + + $this->assertEquals($expected, $node->getOuterHtml()); + } + + public function testElementIsValid() + { + $elementXpath = 'some xpath'; + $node = new NodeElement($elementXpath, $this->session); + + $this->driver + ->expects($this->once()) + ->method('find') + ->with($elementXpath) + ->will($this->returnValue(array($elementXpath))); + + $this->assertTrue($node->isValid()); + } + + public function testElementIsNotValid() + { + $node = new NodeElement('some xpath', $this->session); + + $this->driver + ->expects($this->exactly(2)) + ->method('find') + ->with('some xpath') + ->will($this->onConsecutiveCalls(array(), array('xpath1', 'xpath2'))); + + $this->assertFalse($node->isValid(), 'no elements found is invalid element'); + $this->assertFalse($node->isValid(), 'more then 1 element found is invalid element'); + } + + public function testWaitForSuccess() + { + $callCounter = 0; + $node = new NodeElement('some xpath', $this->session); + + $result = $node->waitFor(5, function ($givenNode) use (&$callCounter) { + $callCounter++; + + if (1 === $callCounter) { + return null; + } elseif (2 === $callCounter) { + return false; + } elseif (3 === $callCounter) { + return array(); + } + + return $givenNode; + }); + + $this->assertEquals(4, $callCounter, '->waitFor() tries to locate element several times before failing'); + $this->assertSame($node, $result, '->waitFor() returns node found in callback'); + } + + public function testWaitForTimeout() + { + $node = new NodeElement('some xpath', $this->session); + + $expectedTimeout = 2; + $startTime = microtime(true); + $result = $node->waitFor($expectedTimeout, function () { + return null; + }); + $endTime = microtime(true); + + $this->assertNull($result, '->waitFor() returns whatever callback gives'); + $this->assertEquals($expectedTimeout, round($endTime - $startTime)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testWaitForFailure() + { + $node = new NodeElement('some xpath', $this->session); + $node->waitFor(5, 'not a callable'); + } + + public function testHasAttribute() + { + $node = new NodeElement('input_tag', $this->session); + + $this->driver + ->expects($this->exactly(2)) + ->method('getAttribute') + ->with('input_tag', 'href') + ->will($this->onConsecutiveCalls(null, 'http://...')); + + $this->assertFalse($node->hasAttribute('href')); + $this->assertTrue($node->hasAttribute('href')); + } + + public function testGetAttribute() + { + $expected = 'http://...'; + $node = new NodeElement('input_tag', $this->session); + + $this->driver + ->expects($this->once()) + ->method('getAttribute') + ->with('input_tag', 'href') + ->will($this->returnValue($expected)); + + $this->assertEquals($expected, $node->getAttribute('href')); + } + + public function testHasClass() + { + $node = new NodeElement('input_tag', $this->session); + + $this->driver + ->expects($this->exactly(6)) + ->method('getAttribute') + ->with('input_tag', 'class') + ->will($this->returnValue('class1 class2')); + + $this->assertTrue($node->hasClass('class1')); + $this->assertTrue($node->hasClass('class2')); + $this->assertFalse($node->hasClass('class3')); + } + + public function testHasClassWithoutArgument() + { + $node = new NodeElement('input_tag', $this->session); + + $this->driver + ->expects($this->once()) + ->method('getAttribute') + ->with('input_tag', 'class') + ->will($this->returnValue(null)); + + $this->assertFalse($node->hasClass('class3')); + } + + public function testGetValue() + { + $expected = 'val1'; + $node = new NodeElement('input_tag', $this->session); + + $this->driver + ->expects($this->once()) + ->method('getValue') + ->with('input_tag') + ->will($this->returnValue($expected)); + + $this->assertEquals($expected, $node->getValue()); + } + + public function testSetValue() + { + $expected = 'new_val'; + $node = new NodeElement('input_tag', $this->session); + + $this->driver + ->expects($this->once()) + ->method('setValue') + ->with('input_tag', $expected); + + $node->setValue($expected); + } + + public function testClick() + { + $node = new NodeElement('link_or_button', $this->session); + + $this->driver + ->expects($this->once()) + ->method('click') + ->with('link_or_button'); + + $node->click(); + } + + public function testPress() + { + $node = new NodeElement('link_or_button', $this->session); + + $this->driver + ->expects($this->once()) + ->method('click') + ->with('link_or_button'); + + $node->press(); + } + + public function testRightClick() + { + $node = new NodeElement('elem', $this->session); + + $this->driver + ->expects($this->once()) + ->method('rightClick') + ->with('elem'); + + $node->rightClick(); + } + + public function testDoubleClick() + { + $node = new NodeElement('elem', $this->session); + + $this->driver + ->expects($this->once()) + ->method('doubleClick') + ->with('elem'); + + $node->doubleClick(); + } + + public function testCheck() + { + $node = new NodeElement('checkbox_or_radio', $this->session); + + $this->driver + ->expects($this->once()) + ->method('check') + ->with('checkbox_or_radio'); + + $node->check(); + } + + public function testUncheck() + { + $node = new NodeElement('checkbox_or_radio', $this->session); + + $this->driver + ->expects($this->once()) + ->method('uncheck') + ->with('checkbox_or_radio'); + + $node->uncheck(); + } + + public function testSelectOption() + { + $node = new NodeElement('select', $this->session); + $option = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + $option + ->expects($this->once()) + ->method('getValue') + ->will($this->returnValue('item1')); + + $this->driver + ->expects($this->once()) + ->method('getTagName') + ->with('select') + ->will($this->returnValue('select')); + + $this->driver + ->expects($this->once()) + ->method('find') + ->with('select/option') + ->will($this->returnValue(array($option))); + + $this->selectors + ->expects($this->once()) + ->method('selectorToXpath') + ->with('named_exact', array('option', 'item1')) + ->will($this->returnValue('option')); + + $this->driver + ->expects($this->once()) + ->method('selectOption') + ->with('select', 'item1', false); + + $node->selectOption('item1'); + } + + /** + * @expectedException \Behat\Mink\Exception\ElementNotFoundException + */ + public function testSelectOptionNotFound() + { + $node = new NodeElement('select', $this->session); + + $this->driver + ->expects($this->once()) + ->method('getTagName') + ->with('select') + ->will($this->returnValue('select')); + + $this->driver + ->expects($this->exactly(2)) + ->method('find') + ->with('select/option') + ->will($this->returnValue(array())); + + $this->selectors + ->expects($this->exactly(2)) + ->method('selectorToXpath') + ->with($this->logicalOr('named_exact', 'named_partial'), array('option', 'item1')) + ->will($this->returnValue('option')); + + $node->selectOption('item1'); + } + + public function testSelectOptionOtherTag() + { + $node = new NodeElement('div', $this->session); + + $this->driver + ->expects($this->once()) + ->method('getTagName') + ->with('div') + ->will($this->returnValue('div')); + + $this->driver + ->expects($this->once()) + ->method('selectOption') + ->with('div', 'item1', false); + + $node->selectOption('item1'); + } + + public function testGetTagName() + { + $node = new NodeElement('html//h3', $this->session); + + $this->driver + ->expects($this->once()) + ->method('getTagName') + ->with('html//h3') + ->will($this->returnValue('h3')); + + $this->assertEquals('h3', $node->getTagName()); + } + + public function testGetParent() + { + $node = new NodeElement('elem', $this->session); + $parent = $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + + $this->driver + ->expects($this->once()) + ->method('find') + ->with('elem/..') + ->will($this->returnValue(array($parent))); + + $this->selectors + ->expects($this->once()) + ->method('selectorToXpath') + ->with('xpath', '..') + ->will($this->returnValue('..')); + + $this->assertSame($parent, $node->getParent()); + } + + public function testAttachFile() + { + $node = new NodeElement('elem', $this->session); + + $this->driver + ->expects($this->once()) + ->method('attachFile') + ->with('elem', 'path'); + + $node->attachFile('path'); + } + + public function testIsVisible() + { + $node = new NodeElement('some_xpath', $this->session); + + $this->driver + ->expects($this->exactly(2)) + ->method('isVisible') + ->with('some_xpath') + ->will($this->onConsecutiveCalls(true, false)); + + $this->assertTrue($node->isVisible()); + $this->assertFalse($node->isVisible()); + } + + public function testIsChecked() + { + $node = new NodeElement('some_xpath', $this->session); + + $this->driver + ->expects($this->exactly(2)) + ->method('isChecked') + ->with('some_xpath') + ->will($this->onConsecutiveCalls(true, false)); + + $this->assertTrue($node->isChecked()); + $this->assertFalse($node->isChecked()); + } + + public function testIsSelected() + { + $node = new NodeElement('some_xpath', $this->session); + + $this->driver + ->expects($this->exactly(2)) + ->method('isSelected') + ->with('some_xpath') + ->will($this->onConsecutiveCalls(true, false)); + + $this->assertTrue($node->isSelected()); + $this->assertFalse($node->isSelected()); + } + + public function testFocus() + { + $node = new NodeElement('some-element', $this->session); + + $this->driver + ->expects($this->once()) + ->method('focus') + ->with('some-element'); + + $node->focus(); + } + + public function testBlur() + { + $node = new NodeElement('some-element', $this->session); + + $this->driver + ->expects($this->once()) + ->method('blur') + ->with('some-element'); + + $node->blur(); + } + + public function testMouseOver() + { + $node = new NodeElement('some-element', $this->session); + + $this->driver + ->expects($this->once()) + ->method('mouseOver') + ->with('some-element'); + + $node->mouseOver(); + } + + public function testDragTo() + { + $node = new NodeElement('some_tag1', $this->session); + + $target = $this->getMock('Behat\Mink\Element\ElementInterface'); + $target->expects($this->any()) + ->method('getXPath') + ->will($this->returnValue('some_tag2')); + + $this->driver + ->expects($this->once()) + ->method('dragTo') + ->with('some_tag1', 'some_tag2'); + + $node->dragTo($target); + } + + public function testKeyPress() + { + $node = new NodeElement('elem', $this->session); + + $this->driver + ->expects($this->once()) + ->method('keyPress') + ->with('elem', 'key'); + + $node->keyPress('key'); + } + + public function testKeyDown() + { + $node = new NodeElement('elem', $this->session); + + $this->driver + ->expects($this->once()) + ->method('keyDown') + ->with('elem', 'key'); + + $node->keyDown('key'); + } + + public function testKeyUp() + { + $node = new NodeElement('elem', $this->session); + + $this->driver + ->expects($this->once()) + ->method('keyUp') + ->with('elem', 'key'); + + $node->keyUp('key'); + } + + public function testSubmitForm() + { + $node = new NodeElement('some_xpath', $this->session); + + $this->driver + ->expects($this->once()) + ->method('submitForm') + ->with('some_xpath'); + + $node->submit(); + } + + public function testFindAllUnion() + { + $node = new NodeElement('some_xpath', $this->session); + $xpath = "some_tag1 | some_tag2[@foo =\n 'bar|'']\n | some_tag3[foo | bar]"; + $expected = "some_xpath/some_tag1 | some_xpath/some_tag2[@foo =\n 'bar|''] | some_xpath/some_tag3[foo | bar]"; + + $this->driver + ->expects($this->exactly(1)) + ->method('find') + ->will($this->returnValueMap(array( + array($expected, array(2, 3, 4)), + ))); + + $this->selectors + ->expects($this->exactly(1)) + ->method('selectorToXpath') + ->will($this->returnValueMap(array( + array('xpath', $xpath, $xpath), + ))); + + $this->assertEquals(3, count($node->findAll('xpath', $xpath))); + } + + public function testFindAllParentUnion() + { + $node = new NodeElement('some_xpath | another_xpath', $this->session); + $xpath = "some_tag1 | some_tag2"; + $expectedPrefixed = "(some_xpath | another_xpath)/some_tag1 | (some_xpath | another_xpath)/some_tag2"; + + $this->driver + ->expects($this->exactly(1)) + ->method('find') + ->will($this->returnValueMap(array( + array($expectedPrefixed, array(2, 3, 4)), + ))); + + $this->selectors + ->expects($this->exactly(1)) + ->method('selectorToXpath') + ->will($this->returnValueMap(array( + array('xpath', $xpath, $xpath), + ))); + + $this->assertEquals(3, count($node->findAll('xpath', $xpath))); + } +} diff --git a/core/vendor/behat/mink/tests/Exception/ElementExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ElementExceptionTest.php new file mode 100644 index 0000000..021e8d5 --- /dev/null +++ b/core/vendor/behat/mink/tests/Exception/ElementExceptionTest.php @@ -0,0 +1,39 @@ +<?php + +namespace Behat\Mink\Tests\Exception; + +use Behat\Mink\Exception\ElementException; + +class ElementExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testMessage() + { + $exception = new ElementException($this->getElementMock(), new \Exception('Something went wrong')); + + $expectedMessage = "Exception thrown by element XPath\nSomething went wrong"; + $this->assertEquals($expectedMessage, $exception->getMessage()); + $this->assertEquals($expectedMessage, (string) $exception); + } + + public function testElement() + { + $element = $this->getElementMock(); + + $exception = new ElementException($element, new \Exception('Something went wrong')); + + $this->assertSame($element, $exception->getElement()); + } + + private function getElementMock() + { + $mock = $this->getMockBuilder('Behat\Mink\Element\Element') + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects($this->any()) + ->method('getXPath') + ->will($this->returnValue('element XPath')); + + return $mock; + } +} diff --git a/core/vendor/behat/mink/tests/Exception/ElementHtmlExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ElementHtmlExceptionTest.php new file mode 100644 index 0000000..63d39dd --- /dev/null +++ b/core/vendor/behat/mink/tests/Exception/ElementHtmlExceptionTest.php @@ -0,0 +1,61 @@ +<?php + +namespace Behat\Mink\Tests\Exception; + +use Behat\Mink\Exception\ElementHtmlException; + +class ElementHtmlExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testExceptionToString() + { + $driver = $this->getMock('Behat\Mink\Driver\DriverInterface'); + $element = $this->getElementMock(); + + $session = $this->getSessionMock(); + $session->expects($this->any()) + ->method('getDriver') + ->will($this->returnValue($driver)); + $session->expects($this->any()) + ->method('getStatusCode') + ->will($this->returnValue(200)); + $session->expects($this->any()) + ->method('getCurrentUrl') + ->will($this->returnValue('http://localhost/test')); + + $element->expects($this->any()) + ->method('getOuterHtml') + ->will($this->returnValue("<div>\n <h1>Hello world</h1>\n <p>Test</p>\n</div>")); + + $expected = <<<'TXT' +Html error + ++--[ HTTP/1.1 200 | http://localhost/test | %s ] +| +| <div> +| <h1>Hello world</h1> +| <p>Test</p> +| </div> +| +TXT; + + $expected = sprintf($expected.' ', get_class($driver)); + + $exception = new ElementHtmlException('Html error', $session, $element); + + $this->assertEquals($expected, $exception->__toString()); + } + + private function getSessionMock() + { + return $this->getMockBuilder('Behat\Mink\Session') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getElementMock() + { + return $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/core/vendor/behat/mink/tests/Exception/ElementNotFoundExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ElementNotFoundExceptionTest.php new file mode 100644 index 0000000..8cb4e5d --- /dev/null +++ b/core/vendor/behat/mink/tests/Exception/ElementNotFoundExceptionTest.php @@ -0,0 +1,34 @@ +<?php + +namespace Behat\Mink\Tests\Exception; + +use Behat\Mink\Exception\ElementNotFoundException; + +class ElementNotFoundExceptionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideExceptionMessage + */ + public function testBuildMessage($message, $type, $selector = null, $locator = null) + { + $session = $this->getMockBuilder('Behat\Mink\Session') + ->disableOriginalConstructor() + ->getMock(); + + $exception = new ElementNotFoundException($session, $type, $selector, $locator); + + $this->assertEquals($message, $exception->getMessage()); + } + + public function provideExceptionMessage() + { + return array( + array('Tag not found.', null), + array('Field not found.', 'field'), + array('Tag matching locator "foobar" not found.', null, null, 'foobar'), + array('Tag matching css "foobar" not found.', null, 'css', 'foobar'), + array('Field matching xpath "foobar" not found.', 'Field', 'xpath', 'foobar'), + array('Tag with name "foobar" not found.', null, 'name', 'foobar'), + ); + } +} diff --git a/core/vendor/behat/mink/tests/Exception/ElementTextExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ElementTextExceptionTest.php new file mode 100644 index 0000000..73cef5b --- /dev/null +++ b/core/vendor/behat/mink/tests/Exception/ElementTextExceptionTest.php @@ -0,0 +1,59 @@ +<?php + +namespace Behat\Mink\Tests\Exception; + +use Behat\Mink\Exception\ElementTextException; + +class ElementTextExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testExceptionToString() + { + $driver = $this->getMock('Behat\Mink\Driver\DriverInterface'); + $element = $this->getElementMock(); + + $session = $this->getSessionMock(); + $session->expects($this->any()) + ->method('getDriver') + ->will($this->returnValue($driver)); + $session->expects($this->any()) + ->method('getStatusCode') + ->will($this->returnValue(200)); + $session->expects($this->any()) + ->method('getCurrentUrl') + ->will($this->returnValue('http://localhost/test')); + + $element->expects($this->any()) + ->method('getText') + ->will($this->returnValue("Hello world\nTest\n")); + + $expected = <<<'TXT' +Text error + ++--[ HTTP/1.1 200 | http://localhost/test | %s ] +| +| Hello world +| Test +| +TXT; + + $expected = sprintf($expected.' ', get_class($driver)); + + $exception = new ElementTextException('Text error', $session, $element); + + $this->assertEquals($expected, $exception->__toString()); + } + + private function getSessionMock() + { + return $this->getMockBuilder('Behat\Mink\Session') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getElementMock() + { + return $this->getMockBuilder('Behat\Mink\Element\NodeElement') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/core/vendor/behat/mink/tests/Exception/ExpectationExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ExpectationExceptionTest.php new file mode 100644 index 0000000..23e53bc --- /dev/null +++ b/core/vendor/behat/mink/tests/Exception/ExpectationExceptionTest.php @@ -0,0 +1,84 @@ +<?php + +namespace Behat\Mink\Tests\Exception; + +use Behat\Mink\Exception\ExpectationException; + +class ExpectationExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testEmptyMessageAndPreviousException() + { + $exception = new ExpectationException('', $this->getSessionMock(), new \Exception('Something failed')); + + $this->assertEquals('Something failed', $exception->getMessage()); + } + + public function testExceptionToString() + { + $driver = $this->getMock('Behat\Mink\Driver\DriverInterface'); + $page = $this->getPageMock(); + + $session = $this->getSessionMock(); + $session->expects($this->any()) + ->method('getDriver') + ->will($this->returnValue($driver)); + $session->expects($this->any()) + ->method('getPage') + ->will($this->returnValue($page)); + $session->expects($this->any()) + ->method('getStatusCode') + ->will($this->returnValue(200)); + $session->expects($this->any()) + ->method('getCurrentUrl') + ->will($this->returnValue('http://localhost/test')); + + $html = "<html><head><title>Hello\n\n

Hello world

\n

Test

\n"; + $page->expects($this->any()) + ->method('getContent') + ->will($this->returnValue($html)); + + $expected = <<<'TXT' +Expectation failure + ++--[ HTTP/1.1 200 | http://localhost/test | %s ] +| +| +|

Hello world

+|

Test

+| +| +TXT; + + $expected = sprintf($expected.' ', get_class($driver)); + + $exception = new ExpectationException('Expectation failure', $session); + + $this->assertEquals($expected, $exception->__toString()); + } + + public function testExceptionWhileRenderingString() + { + $session = $this->getSessionMock(); + $session->expects($this->any()) + ->method('getPage') + ->will($this->throwException(new \Exception('Broken page'))); + + $exception = new ExpectationException('Expectation failure', $session); + + $this->assertEquals('Expectation failure', $exception->__toString()); + } + + private function getSessionMock() + { + return $this->getMockBuilder('Behat\Mink\Session') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getPageMock() + { + return $this->getMockBuilder('Behat\Mink\Element\DocumentElement') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/core/vendor/behat/mink/tests/Exception/ResponseTextExceptionTest.php b/core/vendor/behat/mink/tests/Exception/ResponseTextExceptionTest.php new file mode 100644 index 0000000..4651b7c --- /dev/null +++ b/core/vendor/behat/mink/tests/Exception/ResponseTextExceptionTest.php @@ -0,0 +1,62 @@ +getMock('Behat\Mink\Driver\DriverInterface'); + $page = $this->getPageMock(); + + $session = $this->getSessionMock(); + $session->expects($this->any()) + ->method('getDriver') + ->will($this->returnValue($driver)); + $session->expects($this->any()) + ->method('getPage') + ->will($this->returnValue($page)); + $session->expects($this->any()) + ->method('getStatusCode') + ->will($this->returnValue(200)); + $session->expects($this->any()) + ->method('getCurrentUrl') + ->will($this->returnValue('http://localhost/test')); + + $page->expects($this->any()) + ->method('getText') + ->will($this->returnValue("Hello world\nTest\n")); + + $expected = <<<'TXT' +Text error + ++--[ HTTP/1.1 200 | http://localhost/test | %s ] +| +| Hello world +| Test +| +TXT; + + $expected = sprintf($expected.' ', get_class($driver)); + + $exception = new ResponseTextException('Text error', $session); + + $this->assertEquals($expected, $exception->__toString()); + } + + private function getSessionMock() + { + return $this->getMockBuilder('Behat\Mink\Session') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getPageMock() + { + return $this->getMockBuilder('Behat\Mink\Element\DocumentElement') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/core/vendor/behat/mink/tests/MinkTest.php b/core/vendor/behat/mink/tests/MinkTest.php new file mode 100644 index 0000000..11d6466 --- /dev/null +++ b/core/vendor/behat/mink/tests/MinkTest.php @@ -0,0 +1,246 @@ +mink = new Mink(); + } + + public function testRegisterSession() + { + $session = $this->getSessionMock(); + + $this->assertFalse($this->mink->hasSession('not_registered')); + $this->assertFalse($this->mink->hasSession('js')); + $this->assertFalse($this->mink->hasSession('my')); + + $this->mink->registerSession('my', $session); + + $this->assertTrue($this->mink->hasSession('my')); + $this->assertFalse($this->mink->hasSession('not_registered')); + $this->assertFalse($this->mink->hasSession('js')); + } + + public function testRegisterSessionThroughConstructor() + { + $mink = new Mink(array('my' => $this->getSessionMock())); + + $this->assertTrue($mink->hasSession('my')); + } + + public function testSessionAutostop() + { + $session1 = $this->getSessionMock(); + $session2 = $this->getSessionMock(); + $this->mink->registerSession('my1', $session1); + $this->mink->registerSession('my2', $session2); + + $session1 + ->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + $session1 + ->expects($this->once()) + ->method('stop'); + $session2 + ->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(false)); + $session2 + ->expects($this->never()) + ->method('stop'); + + unset($this->mink); + } + + public function testNotStartedSession() + { + $session = $this->getSessionMock(); + + $session + ->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(false)); + $session + ->expects($this->once()) + ->method('start'); + + $this->mink->registerSession('mock_session', $session); + $this->assertSame($session, $this->mink->getSession('mock_session')); + + $this->setExpectedException('InvalidArgumentException'); + + $this->mink->getSession('not_registered'); + } + + public function testGetAlreadyStartedSession() + { + $session = $this->getSessionMock(); + + $session + ->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + $session + ->expects($this->never()) + ->method('start'); + + $this->mink->registerSession('mock_session', $session); + $this->assertSame($session, $this->mink->getSession('mock_session')); + } + + public function testSetDefaultSessionName() + { + $this->assertNull($this->mink->getDefaultSessionName()); + + $session = $this->getSessionMock(); + $this->mink->registerSession('session_name', $session); + $this->mink->setDefaultSessionName('session_name'); + + $this->assertEquals('session_name', $this->mink->getDefaultSessionName()); + + $this->setExpectedException('InvalidArgumentException'); + + $this->mink->setDefaultSessionName('not_registered'); + } + + public function testGetDefaultSession() + { + $session1 = $this->getSessionMock(); + $session2 = $this->getSessionMock(); + + $this->assertNotSame($session1, $session2); + + $this->mink->registerSession('session_1', $session1); + $this->mink->registerSession('session_2', $session2); + $this->mink->setDefaultSessionName('session_2'); + + $this->assertSame($session1, $this->mink->getSession('session_1')); + $this->assertSame($session2, $this->mink->getSession('session_2')); + $this->assertSame($session2, $this->mink->getSession()); + + $this->mink->setDefaultSessionName('session_1'); + + $this->assertSame($session1, $this->mink->getSession()); + } + + public function testGetNoDefaultSession() + { + $session1 = $this->getSessionMock(); + + $this->mink->registerSession('session_1', $session1); + + $this->setExpectedException('InvalidArgumentException'); + + $this->mink->getSession(); + } + + public function testIsSessionStarted() + { + $session_1 = $this->getSessionMock(); + $session_2 = $this->getSessionMock(); + + $session_1 + ->expects($this->any()) + ->method('isStarted') + ->will($this->returnValue(false)); + $session_1 + ->expects($this->never()) + ->method('start'); + + $session_2 + ->expects($this->any()) + ->method('isStarted') + ->will($this->returnValue(true)); + $session_2 + ->expects($this->never()) + ->method('start'); + + $this->mink->registerSession('not_started', $session_1); + $this->assertFalse($this->mink->isSessionStarted('not_started')); + + $this->mink->registerSession('started', $session_2); + $this->assertTrue($this->mink->isSessionStarted('started')); + + $this->setExpectedException('InvalidArgumentException'); + + $this->mink->getSession('not_registered'); + } + + public function testAssertSession() + { + $session = $this->getSessionMock(); + + $this->mink->registerSession('my', $session); + + $this->assertInstanceOf('Behat\Mink\WebAssert', $this->mink->assertSession('my')); + } + + public function testAssertSessionNotRegistered() + { + $session = $this->getSessionMock(); + + $this->assertInstanceOf('Behat\Mink\WebAssert', $this->mink->assertSession($session)); + } + + public function testResetSessions() + { + $session1 = $this->getSessionMock(); + $session1->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(false)); + $session1->expects($this->never()) + ->method('reset'); + + $session2 = $this->getSessionMock(); + $session2->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + $session2->expects($this->once()) + ->method('reset'); + + $this->mink->registerSession('not started', $session1); + $this->mink->registerSession('started', $session2); + + $this->mink->resetSessions(); + } + + public function testRestartSessions() + { + $session1 = $this->getSessionMock(); + $session1->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(false)); + $session1->expects($this->never()) + ->method('restart'); + + $session2 = $this->getSessionMock(); + $session2->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + $session2->expects($this->once()) + ->method('restart'); + + $this->mink->registerSession('not started', $session1); + $this->mink->registerSession('started', $session2); + + $this->mink->restartSessions(); + } + + private function getSessionMock() + { + return $this->getMockBuilder('Behat\Mink\Session') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/core/vendor/behat/mink/tests/Selector/CssSelectorTest.php b/core/vendor/behat/mink/tests/Selector/CssSelectorTest.php new file mode 100644 index 0000000..ea80d93 --- /dev/null +++ b/core/vendor/behat/mink/tests/Selector/CssSelectorTest.php @@ -0,0 +1,41 @@ +markTestSkipped('Symfony2 CssSelector component not installed'); + } + } + + public function testSelector() + { + $selector = new CssSelector(); + + $this->assertEquals('descendant-or-self::h3', $selector->translateToXPath('h3')); + $this->assertEquals('descendant-or-self::h3/span', $selector->translateToXPath('h3 > span')); + + if (interface_exists('Symfony\Component\CssSelector\XPath\TranslatorInterface')) { + // The rewritten component of Symfony 2.3 checks for attribute existence first for the class. + $expectation = "descendant-or-self::h3/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' my_div ')]"; + } else { + $expectation = "descendant-or-self::h3/*[contains(concat(' ', normalize-space(@class), ' '), ' my_div ')]"; + } + $this->assertEquals($expectation, $selector->translateToXPath('h3 > .my_div')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsForArrayLocator() + { + $selector = new CssSelector(); + + $selector->translateToXPath(array('h3')); + } +} diff --git a/core/vendor/behat/mink/tests/Selector/ExactNamedSelectorTest.php b/core/vendor/behat/mink/tests/Selector/ExactNamedSelectorTest.php new file mode 100644 index 0000000..df51f0f --- /dev/null +++ b/core/vendor/behat/mink/tests/Selector/ExactNamedSelectorTest.php @@ -0,0 +1,18 @@ +getSelector(); + + $selector->registerNamedXpath('some', 'my_xpath'); + $this->assertEquals('my_xpath', $selector->translateToXPath('some')); + + $this->setExpectedException('InvalidArgumentException'); + + $selector->translateToXPath('custom'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidLocator() + { + $namedSelector = $this->getSelector(); + + $namedSelector->translateToXPath(array('foo', 'bar', 'baz')); + } + + /** + * @dataProvider getSelectorTests + */ + public function testSelectors($fixtureFile, $selector, $locator, $expectedExactCount, $expectedPartialCount = null) + { + $expectedCount = $this->allowPartialMatch() && null !== $expectedPartialCount + ? $expectedPartialCount + : $expectedExactCount; + + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->loadHTMLFile(__DIR__.'/fixtures/'.$fixtureFile); + + // Escape the locator as Mink 1.x expects the caller of the NamedSelector to handle it + $selectorsHandler = new SelectorsHandler(); + $locator = $selectorsHandler->xpathLiteral($locator); + + $namedSelector = $this->getSelector(); + + $xpath = $namedSelector->translateToXPath(array($selector, $locator)); + + $domXpath = new \DOMXPath($dom); + $nodeList = $domXpath->query($xpath); + + $this->assertEquals($expectedCount, $nodeList->length); + } + + public function getSelectorTests() + { + $fieldCount = 8; // fields without `type` attribute + $fieldCount += 4; // fields with `type=checkbox` attribute + $fieldCount += 4; // fields with `type=radio` attribute + $fieldCount += 4; // fields with `type=file` attribute + + // Fixture file, selector name, locator, expected number of exact matched elements, expected number of partial matched elements if different + return array( + 'fieldset' => array('test.html', 'fieldset', 'fieldset-text', 2, 3), + + 'field (name/placeholder/label)' => array('test.html', 'field', 'the-field', $fieldCount), + 'field (input, with-id)' => array('test.html', 'field', 'the-field-input', 1), + 'field (textarea, with-id)' => array('test.html', 'field', 'the-field-textarea', 1), + 'field (select, with-id)' => array('test.html', 'field', 'the-field-select', 1), + 'field (input type=submit, with-id) ignored' => array('test.html', 'field', 'the-field-submit-button', 0), + 'field (input type=image, with-id) ignored' => array('test.html', 'field', 'the-field-image-button', 0), + 'field (input type=button, with-id) ignored' => array('test.html', 'field', 'the-field-button-button', 0), + 'field (input type=reset, with-id) ignored' => array('test.html', 'field', 'the-field-reset-button', 0), + 'field (input type=hidden, with-id) ignored' => array('test.html', 'field', 'the-field-hidden', 0), + + 'link (with-href)' => array('test.html', 'link', 'link-text', 5, 9), + 'link (without-href) ignored' => array('test.html', 'link', 'bad-link-text', 0), + 'link* (role=link)' => array('test.html', 'link', 'link-role-text', 4, 7), + + 'button (input, name/value/title)' => array('test.html', 'button', 'button-text', 25, 42), + 'button (type=image, with-alt)' => array('test.html', 'button', 'button-alt-text', 1, 2), + 'button (input type=submit, with-id)' => array('test.html', 'button', 'input-submit-button', 1), + 'button (input type=image, with-id)' => array('test.html', 'button', 'input-image-button', 1), + 'button (input type=button, with-id)' => array('test.html', 'button', 'input-button-button', 1), + 'button (input type=reset, with-id)' => array('test.html', 'button', 'input-reset-button', 1), + 'button (button type=submit, with-id)' => array('test.html', 'button', 'button-submit-button', 1), + 'button (button type=image, with-id)' => array('test.html', 'button', 'button-image-button', 1), + 'button (button type=button, with-id)' => array('test.html', 'button', 'button-button-button', 1), + 'button (button type=reset, with-id)' => array('test.html', 'button', 'button-reset-button', 1), + 'button* (role=button, name/value/title)' => array('test.html', 'button', 'button-role-text', 12, 20), + 'button* (role=button type=submit, with-id)' => array('test.html', 'button', 'role-button-submit-button', 1), + 'button* (role=button type=image, with-id)' => array('test.html', 'button', 'role-button-image-button', 1), + 'button* (role=button type=button, with-id)' => array('test.html', 'button', 'role-button-button-button', 1), + 'button* (role=button type=reset, with-id)' => array('test.html', 'button', 'role-button-reset-button', 1), + + 'link_or_button (with-href)' => array('test.html', 'link_or_button', 'link-text', 5, 9), + 'link_or_button (without-href) ignored' => array('test.html', 'link_or_button', 'bad-link-text', 0), + 'link_or_button* (role=link)' => array('test.html', 'link_or_button', 'link-role-text', 4, 7), + + // bug in selector: 17 instead of 25 and 34 instead of 42, because 8 buttons with `name` attribute were not matched + 'link_or_button (input, name/value/title)' => array('test.html', 'link_or_button', 'button-text', 17, 34), + 'link_or_button (type=image, with-alt)' => array('test.html', 'link_or_button', 'button-alt-text', 1, 2), + 'link_or_button (input type=submit, with-id)' => array('test.html', 'link_or_button', 'input-submit-button', 1), + 'link_or_button (input type=image, with-id)' => array('test.html', 'link_or_button', 'input-image-button', 1), + 'link_or_button (input type=button, with-id)' => array('test.html', 'link_or_button', 'input-button-button', 1), + 'link_or_button (input type=reset, with-id)' => array('test.html', 'link_or_button', 'input-reset-button', 1), + 'link_or_button (button type=submit, with-id)' => array('test.html', 'link_or_button', 'button-submit-button', 1), + 'link_or_button (button type=image, with-id)' => array('test.html', 'link_or_button', 'button-image-button', 1), + 'link_or_button (button type=button, with-id)' => array('test.html', 'link_or_button', 'button-button-button', 1), + 'link_or_button (button type=reset, with-id)' => array('test.html', 'link_or_button', 'button-reset-button', 1), + + // bug in selector: 8 instead of 12 and 16 instead of 20, because 4 buttons with `name` attribute were not matched + 'link_or_button* (role=button, name/value/title)' => array('test.html', 'link_or_button', 'button-role-text', 8, 16), + 'link_or_button* (role=button type=submit, with-id)' => array('test.html', 'link_or_button', 'role-button-submit-button', 1), + 'link_or_button* (role=button type=image, with-id)' => array('test.html', 'link_or_button', 'role-button-image-button', 1), + 'link_or_button* (role=button type=button, with-id)' => array('test.html', 'link_or_button', 'role-button-button-button', 1), + 'link_or_button* (role=button type=reset, with-id)' => array('test.html', 'link_or_button', 'role-button-reset-button', 1), + + // 3 matches, because matches every HTML node in path: html > body > div + 'content' => array('test.html', 'content', 'content-text', 1, 4), + + 'select (name/label)' => array('test.html', 'select', 'the-field', 3), + 'select (with-id)' => array('test.html', 'select', 'the-field-select', 1), + + 'checkbox (name/label)' => array('test.html', 'checkbox', 'the-field', 3), + 'checkbox (with-id)' => array('test.html', 'checkbox', 'the-field-checkbox', 1), + + 'radio (name/label)' => array('test.html', 'radio', 'the-field', 3), + 'radio (with-id)' => array('test.html', 'radio', 'the-field-radio', 1), + + 'file (name/label)' => array('test.html', 'file', 'the-field', 3), + 'file (with-id)' => array('test.html', 'file', 'the-field-file', 1), + + 'optgroup' => array('test.html', 'optgroup', 'group-label', 1, 2), + + 'option' => array('test.html', 'option', 'option-value', 2, 3), + + 'table' => array('test.html', 'table', 'the-table', 2, 3), + + 'id' => array('test.html', 'id', 'bad-link-text', 1), + 'id or name' => array('test.html', 'id_or_name', 'the-table', 2), + ); + } + + /** + * @return NamedSelector + */ + abstract protected function getSelector(); + + /** + * @return boolean + */ + abstract protected function allowPartialMatch(); +} diff --git a/core/vendor/behat/mink/tests/Selector/PartialNamedSelectorTest.php b/core/vendor/behat/mink/tests/Selector/PartialNamedSelectorTest.php new file mode 100644 index 0000000..c3e631a --- /dev/null +++ b/core/vendor/behat/mink/tests/Selector/PartialNamedSelectorTest.php @@ -0,0 +1,18 @@ +getMockBuilder('Behat\Mink\Selector\SelectorInterface')->getMock(); + $handler = new SelectorsHandler(); + + $this->assertFalse($handler->isSelectorRegistered('custom')); + + $handler->registerSelector('custom', $selector); + + $this->assertTrue($handler->isSelectorRegistered('custom')); + $this->assertSame($selector, $handler->getSelector('custom')); + } + + public function testRegisterSelectorThroughConstructor() + { + $selector = $this->getMockBuilder('Behat\Mink\Selector\SelectorInterface')->getMock(); + $handler = new SelectorsHandler(array('custom' => $selector)); + + $this->assertTrue($handler->isSelectorRegistered('custom')); + $this->assertSame($selector, $handler->getSelector('custom')); + } + + public function testRegisterDefaultSelectors() + { + $handler = new SelectorsHandler(); + + $this->assertTrue($handler->isSelectorRegistered('css')); + $this->assertTrue($handler->isSelectorRegistered('named_exact')); + $this->assertTrue($handler->isSelectorRegistered('named_partial')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testXpathSelectorThrowsExceptionForArrayLocator() + { + $handler = new SelectorsHandler(); + $handler->selectorToXpath('xpath', array('some_xpath')); + } + + public function testXpathSelectorIsReturnedAsIs() + { + $handler = new SelectorsHandler(); + $this->assertEquals('some_xpath', $handler->selectorToXpath('xpath', 'some_xpath')); + } + + public function testSelectorToXpath() + { + $selector = $this->getMockBuilder('Behat\Mink\Selector\SelectorInterface')->getMock(); + $handler = new SelectorsHandler(); + + $handler->registerSelector('custom_selector', $selector); + + $selector + ->expects($this->once()) + ->method('translateToXPath') + ->with($locator = 'some[locator]') + ->will($this->returnValue($ret = '[]some[]locator')); + + $this->assertEquals($ret, $handler->selectorToXpath('custom_selector', $locator)); + + $this->setExpectedException('InvalidArgumentException'); + $handler->selectorToXpath('undefined', 'asd'); + } + + public function testXpathLiteral() + { + $handler = new SelectorsHandler(); + + $this->assertEquals("'some simple string'", $handler->xpathLiteral('some simple string')); + } +} diff --git a/core/vendor/behat/mink/tests/Selector/Xpath/EscaperTest.php b/core/vendor/behat/mink/tests/Selector/Xpath/EscaperTest.php new file mode 100644 index 0000000..535dac6 --- /dev/null +++ b/core/vendor/behat/mink/tests/Selector/Xpath/EscaperTest.php @@ -0,0 +1,31 @@ +assertEquals($expected, $escaper->escapeLiteral($string)); + } + + public function getXpathLiterals() + { + return array( + array('some simple string', "'some simple string'"), + array('some "d-brackets" string', "'some \"d-brackets\" string'"), + array('some \'s-brackets\' string', "\"some 's-brackets' string\""), + array( + 'some \'s-brackets\' and "d-brackets" string', + 'concat(\'some \',"\'",\'s-brackets\',"\'",\' and "d-brackets" string\')', + ), + ); + } +} diff --git a/core/vendor/behat/mink/tests/Selector/Xpath/ManipulatorTest.php b/core/vendor/behat/mink/tests/Selector/Xpath/ManipulatorTest.php new file mode 100644 index 0000000..7c20561 --- /dev/null +++ b/core/vendor/behat/mink/tests/Selector/Xpath/ManipulatorTest.php @@ -0,0 +1,64 @@ +assertEquals($expectedXpath, $manipulator->prepend($xpath, $prefix)); + } + + public function getPrependedXpath() + { + return array( + 'simple' => array( + 'some_xpath', + 'some_tag1', + 'some_xpath/some_tag1', + ), + 'with slash' => array( + 'some_xpath', + '/some_tag1', + 'some_xpath/some_tag1', + ), + 'union' => array( + 'some_xpath', + 'some_tag1 | some_tag2', + 'some_xpath/some_tag1 | some_xpath/some_tag2', + ), + 'wrapped union' => array( + 'some_xpath', + '(some_tag1 | some_tag2)/some_child', + '(some_xpath/some_tag1 | some_xpath/some_tag2)/some_child', + ), + 'multiple wrapped union' => array( + 'some_xpath', + '( ( some_tag1 | some_tag2)/some_child | some_tag3)/leaf', + '( ( some_xpath/some_tag1 | some_xpath/some_tag2)/some_child | some_xpath/some_tag3)/leaf', + ), + 'parent union' => array( + 'some_xpath | another_xpath', + 'some_tag1 | some_tag2', + '(some_xpath | another_xpath)/some_tag1 | (some_xpath | another_xpath)/some_tag2', + ), + 'complex condition' => array( + 'some_xpath', + 'some_tag1 | some_tag2[@foo = "bar|"] | some_tag3[foo | bar]', + 'some_xpath/some_tag1 | some_xpath/some_tag2[@foo = "bar|"] | some_xpath/some_tag3[foo | bar]', + ), + 'multiline' => array( + 'some_xpath', + "some_tag1 | some_tag2[@foo =\n 'bar|'']\n | some_tag3[foo | bar]", + "some_xpath/some_tag1 | some_xpath/some_tag2[@foo =\n 'bar|''] | some_xpath/some_tag3[foo | bar]", + ), + ); + } +} diff --git a/core/vendor/behat/mink/tests/Selector/fixtures/test.html b/core/vendor/behat/mink/tests/Selector/fixtures/test.html new file mode 100644 index 0000000..c64ae10 --- /dev/null +++ b/core/vendor/behat/mink/tests/Selector/fixtures/test.html @@ -0,0 +1,310 @@ + + + + + + + + +
+ +
+ +
+ fieldset-text +
+ +
+ fieldset-text sample +
+ + +
fieldset-text
+
+
+ +
+ content-text +
+ +
+ some content-text +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
+ +
+ + + + + +
+ +
+ + + + + +
+ + +
+ +
+ +
+ + +
the-table
+ + + +
some the-table
+ + + + + + + +
the-tablethe-table
+
+ + + + diff --git a/core/vendor/behat/mink/tests/SessionTest.php b/core/vendor/behat/mink/tests/SessionTest.php new file mode 100644 index 0000000..b000ba6 --- /dev/null +++ b/core/vendor/behat/mink/tests/SessionTest.php @@ -0,0 +1,282 @@ +driver = $this->getMockBuilder('Behat\Mink\Driver\DriverInterface')->getMock(); + $this->selectorsHandler = $this->getMockBuilder('Behat\Mink\Selector\SelectorsHandler')->getMock(); + $this->session = new Session($this->driver, $this->selectorsHandler); + } + + public function testGetDriver() + { + $this->assertSame($this->driver, $this->session->getDriver()); + } + + public function testGetPage() + { + $this->assertInstanceOf('Behat\Mink\Element\DocumentElement', $this->session->getPage()); + } + + public function testGetSelectorsHandler() + { + $this->assertSame($this->selectorsHandler, $this->session->getSelectorsHandler()); + } + + public function testIsStarted() + { + $this->driver->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + + $this->assertTrue($this->session->isStarted()); + } + + public function testStart() + { + $this->driver->expects($this->once()) + ->method('start'); + + $this->session->start(); + } + + public function testStop() + { + $this->driver->expects($this->once()) + ->method('stop'); + + $this->session->stop(); + } + + public function testRestart() + { + $this->driver->expects($this->at(0)) + ->method('stop'); + $this->driver->expects($this->at(1)) + ->method('start'); + + $this->session->restart(); + } + + public function testVisit() + { + $this->driver + ->expects($this->once()) + ->method('visit') + ->with($url = 'some_url'); + + $this->session->visit($url); + } + + public function testReset() + { + $this->driver + ->expects($this->once()) + ->method('reset'); + + $this->session->reset(); + } + + public function testSetBasicAuth() + { + $this->driver->expects($this->once()) + ->method('setBasicAuth') + ->with('user', 'pass'); + + $this->session->setBasicAuth('user', 'pass'); + } + + public function testSetRequestHeader() + { + $this->driver->expects($this->once()) + ->method('setRequestHeader') + ->with('name', 'value'); + + $this->session->setRequestHeader('name', 'value'); + } + + public function testGetResponseHeaders() + { + $this->driver + ->expects($this->once()) + ->method('getResponseHeaders') + ->will($this->returnValue($ret = array(2, 3, 4))); + + $this->assertEquals($ret, $this->session->getResponseHeaders()); + } + + public function testSetCookie() + { + $this->driver->expects($this->once()) + ->method('setCookie') + ->with('name', 'value'); + + $this->session->setCookie('name', 'value'); + } + + public function testGetCookie() + { + $this->driver->expects($this->once()) + ->method('getCookie') + ->with('name') + ->will($this->returnValue('value')); + + $this->assertEquals('value', $this->session->getCookie('name')); + } + + public function testGetStatusCode() + { + $this->driver + ->expects($this->once()) + ->method('getStatusCode') + ->will($this->returnValue($ret = 404)); + + $this->assertEquals($ret, $this->session->getStatusCode()); + } + + public function testGetCurrentUrl() + { + $this->driver + ->expects($this->once()) + ->method('getCurrentUrl') + ->will($this->returnValue($ret = 'http://some.url')); + + $this->assertEquals($ret, $this->session->getCurrentUrl()); + } + + public function testGetScreenshot() + { + $this->driver->expects($this->once()) + ->method('getScreenshot') + ->will($this->returnValue('screenshot')); + + $this->assertEquals('screenshot', $this->session->getScreenshot()); + } + + public function testGetWindowNames() + { + $this->driver->expects($this->once()) + ->method('getWindowNames') + ->will($this->returnValue($names = array('window 1', 'window 2'))); + + $this->assertEquals($names, $this->session->getWindowNames()); + } + + public function testGetWindowName() + { + $this->driver->expects($this->once()) + ->method('getWindowName') + ->will($this->returnValue('name')); + + $this->assertEquals('name', $this->session->getWindowName()); + } + + public function testReload() + { + $this->driver->expects($this->once()) + ->method('reload'); + + $this->session->reload(); + } + + public function testBack() + { + $this->driver->expects($this->once()) + ->method('back'); + + $this->session->back(); + } + + public function testForward() + { + $this->driver->expects($this->once()) + ->method('forward'); + + $this->session->forward(); + } + + public function testSwitchToWindow() + { + $this->driver->expects($this->once()) + ->method('switchToWindow') + ->with('test'); + + $this->session->switchToWindow('test'); + } + + public function testSwitchToIFrame() + { + $this->driver->expects($this->once()) + ->method('switchToIFrame') + ->with('test'); + + $this->session->switchToIFrame('test'); + } + + public function testExecuteScript() + { + $this->driver + ->expects($this->once()) + ->method('executeScript') + ->with($arg = 'JS'); + + $this->session->executeScript($arg); + } + + public function testEvaluateScript() + { + $this->driver + ->expects($this->once()) + ->method('evaluateScript') + ->with($arg = 'JS func') + ->will($this->returnValue($ret = '23')); + + $this->assertEquals($ret, $this->session->evaluateScript($arg)); + } + + public function testWait() + { + $this->driver + ->expects($this->once()) + ->method('wait') + ->with(1000, 'function () {}'); + + $this->session->wait(1000, 'function () {}'); + } + + public function testResizeWindow() + { + $this->driver->expects($this->once()) + ->method('resizeWindow') + ->with(800, 600, 'test'); + + $this->session->resizeWindow(800, 600, 'test'); + } + + public function testMaximizeWindow() + { + $this->driver->expects($this->once()) + ->method('maximizeWindow') + ->with('test'); + + $this->session->maximizeWindow('test'); + } +} diff --git a/core/vendor/behat/mink/tests/WebAssertTest.php b/core/vendor/behat/mink/tests/WebAssertTest.php new file mode 100644 index 0000000..9ce5d22 --- /dev/null +++ b/core/vendor/behat/mink/tests/WebAssertTest.php @@ -0,0 +1,1123 @@ +session = $this->getMockBuilder('Behat\\Mink\\Session') + ->disableOriginalConstructor() + ->getMock(); + $this->assert = new WebAssert($this->session); + } + + public function testAddressEquals() + { + $this->session + ->expects($this->exactly(2)) + ->method('getCurrentUrl') + ->will($this->returnValue('http://example.com/script.php/sub/url?param=true#webapp/nav')) + ; + + $this->assertCorrectAssertion('addressEquals', array('/sub/url#webapp/nav')); + $this->assertWrongAssertion( + 'addressEquals', + array('sub_url'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'Current page is "/sub/url#webapp/nav", but "sub_url" expected.' + ); + } + + public function testAddressNotEquals() + { + $this->session + ->expects($this->exactly(2)) + ->method('getCurrentUrl') + ->will($this->returnValue('http://example.com/script.php/sub/url')) + ; + + $this->assertCorrectAssertion('addressNotEquals', array('sub_url')); + $this->assertWrongAssertion( + 'addressNotEquals', + array('/sub/url'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'Current page is "/sub/url", but should not be.' + ); + } + + public function testAddressMatches() + { + $this->session + ->expects($this->exactly(2)) + ->method('getCurrentUrl') + ->will($this->returnValue('http://example.com/script.php/sub/url')) + ; + + $this->assertCorrectAssertion('addressMatches', array('/su.*rl/')); + $this->assertWrongAssertion( + 'addressMatches', + array('/suburl/'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'Current page "/sub/url" does not match the regex "/suburl/".' + ); + } + + /** + * @covers Behat\Mink\WebAssert::cookieEquals + */ + public function testCookieEquals() + { + $this->session-> + expects($this->any())-> + method('getCookie')-> + will($this->returnValueMap( + array( + array('foo', 'bar'), + array('bar', 'baz'), + ) + )); + + $this->assertCorrectAssertion('cookieEquals', array('foo', 'bar')); + $this->assertWrongAssertion( + 'cookieEquals', + array('bar', 'foo'), + 'Behat\Mink\Exception\ExpectationException', + 'Cookie "bar" value is "baz", but should be "foo".' + ); + } + + /** + * @covers Behat\Mink\WebAssert::cookieExists + */ + public function testCookieExists() + { + $this->session-> + expects($this->any())-> + method('getCookie')-> + will($this->returnValueMap( + array( + array('foo', '1'), + array('bar', null), + ) + )); + + $this->assertCorrectAssertion('cookieExists', array('foo')); + $this->assertWrongAssertion( + 'cookieExists', + array('bar'), + 'Behat\Mink\Exception\ExpectationException', + 'Cookie "bar" is not set, but should be.' + ); + } + + public function testStatusCodeEquals() + { + $this->session + ->expects($this->exactly(2)) + ->method('getStatusCode') + ->will($this->returnValue(200)) + ; + + $this->assertCorrectAssertion('statusCodeEquals', array(200)); + $this->assertWrongAssertion( + 'statusCodeEquals', + array(404), + 'Behat\\Mink\\Exception\\ExpectationException', + 'Current response status code is 200, but 404 expected.' + ); + } + + public function testStatusCodeNotEquals() + { + $this->session + ->expects($this->exactly(2)) + ->method('getStatusCode') + ->will($this->returnValue(404)) + ; + + $this->assertCorrectAssertion('statusCodeNotEquals', array(200)); + $this->assertWrongAssertion( + 'statusCodeNotEquals', + array(404), + 'Behat\\Mink\\Exception\\ExpectationException', + 'Current response status code is 404, but should not be.' + ); + } + + public function testPageTextContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('getText') + ->will($this->returnValue("Some page\n\ttext")) + ; + + $this->assertCorrectAssertion('pageTextContains', array('PAGE text')); + $this->assertWrongAssertion( + 'pageTextContains', + array('html text'), + 'Behat\\Mink\\Exception\\ResponseTextException', + 'The text "html text" was not found anywhere in the text of the current page.' + ); + } + + public function testPageTextNotContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('getText') + ->will($this->returnValue("Some html\n\ttext")) + ; + + $this->assertCorrectAssertion('pageTextNotContains', array('PAGE text')); + $this->assertWrongAssertion( + 'pageTextNotContains', + array('HTML text'), + 'Behat\\Mink\\Exception\\ResponseTextException', + 'The text "HTML text" appears in the text of this page, but it should not.' + ); + } + + public function testPageTextMatches() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('getText') + ->will($this->returnValue('Some page text')) + ; + + $this->assertCorrectAssertion('pageTextMatches', array('/PA.E/i')); + $this->assertWrongAssertion( + 'pageTextMatches', + array('/html/'), + 'Behat\\Mink\\Exception\\ResponseTextException', + 'The pattern /html/ was not found anywhere in the text of the current page.' + ); + } + + public function testPageTextNotMatches() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('getText') + ->will($this->returnValue('Some html text')) + ; + + $this->assertCorrectAssertion('pageTextNotMatches', array('/PA.E/i')); + $this->assertWrongAssertion( + 'pageTextNotMatches', + array('/HTML/i'), + 'Behat\\Mink\\Exception\\ResponseTextException', + 'The pattern /HTML/i was found in the text of the current page, but it should not.' + ); + } + + public function testResponseContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('getContent') + ->will($this->returnValue('Some page text')) + ; + + $this->assertCorrectAssertion('responseContains', array('PAGE text')); + $this->assertWrongAssertion( + 'responseContains', + array('html text'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The string "html text" was not found anywhere in the HTML response of the current page.' + ); + } + + public function testResponseNotContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('getContent') + ->will($this->returnValue('Some html text')) + ; + + $this->assertCorrectAssertion('responseNotContains', array('PAGE text')); + $this->assertWrongAssertion( + 'responseNotContains', + array('HTML text'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The string "HTML text" appears in the HTML response of this page, but it should not.' + ); + } + + public function testResponseMatches() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('getContent') + ->will($this->returnValue('Some page text')) + ; + + $this->assertCorrectAssertion('responseMatches', array('/PA.E/i')); + $this->assertWrongAssertion( + 'responseMatches', + array('/html/'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The pattern /html/ was not found anywhere in the HTML response of the page.' + ); + } + + public function testResponseNotMatches() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('getContent') + ->will($this->returnValue('Some html text')) + ; + + $this->assertCorrectAssertion('responseNotMatches', array('/PA.E/i')); + $this->assertWrongAssertion( + 'responseNotMatches', + array('/HTML/i'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The pattern /HTML/i was found in the HTML response of the page, but it should not.' + ); + } + + public function testElementsCount() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('findAll') + ->with('css', 'h2 > span') + ->will($this->returnValue(array(1, 2))) + ; + + $this->assertCorrectAssertion('elementsCount', array('css', 'h2 > span', 2)); + $this->assertWrongAssertion( + 'elementsCount', + array('css', 'h2 > span', 3), + 'Behat\\Mink\\Exception\\ExpectationException', + '2 elements matching css "h2 > span" found on the page, but should be 3.' + ); + } + + public function testElementExists() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(4)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->onConsecutiveCalls(1, null, 1, null)) + ; + + $this->assertCorrectAssertion('elementExists', array('css', 'h2 > span')); + $this->assertWrongAssertion( + 'elementExists', + array('css', 'h2 > span'), + 'Behat\\Mink\\Exception\\ElementNotFoundException', + 'Element matching css "h2 > span" not found.' + ); + + $this->assertCorrectAssertion('elementExists', array('css', 'h2 > span', $page)); + $this->assertWrongAssertion( + 'elementExists', + array('css', 'h2 > span', $page), + 'Behat\\Mink\\Exception\\ElementNotFoundException', + 'Element matching css "h2 > span" not found.' + ); + } + + public function testElementExistsWithArrayLocator() + { + $container = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session->expects($this->never()) + ->method('getPage') + ; + + $container + ->expects($this->exactly(2)) + ->method('find') + ->with('named', array('element', 'Test')) + ->will($this->onConsecutiveCalls(1, null)) + ; + + $this->assertCorrectAssertion('elementExists', array('named', array('element', 'Test'), $container)); + $this->assertWrongAssertion( + 'elementExists', + array('named', array('element', 'Test'), $container), + 'Behat\\Mink\\Exception\\ElementNotFoundException', + 'Element with named "element Test" not found.' + ); + } + + public function testElementNotExists() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(4)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->onConsecutiveCalls(null, 1, null, 1)) + ; + + $this->assertCorrectAssertion('elementNotExists', array('css', 'h2 > span')); + $this->assertWrongAssertion( + 'elementNotExists', + array('css', 'h2 > span'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'An element matching css "h2 > span" appears on this page, but it should not.' + ); + + $this->assertCorrectAssertion('elementNotExists', array('css', 'h2 > span', $page)); + $this->assertWrongAssertion( + 'elementNotExists', + array('css', 'h2 > span', $page), + 'Behat\\Mink\\Exception\\ExpectationException', + 'An element matching css "h2 > span" appears on this page, but it should not.' + ); + } + + /** + * @dataProvider getArrayLocatorFormats + */ + public function testElementNotExistsArrayLocator($selector, $locator, $expectedMessage) + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->once()) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->once()) + ->method('find') + ->with($selector, $locator) + ->will($this->returnValue(1)) + ; + + $this->assertWrongAssertion( + 'elementNotExists', + array($selector, $locator), + 'Behat\\Mink\\Exception\\ExpectationException', + $expectedMessage + ); + } + + public function getArrayLocatorFormats() + { + return array( + 'named' => array( + 'named', + array('button', 'Test'), + 'An button matching locator "Test" appears on this page, but it should not.', + ), + 'custom' => array( + 'custom', + array('test', 'foo'), + 'An element matching custom "test foo" appears on this page, but it should not.', + ), + ); + } + + public function testElementTextContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(2)) + ->method('getText') + ->will($this->returnValue('element text')) + ; + + $this->assertCorrectAssertion('elementTextContains', array('css', 'h2 > span', 'text')); + $this->assertWrongAssertion( + 'elementTextContains', + array('css', 'h2 > span', 'html'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The text "html" was not found in the text of the element matching css "h2 > span".' + ); + } + + public function testElementTextNotContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(2)) + ->method('getText') + ->will($this->returnValue('element text')) + ; + + $this->assertCorrectAssertion('elementTextNotContains', array('css', 'h2 > span', 'html')); + $this->assertWrongAssertion( + 'elementTextNotContains', + array('css', 'h2 > span', 'text'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The text "text" appears in the text of the element matching css "h2 > span", but it should not.' + ); + } + + public function testElementContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(2)) + ->method('getHtml') + ->will($this->returnValue('element html')) + ; + + $this->assertCorrectAssertion('elementContains', array('css', 'h2 > span', 'html')); + $this->assertWrongAssertion( + 'elementContains', + array('css', 'h2 > span', 'text'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The string "text" was not found in the HTML of the element matching css "h2 > span".' + ); + } + + public function testElementNotContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(2)) + ->method('getHtml') + ->will($this->returnValue('element html')) + ; + + $this->assertCorrectAssertion('elementNotContains', array('css', 'h2 > span', 'text')); + $this->assertWrongAssertion( + 'elementNotContains', + array('css', 'h2 > span', 'html'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The string "html" appears in the HTML of the element matching css "h2 > span", but it should not.' + ); + } + + public function testElementAttributeContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(2)) + ->method('hasAttribute') + ->will($this->returnValue(true)) + ; + + $element + ->expects($this->exactly(2)) + ->method('getAttribute') + ->with('name') + ->will($this->returnValue('foo')) + ; + + $this->assertCorrectAssertion('elementAttributeContains', array('css', 'h2 > span', 'name', 'foo')); + $this->assertWrongAssertion( + 'elementAttributeContains', + array('css', 'h2 > span', 'name', 'bar'), + 'Behat\\Mink\\Exception\\ElementHtmlException', + 'The text "bar" was not found in the attribute "name" of the element matching css "h2 > span".' + ); + } + + public function testElementAttributeExists() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->at(0)) + ->method('hasAttribute') + ->with('name') + ->will($this->returnValue(true)) + ; + + $element + ->expects($this->at(1)) + ->method('hasAttribute') + ->with('name') + ->will($this->returnValue(false)) + ; + + $this->assertCorrectAssertion('elementAttributeExists', array('css', 'h2 > span', 'name')); + $this->assertWrongAssertion( + 'elementAttributeExists', + array('css', 'h2 > span', 'name'), + 'Behat\\Mink\\Exception\\ElementHtmlException', + 'The attribute "name" was not found in the element matching css "h2 > span".' + ); + } + + public function testElementAttributeNotContains() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('find') + ->with('css', 'h2 > span') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(2)) + ->method('hasAttribute') + ->will($this->returnValue(true)) + ; + + $element + ->expects($this->exactly(2)) + ->method('getAttribute') + ->with('name') + ->will($this->returnValue('foo')) + ; + + $this->assertCorrectAssertion('elementAttributeNotContains', array('css', 'h2 > span', 'name', 'bar')); + $this->assertWrongAssertion( + 'elementAttributeNotContains', + array('css', 'h2 > span', 'name', 'foo'), + 'Behat\\Mink\\Exception\\ElementHtmlException', + 'The text "foo" was found in the attribute "name" of the element matching css "h2 > span".' + ); + } + + public function testFieldExists() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('findField') + ->with('username') + ->will($this->onConsecutiveCalls($element, null)) + ; + + $this->assertCorrectAssertion('fieldExists', array('username')); + $this->assertWrongAssertion( + 'fieldExists', + array('username'), + 'Behat\\Mink\\Exception\\ElementNotFoundException', + 'Form field with id|name|label|value "username" not found.' + ); + } + + public function testFieldNotExists() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('findField') + ->with('username') + ->will($this->onConsecutiveCalls(null, $element)) + ; + + $this->assertCorrectAssertion('fieldNotExists', array('username')); + $this->assertWrongAssertion( + 'fieldNotExists', + array('username'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'A field "username" appears on this page, but it should not.' + ); + } + + public function testFieldValueEquals() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(4)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(4)) + ->method('findField') + ->with('username') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(4)) + ->method('getValue') + ->will($this->returnValue(234)) + ; + + $this->assertCorrectAssertion('fieldValueEquals', array('username', 234)); + $this->assertWrongAssertion( + 'fieldValueEquals', + array('username', 235), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The field "username" value is "234", but "235" expected.' + ); + $this->assertWrongAssertion( + 'fieldValueEquals', + array('username', 23), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The field "username" value is "234", but "23" expected.' + ); + $this->assertWrongAssertion( + 'fieldValueEquals', + array('username', ''), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The field "username" value is "234", but "" expected.' + ); + } + + public function testFieldValueNotEquals() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(4)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(4)) + ->method('findField') + ->with('username') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(4)) + ->method('getValue') + ->will($this->returnValue(235)) + ; + + $this->assertCorrectAssertion('fieldValueNotEquals', array('username', 234)); + $this->assertWrongAssertion( + 'fieldValueNotEquals', + array('username', 235), + 'Behat\\Mink\\Exception\\ExpectationException', + 'The field "username" value is "235", but it should not be.' + ); + $this->assertCorrectAssertion('fieldValueNotEquals', array('username', 23)); + $this->assertCorrectAssertion('fieldValueNotEquals', array('username', '')); + } + + public function testCheckboxChecked() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('findField') + ->with('remember_me') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(2)) + ->method('isChecked') + ->will($this->onConsecutiveCalls(true, false)) + ; + + $this->assertCorrectAssertion('checkboxChecked', array('remember_me')); + $this->assertWrongAssertion( + 'checkboxChecked', + array('remember_me'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'Checkbox "remember_me" is not checked, but it should be.' + ); + } + + public function testCheckboxNotChecked() + { + $page = $this->getMockBuilder('Behat\\Mink\\Element\\DocumentElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $element = $this->getMockBuilder('Behat\\Mink\\Element\\NodeElement') + ->disableOriginalConstructor() + ->getMock() + ; + + $this->session + ->expects($this->exactly(2)) + ->method('getPage') + ->will($this->returnValue($page)) + ; + + $page + ->expects($this->exactly(2)) + ->method('findField') + ->with('remember_me') + ->will($this->returnValue($element)) + ; + + $element + ->expects($this->exactly(2)) + ->method('isChecked') + ->will($this->onConsecutiveCalls(false, true)) + ; + + $this->assertCorrectAssertion('checkboxNotChecked', array('remember_me')); + $this->assertWrongAssertion( + 'checkboxNotChecked', + array('remember_me'), + 'Behat\\Mink\\Exception\\ExpectationException', + 'Checkbox "remember_me" is checked, but it should not be.' + ); + } + + protected function assertCorrectAssertion($assertion, $arguments) + { + try { + call_user_func_array(array($this->assert, $assertion), $arguments); + } catch (\Exception $e) { + $this->fail('Correct assertion should not throw an exception: '.$e->getMessage()); + } + } + + protected function assertWrongAssertion($assertion, $arguments, $exceptionClass, $exceptionMessage) + { + try { + call_user_func_array(array($this->assert, $assertion), $arguments); + $this->fail('Wrong assertion should throw an exception'); + } catch (\Exception $e) { + $this->assertInstanceOf($exceptionClass, $e); + $this->assertSame($exceptionMessage, $e->getMessage()); + } + } +} diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php index 18472d7..a52ec9f 100644 --- a/core/vendor/composer/autoload_namespaces.php +++ b/core/vendor/composer/autoload_namespaces.php @@ -19,10 +19,12 @@ 'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'), 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Symfony\\Component\\DomCrawler\\' => array($vendorDir . '/symfony/dom-crawler'), 'Symfony\\Component\\DependencyInjection\\' => array($vendorDir . '/symfony/dependency-injection'), 'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'), 'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'), 'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'), + 'Symfony\\Component\\BrowserKit\\' => array($vendorDir . '/symfony/browser-kit'), 'Stack' => array($vendorDir . '/stack/builder/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log'), 'Gliph' => array($vendorDir . '/sdboyer/gliph/src'), @@ -35,4 +37,6 @@ 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib'), 'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib'), 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib'), + 'Behat\\Mink\\Driver' => array($vendorDir . '/behat/mink-browserkit-driver/src', $vendorDir . '/behat/mink-goutte-driver/src'), + 'Behat\\Mink' => array($vendorDir . '/behat/mink/src'), ); diff --git a/core/vendor/composer/autoload_psr4.php b/core/vendor/composer/autoload_psr4.php index 1b053f1..75db70e 100644 --- a/core/vendor/composer/autoload_psr4.php +++ b/core/vendor/composer/autoload_psr4.php @@ -11,6 +11,7 @@ 'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'), 'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), + 'Goutte\\' => array($vendorDir . '/fabpot/goutte/Goutte'), 'Drupal\\Driver\\' => array($baseDir . '/drivers/lib/Drupal/Driver'), 'Drupal\\Core\\' => array($baseDir . '/core/lib/Drupal/Core'), 'Drupal\\Component\\' => array($baseDir . '/core/lib/Drupal/Component'), diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index 03eef68..b40d267 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -1202,82 +1202,6 @@ ] }, { - "name": "phpunit/phpunit", - "version": "4.1.4", - "version_normalized": "4.1.4.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a71c4842c5fb836d8b200624583b859ec34e8a26" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a71c4842c5fb836d8b200624583b859ec34e8a26", - "reference": "a71c4842c5fb836d8b200624583b859ec34e8a26", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpunit/php-code-coverage": "~2.0", - "phpunit/php-file-iterator": "~1.3.1", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "~1.0.2", - "phpunit/phpunit-mock-objects": "~2.1", - "sebastian/comparator": "~1.0", - "sebastian/diff": "~1.1", - "sebastian/environment": "~1.0", - "sebastian/exporter": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.0" - }, - "suggest": { - "phpunit/php-invoker": "~1.1" - }, - "time": "2014-07-18 07:15:58", - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ] - }, - { "name": "sdboyer/gliph", "version": "0.1.8", "version_normalized": "0.1.8.0", @@ -2601,5 +2525,412 @@ "homepage": "https://github.com/mtdowling" } ] + }, + { + "name": "behat/mink", + "version": "v1.6.0", + "version_normalized": "1.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Mink.git", + "reference": "090900a0049c441f1e072bbd837db4079b2250c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Mink/zipball/090900a0049c441f1e072bbd837db4079b2250c5", + "reference": "090900a0049c441f1e072bbd837db4079b2250c5", + "shasum": "" + }, + "require": { + "php": ">=5.3.1", + "symfony/css-selector": "~2.0" + }, + "suggest": { + "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)", + "behat/mink-goutte-driver": "fast headless driver for any app without JS emulation", + "behat/mink-selenium2-driver": "slow, but JS-enabled driver for any app (requires Selenium2)", + "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)" + }, + "time": "2014-09-26 09:25:05", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Behat\\Mink": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Web acceptance testing framework for PHP 5.3", + "homepage": "http://mink.behat.org/", + "keywords": [ + "browser", + "testing", + "web" + ] + }, + { + "name": "behat/mink-browserkit-driver", + "version": "v1.2.0", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/MinkBrowserKitDriver.git", + "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/MinkBrowserKitDriver/zipball/aed8f4a596b79014a75254c3e337511c33e38cbd", + "reference": "aed8f4a596b79014a75254c3e337511c33e38cbd", + "shasum": "" + }, + "require": { + "behat/mink": "~1.6@dev", + "php": ">=5.3.1", + "symfony/browser-kit": "~2.0", + "symfony/dom-crawler": "~2.0" + }, + "require-dev": { + "silex/silex": "~1.2" + }, + "time": "2014-09-26 11:35:19", + "type": "mink-driver", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Behat\\Mink\\Driver": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Symfony2 BrowserKit driver for Mink framework", + "homepage": "http://mink.behat.org/", + "keywords": [ + "Mink", + "Symfony2", + "browser", + "testing" + ] + }, + { + "name": "behat/mink-goutte-driver", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/Behat/MinkGoutteDriver.git", + "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/MinkGoutteDriver/zipball/2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", + "reference": "2bf327b4166694ecaa8ae7f956cb6ae252ecf03e", + "shasum": "" + }, + "require": { + "behat/mink": "~1.6@dev", + "behat/mink-browserkit-driver": "~1.2@dev", + "fabpot/goutte": "~1.0.4|~2.0", + "php": ">=5.3.1" + }, + "time": "2014-10-09 09:21:12", + "type": "mink-driver", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Behat\\Mink\\Driver": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Goutte driver for Mink framework", + "homepage": "http://mink.behat.org/", + "keywords": [ + "browser", + "goutte", + "headless", + "testing" + ] + }, + { + "name": "phpunit/phpunit", + "version": "4.1.4", + "version_normalized": "4.1.4.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a71c4842c5fb836d8b200624583b859ec34e8a26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a71c4842c5fb836d8b200624583b859ec34e8a26", + "reference": "a71c4842c5fb836d8b200624583b859ec34e8a26", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": "~2.0", + "phpunit/php-file-iterator": "~1.3.1", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "~1.0.2", + "phpunit/phpunit-mock-objects": "~2.1", + "sebastian/comparator": "~1.0", + "sebastian/diff": "~1.1", + "sebastian/environment": "~1.0", + "sebastian/exporter": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "time": "2014-07-18 07:15:58", + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ] + }, + { + "name": "symfony/dom-crawler", + "version": "v2.5.7", + "version_normalized": "2.5.7.0", + "target-dir": "Symfony/Component/DomCrawler", + "source": { + "type": "git", + "url": "https://github.com/symfony/DomCrawler.git", + "reference": "3245e012a7c88fd9ba5896be8d9b3a8bb99db869" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/3245e012a7c88fd9ba5896be8d9b3a8bb99db869", + "reference": "3245e012a7c88fd9ba5896be8d9b3a8bb99db869", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/css-selector": "~2.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "time": "2014-11-20 13:22:25", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\DomCrawler\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "http://symfony.com" + }, + { + "name": "symfony/browser-kit", + "version": "v2.5.7", + "version_normalized": "2.5.7.0", + "target-dir": "Symfony/Component/BrowserKit", + "source": { + "type": "git", + "url": "https://github.com/symfony/BrowserKit.git", + "reference": "4792cf459d0298bcf4b4f56adfa5d11afd601fc9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/4792cf459d0298bcf4b4f56adfa5d11afd601fc9", + "reference": "4792cf459d0298bcf4b4f56adfa5d11afd601fc9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/dom-crawler": "~2.0" + }, + "require-dev": { + "symfony/css-selector": "~2.0", + "symfony/process": "~2.0" + }, + "suggest": { + "symfony/process": "" + }, + "time": "2014-10-26 07:41:27", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\BrowserKit\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "http://symfony.com" + }, + { + "name": "fabpot/goutte", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/Goutte.git", + "reference": "513c8f832645626f60e8441955ceb339d89d0c23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/513c8f832645626f60e8441955ceb339d89d0c23", + "reference": "513c8f832645626f60e8441955ceb339d89d0c23", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": ">=4,<6", + "php": ">=5.4.0", + "symfony/browser-kit": "~2.1", + "symfony/css-selector": "~2.1", + "symfony/dom-crawler": "~2.1" + }, + "time": "2014-11-17 13:32:14", + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Goutte\\": "Goutte" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A simple PHP Web Scraper", + "homepage": "https://github.com/FriendsOfPHP/Goutte", + "keywords": [ + "scraper" + ] } ] diff --git a/core/vendor/fabpot/goutte/.gitignore b/core/vendor/fabpot/goutte/.gitignore new file mode 100644 index 0000000..81b9258 --- /dev/null +++ b/core/vendor/fabpot/goutte/.gitignore @@ -0,0 +1,3 @@ +composer.lock +phpunit.xml +vendor diff --git a/core/vendor/fabpot/goutte/.travis.yml b/core/vendor/fabpot/goutte/.travis.yml new file mode 100644 index 0000000..66cf320 --- /dev/null +++ b/core/vendor/fabpot/goutte/.travis.yml @@ -0,0 +1,17 @@ +language: php + +php: + - 5.6 + - 5.5 + - 5.4 + - hhvm + +install: + - travis_retry composer install --no-interaction --prefer-source + +script: + - phpunit + +matrix: + allow_failures: + - php: hhvm diff --git a/core/vendor/fabpot/goutte/Goutte/Client.php b/core/vendor/fabpot/goutte/Goutte/Client.php new file mode 100644 index 0000000..ecad34b --- /dev/null +++ b/core/vendor/fabpot/goutte/Goutte/Client.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Goutte; + +use GuzzleHttp\Client as GuzzleClient; +use GuzzleHttp\ClientInterface as GuzzleClientInterface; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\Response as GuzzleResponse; +use GuzzleHttp\Post\PostFile; +use Symfony\Component\BrowserKit\Client as BaseClient; +use Symfony\Component\BrowserKit\Response; + +/** + * Client. + * + * @author Fabien Potencier + * @author Michael Dowling + */ +class Client extends BaseClient +{ + protected $client; + + private $headers = array(); + private $auth = null; + + public function setClient(GuzzleClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getClient() + { + if (!$this->client) { + $this->client = new GuzzleClient(array('defaults' => array('allow_redirects' => false, 'cookies' => true))); + } + + return $this->client; + } + + public function setHeader($name, $value) + { + $this->headers[$name] = $value; + + return $this; + } + + public function removeHeader($name) + { + unset($this->headers[$name]); + } + + public function setAuth($user, $password = '', $type = 'basic') + { + $this->auth = array($user, $password, $type); + + return $this; + } + + public function resetAuth() + { + $this->auth = null; + + return $this; + } + + protected function doRequest($request) + { + $headers = array(); + foreach ($request->getServer() as $key => $val) { + $key = strtolower(str_replace('_', '-', $key)); + $contentHeaders = array('content-length' => true, 'content-md5' => true, 'content-type' => true); + if (0 === strpos($key, 'http-')) { + $headers[substr($key, 5)] = $val; + } + // CONTENT_* are not prefixed with HTTP_ + elseif (isset($contentHeaders[$key])) { + $headers[$key] = $val; + } + } + + $body = null; + if (!in_array($request->getMethod(), array('GET','HEAD'))) { + if (null !== $request->getContent()) { + $body = $request->getContent(); + } else { + $body = $request->getParameters(); + } + } + + $this->getClient()->setDefaultOption('auth', $this->auth); + + $requestOptions = array( + 'body' => $body, + 'cookies' => $this->getCookieJar()->allRawValues($request->getUri()), + 'allow_redirects' => false, + 'timeout' => 30, + ); + + if (!empty($headers)) { + $requestOptions['headers'] = $headers; + } + + $guzzleRequest = $this->getClient()->createRequest( + $request->getMethod(), + $request->getUri(), + $requestOptions + ); + + foreach ($this->headers as $name => $value) { + $guzzleRequest->setHeader($name, $value); + } + + if ('POST' == $request->getMethod() || 'PUT' == $request->getMethod()) { + $this->addPostFiles($guzzleRequest, $request->getFiles()); + } + + // Let BrowserKit handle redirects + try { + $response = $this->getClient()->send($guzzleRequest); + } catch (RequestException $e) { + $response = $e->getResponse(); + if (null === $response) { + throw $e; + } + } + + return $this->createResponse($response); + } + + protected function addPostFiles(RequestInterface $request, array $files, $arrayName = '') + { + foreach ($files as $name => $info) { + if (!empty($arrayName)) { + $name = $arrayName.'['.$name.']'; + } + + if (is_array($info)) { + if (isset($info['tmp_name'])) { + if ('' !== $info['tmp_name']) { + $request->getBody()->addFile(new PostFile($name, fopen($info['tmp_name'], 'r'), isset($info['name']) ? $info['name'] : null)); + } else { + continue; + } + } else { + $this->addPostFiles($request, $info, $name); + } + } else { + $request->getBody()->addFile(new PostFile($name, fopen($info, 'r'))); + } + } + } + + protected function createResponse(GuzzleResponse $response) + { + $headers = $response->getHeaders(); + + return new Response($response->getBody(true), $response->getStatusCode(), $headers); + } +} diff --git a/core/vendor/fabpot/goutte/Goutte/Resources/phar-stub.php b/core/vendor/fabpot/goutte/Goutte/Resources/phar-stub.php new file mode 100644 index 0000000..b9c3b44 --- /dev/null +++ b/core/vendor/fabpot/goutte/Goutte/Resources/phar-stub.php @@ -0,0 +1,14 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +require_once 'phar://'.__FILE__.'/vendor/autoload.php'; + +__HALT_COMPILER(); diff --git a/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php b/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php new file mode 100644 index 0000000..da2e47c --- /dev/null +++ b/core/vendor/fabpot/goutte/Goutte/Tests/ClientTest.php @@ -0,0 +1,319 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Goutte\Tests; + +use Goutte\Client; +use GuzzleHttp\Client as GuzzleClient; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\Response as GuzzleResponse; +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Subscriber\History; +use GuzzleHttp\Subscriber\Mock; +use GuzzleHttp\Post\PostFile; +use Symfony\Component\BrowserKit\Cookie; + +/** + * Goutte Client Test + * + * @author Michael Dowling + */ +class ClientTest extends \PHPUnit_Framework_TestCase +{ + protected $history; + protected $mock; + + protected function getGuzzle() + { + $this->history = new History(); + $this->mock = new Mock(); + $this->mock->addResponse(new GuzzleResponse(200, array(), Stream::factory('

Hi

'))); + $guzzle = new GuzzleClient(array('redirect.disable' => true, 'base_url' => '')); + $guzzle->getEmitter()->attach($this->mock); + $guzzle->getEmitter()->attach($this->history); + + return $guzzle; + } + + public function testCreatesDefaultClient() + { + $client = new Client(); + $this->assertInstanceOf('GuzzleHttp\\ClientInterface', $client->getClient()); + } + + public function testUsesCustomClient() + { + $guzzle = new GuzzleClient(); + $client = new Client(); + $this->assertSame($client, $client->setClient($guzzle)); + $this->assertSame($guzzle, $client->getClient()); + } + + public function testUsesCustomHeaders() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $client->setHeader('X-Test', 'test'); + $crawler = $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('test', $this->history->getLastRequest()->getHeader('X-Test')); + } + + public function testCustomUserAgent() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $client->setHeader('User-Agent', 'foo'); + $crawler = $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('foo', $this->history->getLastRequest()->getHeader('User-Agent')); + } + + public function testUsesAuth() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $client->setAuth('me', '**'); + $crawler = $client->request('GET', 'http://www.example.com/'); + $request = $this->history->getLastRequest(); + $this->assertEquals('me', $request->getConfig()->get('auth')[0]); + $this->assertEquals('**', $request->getConfig()->get('auth')[1]); + $this->assertEquals('basic', $request->getConfig()->get('auth')[2]); + } + + public function testResetsAuth() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $client->setAuth('me', '**'); + $client->resetAuth(); + $crawler = $client->request('GET', 'http://www.example.com/'); + $request = $this->history->getLastRequest(); + $this->assertNull($request->getConfig()->get('auth')[0]); + $this->assertNull($request->getConfig()->get('auth')[1]); + } + + public function testUsesCookies() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $client->getCookieJar()->set(new Cookie('test', '123')); + $crawler = $client->request('GET', 'http://www.example.com/'); + $request = $this->history->getLastRequest(); + $this->assertEquals('test=123', $request->getHeader('Cookie')); + } + + public function testUsesPostFiles() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $files = array( + 'test' => array( + 'name' => 'test.txt', + 'tmp_name' => __FILE__, + ), + ); + + $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); + $request = $this->history->getLastRequest(); + + $files = $request->getBody()->getFiles(); + $this->assertFile(reset($files), 'test', 'test.txt', array( + 'Content-Type' => 'text/plain', + 'Content-Disposition' => 'form-data; filename="test.txt"; name="test"', + )); + } + + public function testUsesPostNamedFiles() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $files = array( + 'test' => __FILE__, + ); + + $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); + $request = $this->history->getLastRequest(); + $files = $request->getBody()->getFiles(); + $this->assertFile(reset($files), 'test', __FILE__, array( + 'Content-Type' => 'text/x-php', + 'Content-Disposition' => 'form-data; filename="ClientTest.php"; name="test"', + )); + } + + public function testUsesPostFilesNestedFields() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $files = array( + 'form' => array( + 'test' => array( + 'name' => 'test.txt', + 'tmp_name' => __FILE__, + ), + ), + ); + + $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); + $request = $this->history->getLastRequest(); + $files = $request->getBody()->getFiles(); + $this->assertFile(reset($files), 'form[test]', 'test.txt', array( + 'Content-Type' => 'text/plain', + 'Content-Disposition' => 'form-data; filename="test.txt"; name="form[test]"', + )); + } + + public function testUsesPostFilesOnClientSide() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $files = array( + 'test' => __FILE__, + ); + + $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); + $request = $this->history->getLastRequest(); + $files = $request->getBody()->getFiles(); + $this->assertFile(reset($files), 'test', __FILE__, array( + 'Content-Type' => 'text/x-php', + 'Content-Disposition' => 'form-data; filename="ClientTest.php"; name="test"', + )); + } + + public function testUsesPostFilesUploadError() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $files = array( + 'test' => array( + 'name' => '', + 'type' => '', + 'tmp_name' => '', + 'error' => 4, + 'size' => 0, + ), + ); + + $crawler = $client->request('POST', 'http://www.example.com/', array(), $files); + $request = $this->history->getLastRequest(); + + $this->assertEquals(array(), $request->getBody()->getFiles()); + } + + public function testCreatesResponse() + { + $guzzle = $this->getGuzzle(); + $client = new Client(); + $client->setClient($guzzle); + $crawler = $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('Hi', $crawler->filter('p')->text()); + } + + public function testHandlesRedirectsCorrectly() + { + $guzzle = $this->getGuzzle(); + + $this->mock->clearQueue(); + $this->mock->addResponse(new GuzzleResponse(301, array( + 'Location' => 'http://www.example.com/', + ))); + $this->mock->addResponse(new GuzzleResponse(200, [], Stream::factory('

Test

'))); + + $client = new Client(); + $client->setClient($guzzle); + + $crawler = $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('Test', $crawler->filter('p')->text()); + + // Ensure that two requests were sent + $this->assertEquals(2, count($this->history)); + } + + public function testConvertsGuzzleHeadersToArrays() + { + $guzzle = $this->getGuzzle(); + + $this->mock->clearQueue(); + $this->mock->addResponse(new GuzzleResponse(200, array( + 'Date' => 'Tue, 04 Jun 2013 13:22:41 GMT', + ))); + + $client = new Client(); + $client->setClient($guzzle); + $client->request('GET', 'http://www.example.com/'); + $response = $client->getResponse(); + $headers = $response->getHeaders(); + + $this->assertInternalType("array", array_shift($headers), "Header not converted from Guzzle\Http\Message\Header to array"); + } + + public function testNullResponseException() + { + $this->setExpectedException('GuzzleHttp\Exception\RequestException'); + $guzzle = $this->getGuzzle(); + $this->mock->clearQueue(); + $exception = new RequestException('', $this->getMock('GuzzleHttp\Message\RequestInterface')); + $this->mock->addException($exception); + $client = new Client(); + $client->setClient($guzzle); + $client->request('GET', 'http://www.example.com/'); + $response = $client->getResponse(); + } + + protected function assertFile(PostFile $postFile, $fieldName, $fileName, $headers) + { + $this->assertEquals($postFile->getName(), $fieldName); + $this->assertEquals($postFile->getFilename(), $fileName); + + $postFileHeaders = $postFile->getHeaders(); + + // Note: Sort 'Content-Disposition' values before comparing, because the order changed in Guzzle 4.2.2 + $postFileHeaders['Content-Disposition'] = explode('; ', $postFileHeaders['Content-Disposition']); + sort($postFileHeaders['Content-Disposition']); + $headers['Content-Disposition'] = explode('; ', $headers['Content-Disposition']); + sort($headers['Content-Disposition']); + + $this->assertEquals($postFileHeaders, $headers); + } + + public function testHttps() + { + $guzzle = $this->getGuzzle(); + + $this->mock->clearQueue(); + $this->mock->addResponse(new GuzzleResponse(200, [], Stream::factory('

Test

'))); + $client = new Client(); + $client->setClient($guzzle); + $crawler = $client->request('GET', 'https://www.example.com/'); + $this->assertEquals('https', $this->history->getLastRequest()->getScheme()); + $this->assertEquals('Test', $crawler->filter('p')->text()); + } + + public function testCustomUserAgentConstructor() + { + $guzzle = $this->getGuzzle(); + $client = new Client([ + 'HTTP_HOST' => '1.2.3.4', + 'HTTP_USER_AGENT' => 'SomeHost', + ]); + $client->setClient($guzzle); + $crawler = $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('SomeHost', $this->history->getLastRequest()->getHeader('User-Agent')); + } +} diff --git a/core/vendor/fabpot/goutte/LICENSE b/core/vendor/fabpot/goutte/LICENSE new file mode 100644 index 0000000..ff727f4 --- /dev/null +++ b/core/vendor/fabpot/goutte/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010-2013 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/vendor/fabpot/goutte/README.rst b/core/vendor/fabpot/goutte/README.rst new file mode 100644 index 0000000..58eecdb --- /dev/null +++ b/core/vendor/fabpot/goutte/README.rst @@ -0,0 +1,114 @@ +Goutte, a simple PHP Web Scraper +================================ + +Goutte is a screen scraping and web crawling library for PHP. + +Goutte provides a nice API to crawl websites and extract data from the HTML/XML +responses. + +Requirements +------------ + +Goutte depends on PHP 5.4+ and Guzzle 4+. + +.. tip:: + + If you need support for PHP 5.3 or Guzzle 3, use Goutte 1.0.6. + +Installation +------------ + +Add ``fabpot/goutte`` as a require dependency in your ``composer.json`` file: + +.. code-block:: bash + + php composer.phar require fabpot/goutte:~2.0 + +.. tip:: + + You can also download the `Goutte.phar`_ file: + + .. code-block:: php + + require_once '/path/to/goutte.phar'; + +Usage +----- + +Create a Goutte Client instance (which extends +``Symfony\Component\BrowserKit\Client``): + +.. code-block:: php + + use Goutte\Client; + + $client = new Client(); + +Make requests with the ``request()`` method: + +.. code-block:: php + + // Go to the symfony.com website + $crawler = $client->request('GET', 'http://www.symfony.com/blog/'); + +The method returns a ``Crawler`` object +(``Symfony\Component\DomCrawler\Crawler``). + +Fine-tune cURL options: + +.. code-block:: php + + $client->getClient()->setDefaultOption('config/curl/'.CURLOPT_TIMEOUT, 60); + +Click on links: + +.. code-block:: php + + // Click on the "Security Advisories" link + $link = $crawler->selectLink('Security Advisories')->link(); + $crawler = $client->click($link); + +Extract data: + +.. code-block:: php + + // Get the latest post in this category and display the titles + $crawler->filter('h2.post > a')->each(function ($node) { + print $node->text()."\n"; + }); + +Submit forms: + +.. code-block:: php + + $crawler = $client->request('GET', 'http://github.com/'); + $crawler = $client->click($crawler->selectLink('Sign in')->link()); + $form = $crawler->selectButton('Sign in')->form(); + $crawler = $client->submit($form, array('login' => 'fabpot', 'password' => 'xxxxxx')); + $crawler->filter('.flash-error')->each(function ($node) { + print $node->text()."\n"; + }); + +More Information +---------------- + +Read the documentation of the BrowserKit and DomCrawler Symfony Components for +more information about what you can do with Goutte. + +Technical Information +--------------------- + +Goutte is a thin wrapper around the following fine PHP libraries: + +* Symfony Components: BrowserKit, CssSelector and DomCrawler; + +* `Guzzle`_ HTTP Component. + +License +------- + +Goutte is licensed under the MIT license. + +.. _`Composer`: http://getcomposer.org +.. _`Goutte.phar`: http://get.sensiolabs.org/goutte.phar +.. _`Guzzle`: http://docs.guzzlephp.org diff --git a/core/vendor/fabpot/goutte/box.json b/core/vendor/fabpot/goutte/box.json new file mode 100644 index 0000000..943c61e --- /dev/null +++ b/core/vendor/fabpot/goutte/box.json @@ -0,0 +1,21 @@ +{ + "output": "goutte.phar", + "chmod": "0755", + "compactors": [ + "Herrera\\Box\\Compactor\\Php" + ], + "extract": false, + "files": [ + "LICENSE", + "Goutte/Client.php" + ], + "finder": [ + { + "name": ["*.php", "*.pem*"], + "exclude": ["Tests", "tests"], + "in": "vendor" + } + ], + "stub": "Goutte/Resources/phar-stub.php", + "web": false +} diff --git a/core/vendor/fabpot/goutte/composer.json b/core/vendor/fabpot/goutte/composer.json new file mode 100644 index 0000000..63d87e7 --- /dev/null +++ b/core/vendor/fabpot/goutte/composer.json @@ -0,0 +1,29 @@ +{ + "name": "fabpot/goutte", + "type": "application", + "description": "A simple PHP Web Scraper", + "keywords": ["scraper"], + "homepage": "https://github.com/FriendsOfPHP/Goutte", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.4.0", + "symfony/browser-kit": "~2.1", + "symfony/css-selector": "~2.1", + "symfony/dom-crawler": "~2.1", + "guzzlehttp/guzzle": ">=4,<6" + }, + "autoload": { + "psr-4": { "Goutte\\": "Goutte" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + } +} diff --git a/core/vendor/fabpot/goutte/phpunit.xml.dist b/core/vendor/fabpot/goutte/phpunit.xml.dist new file mode 100644 index 0000000..9e7ff98 --- /dev/null +++ b/core/vendor/fabpot/goutte/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + + ./Goutte/Tests + + + diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/.gitignore b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CHANGELOG.md b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CHANGELOG.md new file mode 100644 index 0000000..d2b1074 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CHANGELOG.md @@ -0,0 +1,18 @@ +CHANGELOG +========= + +2.3.0 +----- + + * [BC BREAK] `Client::followRedirect()` won't redirect responses with + a non-3xx Status Code and `Location` header anymore, as per + http://tools.ietf.org/html/rfc2616#section-14.30 + + * added `Client::getInternalRequest()` and `Client::getInternalResponse()` to + have access to the BrowserKit internal request and response objects + +2.1.0 +----- + + * [BC BREAK] The CookieJar internals have changed to allow cookies with the + same name on different sub-domains/sub-paths diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php new file mode 100644 index 0000000..e404edb --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php @@ -0,0 +1,619 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +use Symfony\Component\DomCrawler\Crawler; +use Symfony\Component\DomCrawler\Link; +use Symfony\Component\DomCrawler\Form; +use Symfony\Component\Process\PhpProcess; + +/** + * Client simulates a browser. + * + * To make the actual request, you need to implement the doRequest() method. + * + * If you want to be able to run requests in their own process (insulated flag), + * you need to also implement the getScript() method. + * + * @author Fabien Potencier + * + * @api + */ +abstract class Client +{ + protected $history; + protected $cookieJar; + protected $server = array(); + protected $internalRequest; + protected $request; + protected $internalResponse; + protected $response; + protected $crawler; + protected $insulated = false; + protected $redirect; + protected $followRedirects = true; + + private $maxRedirects = -1; + private $redirectCount = 0; + private $isMainRequest = true; + + /** + * Constructor. + * + * @param array $server The server parameters (equivalent of $_SERVER) + * @param History $history A History instance to store the browser history + * @param CookieJar $cookieJar A CookieJar instance to store the cookies + * + * @api + */ + public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null) + { + $this->setServerParameters($server); + $this->history = $history ?: new History(); + $this->cookieJar = $cookieJar ?: new CookieJar(); + } + + /** + * Sets whether to automatically follow redirects or not. + * + * @param bool $followRedirect Whether to follow redirects + * + * @api + */ + public function followRedirects($followRedirect = true) + { + $this->followRedirects = (bool) $followRedirect; + } + + /** + * Sets the maximum number of requests that crawler can follow. + * + * @param int $maxRedirects + */ + public function setMaxRedirects($maxRedirects) + { + $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects; + $this->followRedirects = -1 != $this->maxRedirects; + } + + /** + * Sets the insulated flag. + * + * @param bool $insulated Whether to insulate the requests or not + * + * @throws \RuntimeException When Symfony Process Component is not installed + * + * @api + */ + public function insulate($insulated = true) + { + if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) { + throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.'); + } + + $this->insulated = (bool) $insulated; + } + + /** + * Sets server parameters. + * + * @param array $server An array of server parameters + * + * @api + */ + public function setServerParameters(array $server) + { + $this->server = array_merge(array( + 'HTTP_HOST' => 'localhost', + 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit', + ), $server); + } + + /** + * Sets single server parameter. + * + * @param string $key A key of the parameter + * @param string $value A value of the parameter + */ + public function setServerParameter($key, $value) + { + $this->server[$key] = $value; + } + + /** + * Gets single server parameter for specified key. + * + * @param string $key A key of the parameter to get + * @param string $default A default value when key is undefined + * + * @return string A value of the parameter + */ + public function getServerParameter($key, $default = '') + { + return (isset($this->server[$key])) ? $this->server[$key] : $default; + } + + /** + * Returns the History instance. + * + * @return History A History instance + * + * @api + */ + public function getHistory() + { + return $this->history; + } + + /** + * Returns the CookieJar instance. + * + * @return CookieJar A CookieJar instance + * + * @api + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * Returns the current Crawler instance. + * + * @return Crawler|null A Crawler instance + * + * @api + */ + public function getCrawler() + { + return $this->crawler; + } + + /** + * Returns the current BrowserKit Response instance. + * + * @return Response|null A BrowserKit Response instance + * + * @api + */ + public function getInternalResponse() + { + return $this->internalResponse; + } + + /** + * Returns the current origin response instance. + * + * The origin response is the response instance that is returned + * by the code that handles requests. + * + * @return object|null A response instance + * + * @see doRequest + * + * @api + */ + public function getResponse() + { + return $this->response; + } + + /** + * Returns the current BrowserKit Request instance. + * + * @return Request|null A BrowserKit Request instance + * + * @api + */ + public function getInternalRequest() + { + return $this->internalRequest; + } + + /** + * Returns the current origin Request instance. + * + * The origin request is the request instance that is sent + * to the code that handles requests. + * + * @return object|null A Request instance + * + * @see doRequest + * + * @api + */ + public function getRequest() + { + return $this->request; + } + + /** + * Clicks on a given link. + * + * @param Link $link A Link instance + * + * @return Crawler + * + * @api + */ + public function click(Link $link) + { + if ($link instanceof Form) { + return $this->submit($link); + } + + return $this->request($link->getMethod(), $link->getUri()); + } + + /** + * Submits a form. + * + * @param Form $form A Form instance + * @param array $values An array of form field values + * + * @return Crawler + * + * @api + */ + public function submit(Form $form, array $values = array()) + { + $form->setValues($values); + + return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles()); + } + + /** + * Calls a URI. + * + * @param string $method The request method + * @param string $uri The URI to fetch + * @param array $parameters The Request parameters + * @param array $files The files + * @param array $server The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does) + * @param string $content The raw body data + * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) + * + * @return Crawler + * + * @api + */ + public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true) + { + if ($this->isMainRequest) { + $this->redirectCount = 0; + } else { + ++$this->redirectCount; + } + + $uri = $this->getAbsoluteUri($uri); + + if (!empty($server['HTTP_HOST'])) { + $uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri); + } + + if (isset($server['HTTPS'])) { + $uri = preg_replace('{^'.parse_url($uri, PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri); + } + + $server = array_merge($this->server, $server); + + if (!$this->history->isEmpty()) { + $server['HTTP_REFERER'] = $this->history->current()->getUri(); + } + + $server['HTTP_HOST'] = $this->extractHost($uri); + $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME); + + $this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content); + + $this->request = $this->filterRequest($this->internalRequest); + + if (true === $changeHistory) { + $this->history->add($this->internalRequest); + } + + if ($this->insulated) { + $this->response = $this->doRequestInProcess($this->request); + } else { + $this->response = $this->doRequest($this->request); + } + + $this->internalResponse = $this->filterResponse($this->response); + + $this->cookieJar->updateFromResponse($this->internalResponse, $uri); + + $status = $this->internalResponse->getStatus(); + + if ($status >= 300 && $status < 400) { + $this->redirect = $this->internalResponse->getHeader('Location'); + } else { + $this->redirect = null; + } + + if ($this->followRedirects && $this->redirect) { + return $this->crawler = $this->followRedirect(); + } + + return $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type')); + } + + /** + * Makes a request in another process. + * + * @param object $request An origin request instance + * + * @return object An origin response instance + * + * @throws \RuntimeException When processing returns exit code + */ + protected function doRequestInProcess($request) + { + // We set the TMPDIR (for Macs) and TEMP (for Windows), because on these platforms the temp directory changes based on the user. + $process = new PhpProcess($this->getScript($request), null, array('TMPDIR' => sys_get_temp_dir(), 'TEMP' => sys_get_temp_dir())); + $process->run(); + + if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) { + throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s', $process->getOutput(), $process->getErrorOutput())); + } + + return unserialize($process->getOutput()); + } + + /** + * Makes a request. + * + * @param object $request An origin request instance + * + * @return object An origin response instance + */ + abstract protected function doRequest($request); + + /** + * Returns the script to execute when the request must be insulated. + * + * @param object $request An origin request instance + * + * @throws \LogicException When this abstract class is not implemented + */ + protected function getScript($request) + { + throw new \LogicException('To insulate requests, you need to override the getScript() method.'); + } + + /** + * Filters the BrowserKit request to the origin one. + * + * @param Request $request The BrowserKit Request to filter + * + * @return object An origin request instance + */ + protected function filterRequest(Request $request) + { + return $request; + } + + /** + * Filters the origin response to the BrowserKit one. + * + * @param object $response The origin response to filter + * + * @return Response An BrowserKit Response instance + */ + protected function filterResponse($response) + { + return $response; + } + + /** + * Creates a crawler. + * + * This method returns null if the DomCrawler component is not available. + * + * @param string $uri A URI + * @param string $content Content for the crawler to use + * @param string $type Content type + * + * @return Crawler|null + */ + protected function createCrawlerFromContent($uri, $content, $type) + { + if (!class_exists('Symfony\Component\DomCrawler\Crawler')) { + return; + } + + $crawler = new Crawler(null, $uri); + $crawler->addContent($content, $type); + + return $crawler; + } + + /** + * Goes back in the browser history. + * + * @return Crawler + * + * @api + */ + public function back() + { + return $this->requestFromRequest($this->history->back(), false); + } + + /** + * Goes forward in the browser history. + * + * @return Crawler + * + * @api + */ + public function forward() + { + return $this->requestFromRequest($this->history->forward(), false); + } + + /** + * Reloads the current browser. + * + * @return Crawler + * + * @api + */ + public function reload() + { + return $this->requestFromRequest($this->history->current(), false); + } + + /** + * Follow redirects? + * + * @return Crawler + * + * @throws \LogicException If request was not a redirect + * + * @api + */ + public function followRedirect() + { + if (empty($this->redirect)) { + throw new \LogicException('The request was not redirected.'); + } + + if (-1 !== $this->maxRedirects) { + if ($this->redirectCount > $this->maxRedirects) { + throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects)); + } + } + + $request = $this->internalRequest; + + if (in_array($this->internalResponse->getStatus(), array(302, 303))) { + $method = 'get'; + $files = array(); + $content = null; + } else { + $method = $request->getMethod(); + $files = $request->getFiles(); + $content = $request->getContent(); + } + + if ('get' === strtolower($method)) { + // Don't forward parameters for GET request as it should reach the redirection URI + $parameters = array(); + } else { + $parameters = $request->getParameters(); + } + + $server = $request->getServer(); + $server = $this->updateServerFromUri($server, $this->redirect); + + $this->isMainRequest = false; + + $response = $this->request($method, $this->redirect, $parameters, $files, $server, $content); + + $this->isMainRequest = true; + + return $response; + } + + /** + * Restarts the client. + * + * It flushes history and all cookies. + * + * @api + */ + public function restart() + { + $this->cookieJar->clear(); + $this->history->clear(); + } + + /** + * Takes a URI and converts it to absolute if it is not already absolute. + * + * @param string $uri A URI + * + * @return string An absolute URI + */ + protected function getAbsoluteUri($uri) + { + // already absolute? + if (0 === strpos($uri, 'http')) { + return $uri; + } + + if (!$this->history->isEmpty()) { + $currentUri = $this->history->current()->getUri(); + } else { + $currentUri = sprintf('http%s://%s/', + isset($this->server['HTTPS']) ? 's' : '', + isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost' + ); + } + + // protocol relative URL + if (0 === strpos($uri, '//')) { + return parse_url($currentUri, PHP_URL_SCHEME).':'.$uri; + } + + // anchor? + if (!$uri || '#' == $uri[0]) { + return preg_replace('/#.*?$/', '', $currentUri).$uri; + } + + if ('/' !== $uri[0]) { + $path = parse_url($currentUri, PHP_URL_PATH); + + if ('/' !== substr($path, -1)) { + $path = substr($path, 0, strrpos($path, '/') + 1); + } + + $uri = $path.$uri; + } + + return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri; + } + + /** + * Makes a request from a Request object directly. + * + * @param Request $request A Request instance + * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) + * + * @return Crawler + */ + protected function requestFromRequest(Request $request, $changeHistory = true) + { + return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory); + } + + private function updateServerFromUri($server, $uri) + { + $server['HTTP_HOST'] = $this->extractHost($uri); + $scheme = parse_url($uri, PHP_URL_SCHEME); + $server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme; + unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']); + + return $server; + } + + private function extractHost($uri) + { + $host = parse_url($uri, PHP_URL_HOST); + + if ($port = parse_url($uri, PHP_URL_PORT)) { + return $host.':'.$port; + } + + return $host; + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Cookie.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Cookie.php new file mode 100644 index 0000000..30d93d0 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Cookie.php @@ -0,0 +1,332 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * Cookie represents an HTTP cookie. + * + * @author Fabien Potencier + * + * @api + */ +class Cookie +{ + /** + * Handles dates as defined by RFC 2616 section 3.3.1, and also some other + * non-standard, but common formats. + * + * @var array + */ + private static $dateFormats = array( + 'D, d M Y H:i:s T', + 'D, d-M-y H:i:s T', + 'D, d-M-Y H:i:s T', + 'D, d-m-y H:i:s T', + 'D, d-m-Y H:i:s T', + 'D M j G:i:s Y', + 'D M d H:i:s Y T', + ); + + protected $name; + protected $value; + protected $expires; + protected $path; + protected $domain; + protected $secure; + protected $httponly; + protected $rawValue; + + /** + * Sets a cookie. + * + * @param string $name The cookie name + * @param string $value The value of the cookie + * @param string $expires The time the cookie expires + * @param string $path The path on the server in which the cookie will be available on + * @param string $domain The domain that the cookie is available + * @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client + * @param bool $httponly The cookie httponly flag + * @param bool $encodedValue Whether the value is encoded or not + * + * @api + */ + public function __construct($name, $value, $expires = null, $path = null, $domain = '', $secure = false, $httponly = true, $encodedValue = false) + { + if ($encodedValue) { + $this->value = urldecode($value); + $this->rawValue = $value; + } else { + $this->value = $value; + $this->rawValue = urlencode($value); + } + $this->name = $name; + $this->expires = null === $expires ? null : (int) $expires; + $this->path = empty($path) ? '/' : $path; + $this->domain = $domain; + $this->secure = (bool) $secure; + $this->httponly = (bool) $httponly; + } + + /** + * Returns the HTTP representation of the Cookie. + * + * @return string The HTTP representation of the Cookie + * + * @throws \UnexpectedValueException + * + * @api + */ + public function __toString() + { + $cookie = sprintf('%s=%s', $this->name, $this->rawValue); + + if (null !== $this->expires) { + $dateTime = \DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT')); + + if ($dateTime === false) { + throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.'), $this->expires); + } + + $cookie .= '; expires='.str_replace('+0000', '', $dateTime->format(self::$dateFormats[0])); + } + + if ('' !== $this->domain) { + $cookie .= '; domain='.$this->domain; + } + + if ($this->path) { + $cookie .= '; path='.$this->path; + } + + if ($this->secure) { + $cookie .= '; secure'; + } + + if ($this->httponly) { + $cookie .= '; httponly'; + } + + return $cookie; + } + + /** + * Creates a Cookie instance from a Set-Cookie header value. + * + * @param string $cookie A Set-Cookie header value + * @param string $url The base URL + * + * @return Cookie A Cookie instance + * + * @throws \InvalidArgumentException + * + * @api + */ + public static function fromString($cookie, $url = null) + { + $parts = explode(';', $cookie); + + if (false === strpos($parts[0], '=')) { + throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0])); + } + + list($name, $value) = explode('=', array_shift($parts), 2); + + $values = array( + 'name' => trim($name), + 'value' => trim($value), + 'expires' => null, + 'path' => '/', + 'domain' => '', + 'secure' => false, + 'httponly' => false, + 'passedRawValue' => true, + ); + + if (null !== $url) { + if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host'])) { + throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url)); + } + + $values['domain'] = $urlParts['host']; + $values['path'] = isset($urlParts['path']) ? substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) : ''; + } + + foreach ($parts as $part) { + $part = trim($part); + + if ('secure' === strtolower($part)) { + // Ignore the secure flag if the original URI is not given or is not HTTPS + if (!$url || !isset($urlParts['scheme']) || 'https' != $urlParts['scheme']) { + continue; + } + + $values['secure'] = true; + + continue; + } + + if ('httponly' === strtolower($part)) { + $values['httponly'] = true; + + continue; + } + + if (2 === count($elements = explode('=', $part, 2))) { + if ('expires' === strtolower($elements[0])) { + $elements[1] = self::parseDate($elements[1]); + } + + $values[strtolower($elements[0])] = $elements[1]; + } + } + + return new static( + $values['name'], + $values['value'], + $values['expires'], + $values['path'], + $values['domain'], + $values['secure'], + $values['httponly'], + $values['passedRawValue'] + ); + } + + private static function parseDate($dateValue) + { + // trim single quotes around date if present + if (($length = strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length-1]) { + $dateValue = substr($dateValue, 1, -1); + } + + foreach (self::$dateFormats as $dateFormat) { + if (false !== $date = \DateTime::createFromFormat($dateFormat, $dateValue, new \DateTimeZone('GMT'))) { + return $date->getTimestamp(); + } + } + + // attempt a fallback for unusual formatting + if (false !== $date = date_create($dateValue, new \DateTimeZone('GMT'))) { + return $date->getTimestamp(); + } + + throw new \InvalidArgumentException(sprintf('Could not parse date "%s".', $dateValue)); + } + + /** + * Gets the name of the cookie. + * + * @return string The cookie name + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the cookie. + * + * @return string The cookie value + * + * @api + */ + public function getValue() + { + return $this->value; + } + + /** + * Gets the raw value of the cookie. + * + * @return string The cookie value + * + * @api + */ + public function getRawValue() + { + return $this->rawValue; + } + + /** + * Gets the expires time of the cookie. + * + * @return string The cookie expires time + * + * @api + */ + public function getExpiresTime() + { + return $this->expires; + } + + /** + * Gets the path of the cookie. + * + * @return string The cookie path + * + * @api + */ + public function getPath() + { + return $this->path; + } + + /** + * Gets the domain of the cookie. + * + * @return string The cookie domain + * + * @api + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Returns the secure flag of the cookie. + * + * @return bool The cookie secure flag + * + * @api + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Returns the httponly flag of the cookie. + * + * @return bool The cookie httponly flag + * + * @api + */ + public function isHttpOnly() + { + return $this->httponly; + } + + /** + * Returns true if the cookie has expired. + * + * @return bool true if the cookie has expired, false otherwise + * + * @api + */ + public function isExpired() + { + return null !== $this->expires && 0 !== $this->expires && $this->expires < time(); + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CookieJar.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CookieJar.php new file mode 100644 index 0000000..a3b0e24 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CookieJar.php @@ -0,0 +1,265 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * CookieJar. + * + * @author Fabien Potencier + * + * @api + */ +class CookieJar +{ + protected $cookieJar = array(); + + /** + * Sets a cookie. + * + * @param Cookie $cookie A Cookie instance + * + * @api + */ + public function set(Cookie $cookie) + { + $this->cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; + } + + /** + * Gets a cookie by name. + * + * You should never use an empty domain, but if you do so, + * this method returns the first cookie for the given name/path + * (this behavior ensures a BC behavior with previous versions of + * Symfony). + * + * @param string $name The cookie name + * @param string $path The cookie path + * @param string $domain The cookie domain + * + * @return Cookie|null A Cookie instance or null if the cookie does not exist + * + * @api + */ + public function get($name, $path = '/', $domain = null) + { + $this->flushExpiredCookies(); + + if (!empty($domain)) { + foreach ($this->cookieJar as $cookieDomain => $pathCookies) { + if ($cookieDomain) { + $cookieDomain = '.'.ltrim($cookieDomain, '.'); + if ($cookieDomain != substr('.'.$domain, -strlen($cookieDomain))) { + continue; + } + } + + foreach ($pathCookies as $cookiePath => $namedCookies) { + if ($cookiePath != substr($path, 0, strlen($cookiePath))) { + continue; + } + if (isset($namedCookies[$name])) { + return $namedCookies[$name]; + } + } + } + + return; + } + + // avoid relying on this behavior that is mainly here for BC reasons + foreach ($this->cookieJar as $cookies) { + if (isset($cookies[$path][$name])) { + return $cookies[$path][$name]; + } + } + } + + /** + * Removes a cookie by name. + * + * You should never use an empty domain, but if you do so, + * all cookies for the given name/path expire (this behavior + * ensures a BC behavior with previous versions of Symfony). + * + * @param string $name The cookie name + * @param string $path The cookie path + * @param string $domain The cookie domain + * + * @api + */ + public function expire($name, $path = '/', $domain = null) + { + if (null === $path) { + $path = '/'; + } + + if (empty($domain)) { + // an empty domain means any domain + // this should never happen but it allows for a better BC + $domains = array_keys($this->cookieJar); + } else { + $domains = array($domain); + } + + foreach ($domains as $domain) { + unset($this->cookieJar[$domain][$path][$name]); + + if (empty($this->cookieJar[$domain][$path])) { + unset($this->cookieJar[$domain][$path]); + + if (empty($this->cookieJar[$domain])) { + unset($this->cookieJar[$domain]); + } + } + } + } + + /** + * Removes all the cookies from the jar. + * + * @api + */ + public function clear() + { + $this->cookieJar = array(); + } + + /** + * Updates the cookie jar from a response Set-Cookie headers. + * + * @param array $setCookies Set-Cookie headers from an HTTP response + * @param string $uri The base URL + */ + public function updateFromSetCookie(array $setCookies, $uri = null) + { + $cookies = array(); + + foreach ($setCookies as $cookie) { + foreach (explode(',', $cookie) as $i => $part) { + if (0 === $i || preg_match('/^(?P\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) { + $cookies[] = ltrim($part); + } else { + $cookies[count($cookies) - 1] .= ','.$part; + } + } + } + + foreach ($cookies as $cookie) { + try { + $this->set(Cookie::fromString($cookie, $uri)); + } catch (\InvalidArgumentException $e) { + // invalid cookies are just ignored + } + } + } + + /** + * Updates the cookie jar from a Response object. + * + * @param Response $response A Response object + * @param string $uri The base URL + */ + public function updateFromResponse(Response $response, $uri = null) + { + $this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri); + } + + /** + * Returns not yet expired cookies. + * + * @return Cookie[] An array of cookies + */ + public function all() + { + $this->flushExpiredCookies(); + + $flattenedCookies = array(); + foreach ($this->cookieJar as $path) { + foreach ($path as $cookies) { + foreach ($cookies as $cookie) { + $flattenedCookies[] = $cookie; + } + } + } + + return $flattenedCookies; + } + + /** + * Returns not yet expired cookie values for the given URI. + * + * @param string $uri A URI + * @param bool $returnsRawValue Returns raw value or urldecoded value + * + * @return array An array of cookie values + */ + public function allValues($uri, $returnsRawValue = false) + { + $this->flushExpiredCookies(); + + $parts = array_replace(array('path' => '/'), parse_url($uri)); + $cookies = array(); + foreach ($this->cookieJar as $domain => $pathCookies) { + if ($domain) { + $domain = '.'.ltrim($domain, '.'); + if ($domain != substr('.'.$parts['host'], -strlen($domain))) { + continue; + } + } + + foreach ($pathCookies as $path => $namedCookies) { + if ($path != substr($parts['path'], 0, strlen($path))) { + continue; + } + + foreach ($namedCookies as $cookie) { + if ($cookie->isSecure() && 'https' != $parts['scheme']) { + continue; + } + + $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue(); + } + } + } + + return $cookies; + } + + /** + * Returns not yet expired raw cookie values for the given URI. + * + * @param string $uri A URI + * + * @return array An array of cookie values + */ + public function allRawValues($uri) + { + return $this->allValues($uri, true); + } + + /** + * Removes all expired cookies. + */ + public function flushExpiredCookies() + { + foreach ($this->cookieJar as $domain => $pathCookies) { + foreach ($pathCookies as $path => $namedCookies) { + foreach ($namedCookies as $name => $cookie) { + if ($cookie->isExpired()) { + unset($this->cookieJar[$domain][$path][$name]); + } + } + } + } + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/History.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/History.php new file mode 100644 index 0000000..d76d79b --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/History.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * History. + * + * @author Fabien Potencier + */ +class History +{ + protected $stack = array(); + protected $position = -1; + + /** + * Clears the history. + */ + public function clear() + { + $this->stack = array(); + $this->position = -1; + } + + /** + * Adds a Request to the history. + * + * @param Request $request A Request instance + */ + public function add(Request $request) + { + $this->stack = array_slice($this->stack, 0, $this->position + 1); + $this->stack[] = clone $request; + $this->position = count($this->stack) - 1; + } + + /** + * Returns true if the history is empty. + * + * @return bool true if the history is empty, false otherwise + */ + public function isEmpty() + { + return count($this->stack) == 0; + } + + /** + * Goes back in the history. + * + * @return Request A Request instance + * + * @throws \LogicException if the stack is already on the first page + */ + public function back() + { + if ($this->position < 1) { + throw new \LogicException('You are already on the first page.'); + } + + return clone $this->stack[--$this->position]; + } + + /** + * Goes forward in the history. + * + * @return Request A Request instance + * + * @throws \LogicException if the stack is already on the last page + */ + public function forward() + { + if ($this->position > count($this->stack) - 2) { + throw new \LogicException('You are already on the last page.'); + } + + return clone $this->stack[++$this->position]; + } + + /** + * Returns the current element in the history. + * + * @return Request A Request instance + * + * @throws \LogicException if the stack is empty + */ + public function current() + { + if (-1 == $this->position) { + throw new \LogicException('The page history is empty.'); + } + + return clone $this->stack[$this->position]; + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/LICENSE b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/LICENSE new file mode 100644 index 0000000..0b3292c --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/README.md b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/README.md new file mode 100644 index 0000000..1b40c9f --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/README.md @@ -0,0 +1,23 @@ +BrowserKit Component +==================== + +BrowserKit simulates the behavior of a web browser. + +The component only provide an abstract client and does not provide any +"default" backend for the HTTP layer. + +Resources +--------- + +For a simple implementation of a browser based on an HTTP layer, have a look +at [Goutte](https://github.com/fabpot/Goutte). + +For an implementation based on HttpKernelInterface, have a look at the +[Client](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Client.php) +provided by the HttpKernel component. + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/BrowserKit/ + $ composer.phar install + $ phpunit diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Request.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Request.php new file mode 100644 index 0000000..6d381d2 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Request.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * Request object. + * + * @author Fabien Potencier + * + * @api + */ +class Request +{ + protected $uri; + protected $method; + protected $parameters; + protected $files; + protected $cookies; + protected $server; + protected $content; + + /** + * Constructor. + * + * @param string $uri The request URI + * @param string $method The HTTP method request + * @param array $parameters The request parameters + * @param array $files An array of uploaded files + * @param array $cookies An array of cookies + * @param array $server An array of server parameters + * @param string $content The raw body data + * + * @api + */ + public function __construct($uri, $method, array $parameters = array(), array $files = array(), array $cookies = array(), array $server = array(), $content = null) + { + $this->uri = $uri; + $this->method = $method; + $this->parameters = $parameters; + $this->files = $files; + $this->cookies = $cookies; + $this->server = $server; + $this->content = $content; + } + + /** + * Gets the request URI. + * + * @return string The request URI + * + * @api + */ + public function getUri() + { + return $this->uri; + } + + /** + * Gets the request HTTP method. + * + * @return string The request HTTP method + * + * @api + */ + public function getMethod() + { + return $this->method; + } + + /** + * Gets the request parameters. + * + * @return array The request parameters + * + * @api + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets the request server files. + * + * @return array The request files + * + * @api + */ + public function getFiles() + { + return $this->files; + } + + /** + * Gets the request cookies. + * + * @return array The request cookies + * + * @api + */ + public function getCookies() + { + return $this->cookies; + } + + /** + * Gets the request server parameters. + * + * @return array The request server parameters + * + * @api + */ + public function getServer() + { + return $this->server; + } + + /** + * Gets the request raw body data. + * + * @return string The request raw body data. + * + * @api + */ + public function getContent() + { + return $this->content; + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Response.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Response.php new file mode 100644 index 0000000..a3a22cc --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Response.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * Response object. + * + * @author Fabien Potencier + * + * @api + */ +class Response +{ + protected $content; + protected $status; + protected $headers; + + /** + * Constructor. + * + * The headers array is a set of key/value pairs. If a header is present multiple times + * then the value is an array of all the values. + * + * @param string $content The content of the response + * @param int $status The response status code + * @param array $headers An array of headers + * + * @api + */ + public function __construct($content = '', $status = 200, array $headers = array()) + { + $this->content = $content; + $this->status = $status; + $this->headers = $headers; + } + + /** + * Converts the response object to string containing all headers and the response content. + * + * @return string The response with headers and content + */ + public function __toString() + { + $headers = ''; + foreach ($this->headers as $name => $value) { + if (is_string($value)) { + $headers .= $this->buildHeader($name, $value); + } else { + foreach ($value as $headerValue) { + $headers .= $this->buildHeader($name, $headerValue); + } + } + } + + return $headers."\n".$this->content; + } + + /** + * Returns the build header line. + * + * @param string $name The header name + * @param string $value The header value + * + * @return string The built header line + */ + protected function buildHeader($name, $value) + { + return sprintf("%s: %s\n", $name, $value); + } + + /** + * Gets the response content. + * + * @return string The response content + * + * @api + */ + public function getContent() + { + return $this->content; + } + + /** + * Gets the response status code. + * + * @return int The response status code + * + * @api + */ + public function getStatus() + { + return $this->status; + } + + /** + * Gets the response headers. + * + * @return array The response headers + * + * @api + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Gets a response header. + * + * @param string $header The header name + * @param bool $first Whether to return the first value or all header values + * + * @return string|array The first header value if $first is true, an array of values otherwise + */ + public function getHeader($header, $first = true) + { + foreach ($this->headers as $key => $value) { + if (str_replace('-', '_', strtolower($key)) == str_replace('-', '_', strtolower($header))) { + if ($first) { + return is_array($value) ? (count($value) ? $value[0] : '') : $value; + } + + return is_array($value) ? $value : array($value); + } + } + + return $first ? null : array(); + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ClientTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ClientTest.php new file mode 100644 index 0000000..38951b4 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ClientTest.php @@ -0,0 +1,625 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\Client; +use Symfony\Component\BrowserKit\History; +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\BrowserKit\Request; +use Symfony\Component\BrowserKit\Response; + +class SpecialResponse extends Response +{ +} + +class TestClient extends Client +{ + protected $nextResponse = null; + protected $nextScript = null; + + public function setNextResponse(Response $response) + { + $this->nextResponse = $response; + } + + public function setNextScript($script) + { + $this->nextScript = $script; + } + + protected function doRequest($request) + { + if (null === $this->nextResponse) { + return new Response(); + } + + $response = $this->nextResponse; + $this->nextResponse = null; + + return $response; + } + + protected function filterResponse($response) + { + if ($response instanceof SpecialResponse) { + return new Response($response->getContent(), $response->getStatus(), $response->getHeaders()); + } + + return $response; + } + + protected function getScript($request) + { + $r = new \ReflectionClass('Symfony\Component\BrowserKit\Response'); + $path = $r->getFileName(); + + return <<nextScript); +EOF; + } +} + +class ClientTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Symfony\Component\BrowserKit\Client::getHistory + */ + public function testGetHistory() + { + $client = new TestClient(array(), $history = new History()); + $this->assertSame($history, $client->getHistory(), '->getHistory() returns the History'); + } + + /** + * @covers Symfony\Component\BrowserKit\Client::getCookieJar + */ + public function testGetCookieJar() + { + $client = new TestClient(array(), null, $cookieJar = new CookieJar()); + $this->assertSame($cookieJar, $client->getCookieJar(), '->getCookieJar() returns the CookieJar'); + } + + /** + * @covers Symfony\Component\BrowserKit\Client::getRequest + */ + public function testGetRequest() + { + $client = new TestClient(); + $client->request('GET', 'http://example.com/'); + + $this->assertEquals('http://example.com/', $client->getRequest()->getUri(), '->getCrawler() returns the Request of the last request'); + } + + public function testGetRequestWithIpAsHost() + { + $client = new TestClient(); + $client->request('GET', 'https://example.com/foo', array(), array(), array('HTTP_HOST' => '127.0.0.1')); + + $this->assertEquals('https://127.0.0.1/foo', $client->getRequest()->getUri()); + } + + public function testGetResponse() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo')); + $client->request('GET', 'http://example.com/'); + + $this->assertEquals('foo', $client->getResponse()->getContent(), '->getCrawler() returns the Response of the last request'); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getResponse(), '->getCrawler() returns the Response of the last request'); + } + + public function testGetInternalResponse() + { + $client = new TestClient(); + $client->setNextResponse(new SpecialResponse('foo')); + $client->request('GET', 'http://example.com/'); + + $this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getInternalResponse()); + $this->assertNotInstanceOf('Symfony\Component\BrowserKit\Tests\SpecialResponse', $client->getInternalResponse()); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Tests\SpecialResponse', $client->getResponse()); + } + + public function testGetContent() + { + $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; + + $client = new TestClient(); + $client->request('POST', 'http://example.com/jsonrpc', array(), array(), array(), $json); + $this->assertEquals($json, $client->getRequest()->getContent()); + } + + /** + * @covers Symfony\Component\BrowserKit\Client::getCrawler + */ + public function testGetCrawler() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo')); + $crawler = $client->request('GET', 'http://example.com/'); + + $this->assertSame($crawler, $client->getCrawler(), '->getCrawler() returns the Crawler of the last request'); + } + + public function testRequestHttpHeaders() + { + $client = new TestClient(); + $client->request('GET', '/'); + $headers = $client->getRequest()->getServer(); + $this->assertEquals('localhost', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com'); + $headers = $client->getRequest()->getServer(); + $this->assertEquals('www.example.com', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header'); + + $client->request('GET', 'https://www.example.com'); + $headers = $client->getRequest()->getServer(); + $this->assertTrue($headers['HTTPS'], '->request() sets the HTTPS header'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com:8080'); + $headers = $client->getRequest()->getServer(); + $this->assertEquals('www.example.com:8080', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header with port'); + } + + public function testRequestURIConversion() + { + $client = new TestClient(); + $client->request('GET', '/foo'); + $this->assertEquals('http://localhost/foo', $client->getRequest()->getUri(), '->request() converts the URI to an absolute one'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com'); + $this->assertEquals('http://www.example.com', $client->getRequest()->getUri(), '->request() does not change absolute URIs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/'); + $client->request('GET', '/foo'); + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo'); + $client->request('GET', '#'); + $this->assertEquals('http://www.example.com/foo#', $client->getRequest()->getUri(), '->request() uses the previous request for #'); + $client->request('GET', '#'); + $this->assertEquals('http://www.example.com/foo#', $client->getRequest()->getUri(), '->request() uses the previous request for #'); + $client->request('GET', '#foo'); + $this->assertEquals('http://www.example.com/foo#foo', $client->getRequest()->getUri(), '->request() uses the previous request for #'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/'); + $client->request('GET', 'bar'); + $this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->request('GET', 'bar'); + $this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + } + + public function testRequestURIConversionByServerHost() + { + $client = new TestClient(); + + $server = array('HTTP_HOST' => 'www.exampl+e.com:8000'); + $parameters = array(); + $files = array(); + + $client->request('GET', 'http://exampl+e.com', $parameters, $files, $server); + $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST to add port'); + + $client->request('GET', 'http://exampl+e.com:8888', $parameters, $files, $server); + $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST to modify existing port'); + + $client->request('GET', 'http://exampl+e.com:8000', $parameters, $files, $server); + $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST respects correct set port'); + } + + public function testRequestReferer() + { + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->request('GET', 'bar'); + $server = $client->getRequest()->getServer(); + $this->assertEquals('http://www.example.com/foo/foobar', $server['HTTP_REFERER'], '->request() sets the referer'); + } + + public function testRequestHistory() + { + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->request('GET', 'bar'); + + $this->assertEquals('http://www.example.com/foo/bar', $client->getHistory()->current()->getUri(), '->request() updates the History'); + $this->assertEquals('http://www.example.com/foo/foobar', $client->getHistory()->back()->getUri(), '->request() updates the History'); + } + + public function testRequestCookies() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo', 200, array('Set-Cookie' => 'foo=bar'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals(array('foo' => 'bar'), $client->getCookieJar()->allValues('http://www.example.com/foo/foobar'), '->request() updates the CookieJar'); + + $client->request('GET', 'bar'); + $this->assertEquals(array('foo' => 'bar'), $client->getCookieJar()->allValues('http://www.example.com/foo/foobar'), '->request() updates the CookieJar'); + } + + public function testRequestSecureCookies() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo', 200, array('Set-Cookie' => 'foo=bar; path=/; secure'))); + $client->request('GET', 'https://www.example.com/foo/foobar'); + + $this->assertTrue($client->getCookieJar()->get('foo', '/', 'www.example.com')->isSecure()); + } + + public function testClick() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo')); + $crawler = $client->request('GET', 'http://www.example.com/foo/foobar'); + + $client->click($crawler->filter('a')->link()); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() clicks on links'); + } + + public function testClickForm() + { + $client = new TestClient(); + $client->setNextResponse(new Response('
')); + $crawler = $client->request('GET', 'http://www.example.com/foo/foobar'); + + $client->click($crawler->filter('input')->form()); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() Form submit forms'); + } + + public function testSubmit() + { + $client = new TestClient(); + $client->setNextResponse(new Response('
')); + $crawler = $client->request('GET', 'http://www.example.com/foo/foobar'); + + $client->submit($crawler->filter('input')->form()); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->submit() submit forms'); + } + + public function testSubmitPreserveAuth() + { + $client = new TestClient(array('PHP_AUTH_USER' => 'foo', 'PHP_AUTH_PW' => 'bar')); + $client->setNextResponse(new Response('
')); + $crawler = $client->request('GET', 'http://www.example.com/foo/foobar'); + + $server = $client->getRequest()->getServer(); + $this->assertArrayHasKey('PHP_AUTH_USER', $server); + $this->assertEquals('foo', $server['PHP_AUTH_USER']); + $this->assertArrayHasKey('PHP_AUTH_PW', $server); + $this->assertEquals('bar', $server['PHP_AUTH_PW']); + + $client->submit($crawler->filter('input')->form()); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->submit() submit forms'); + + $server = $client->getRequest()->getServer(); + $this->assertArrayHasKey('PHP_AUTH_USER', $server); + $this->assertEquals('foo', $server['PHP_AUTH_USER']); + $this->assertArrayHasKey('PHP_AUTH_PW', $server); + $this->assertEquals('bar', $server['PHP_AUTH_PW']); + } + + public function testFollowRedirect() + { + $client = new TestClient(); + $client->followRedirects(false); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + try { + $client->followRedirect(); + $this->fail('->followRedirect() throws a \LogicException if the request was not redirected'); + } catch (\Exception $e) { + $this->assertInstanceof('LogicException', $e, '->followRedirect() throws a \LogicException if the request was not redirected'); + } + + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->followRedirect(); + + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() automatically follows redirects if followRedirects is true'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 201, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->followRedirect() does not follow redirect if HTTP Code is not 30x'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 201, array('Location' => 'http://www.example.com/redirected'))); + $client->followRedirects(false); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + try { + $client->followRedirect(); + $this->fail('->followRedirect() throws a \LogicException if the request did not respond with 30x HTTP Code'); + } catch (\Exception $e) { + $this->assertInstanceof('LogicException', $e, '->followRedirect() throws a \LogicException if the request did not respond with 30x HTTP Code'); + } + } + + public function testFollowRelativeRedirect() + { + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => '/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => '/redirected:1234'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals('http://www.example.com/redirected:1234', $client->getRequest()->getUri(), '->followRedirect() follows relative urls'); + } + + public function testFollowRedirectWithMaxRedirects() + { + $client = new TestClient(); + $client->setMaxRedirects(1); + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any'); + + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected2'))); + try { + $client->followRedirect(); + $this->fail('->followRedirect() throws a \LogicException if the request was redirected and limit of redirections was reached'); + } catch (\Exception $e) { + $this->assertInstanceof('LogicException', $e, '->followRedirect() throws a \LogicException if the request was redirected and limit of redirections was reached'); + } + + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any'); + + $client->setNextResponse(new Response('', 302, array('Location' => '/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows relative URLs'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => '//www.example.org/'))); + $client->request('GET', 'https://www.example.com/'); + + $this->assertEquals('https://www.example.org/', $client->getRequest()->getUri(), '->followRedirect() follows protocol-relative URLs'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('POST', 'http://www.example.com/foo/foobar', array('name' => 'bar')); + + $this->assertEquals('get', $client->getRequest()->getMethod(), '->followRedirect() uses a get for 302'); + $this->assertEquals(array(), $client->getRequest()->getParameters(), '->followRedirect() does not submit parameters when changing the method'); + } + + public function testFollowRedirectWithCookies() + { + $client = new TestClient(); + $client->followRedirects(false); + $client->setNextResponse(new Response('', 302, array( + 'Location' => 'http://www.example.com/redirected', + 'Set-Cookie' => 'foo=bar', + ))); + $client->request('GET', 'http://www.example.com/'); + $this->assertEquals(array(), $client->getRequest()->getCookies()); + $client->followRedirect(); + $this->assertEquals(array('foo' => 'bar'), $client->getRequest()->getCookies()); + } + + public function testFollowRedirectWithHeaders() + { + $headers = array( + 'HTTP_HOST' => 'www.example.com', + 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit', + 'CONTENT_TYPE' => 'application/vnd.custom+xml', + 'HTTPS' => false, + ); + + $client = new TestClient(); + $client->followRedirects(false); + $client->setNextResponse(new Response('', 302, array( + 'Location' => 'http://www.example.com/redirected', + ))); + $client->request('GET', 'http://www.example.com/', array(), array(), array( + 'CONTENT_TYPE' => 'application/vnd.custom+xml', + )); + + $this->assertEquals($headers, $client->getRequest()->getServer()); + + $client->followRedirect(); + + $headers['HTTP_REFERER'] = 'http://www.example.com/'; + + $this->assertEquals($headers, $client->getRequest()->getServer()); + } + + public function testFollowRedirectWithPort() + { + $headers = array( + 'HTTP_HOST' => 'www.example.com:8080', + 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit', + 'HTTPS' => false, + 'HTTP_REFERER' => 'http://www.example.com:8080/', + ); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array( + 'Location' => 'http://www.example.com:8080/redirected', + ))); + $client->request('GET', 'http://www.example.com:8080/'); + + $this->assertEquals($headers, $client->getRequest()->getServer()); + } + + public function testBack() + { + $client = new TestClient(); + + $parameters = array('foo' => 'bar'); + $files = array('myfile.foo' => 'baz'); + $server = array('X_TEST_FOO' => 'bazbar'); + $content = 'foobarbaz'; + + $client->request('GET', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content); + $client->request('GET', 'http://www.example.com/foo'); + $client->back(); + + $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->back() goes back in the history'); + $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->back() keeps parameters'); + $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->back() keeps files'); + $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->back() keeps $_SERVER'); + $this->assertEquals($content, $client->getRequest()->getContent(), '->back() keeps content'); + } + + public function testForward() + { + $client = new TestClient(); + + $parameters = array('foo' => 'bar'); + $files = array('myfile.foo' => 'baz'); + $server = array('X_TEST_FOO' => 'bazbar'); + $content = 'foobarbaz'; + + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->request('GET', 'http://www.example.com/foo', $parameters, $files, $server, $content); + $client->back(); + $client->forward(); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->forward() goes forward in the history'); + $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->forward() keeps parameters'); + $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->forward() keeps files'); + $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->forward() keeps $_SERVER'); + $this->assertEquals($content, $client->getRequest()->getContent(), '->forward() keeps content'); + } + + public function testReload() + { + $client = new TestClient(); + + $parameters = array('foo' => 'bar'); + $files = array('myfile.foo' => 'baz'); + $server = array('X_TEST_FOO' => 'bazbar'); + $content = 'foobarbaz'; + + $client->request('GET', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content); + $client->reload(); + + $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->reload() reloads the current page'); + $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->reload() keeps parameters'); + $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->reload() keeps files'); + $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->reload() keeps $_SERVER'); + $this->assertEquals($content, $client->getRequest()->getContent(), '->reload() keeps content'); + } + + public function testRestart() + { + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->restart(); + + $this->assertTrue($client->getHistory()->isEmpty(), '->restart() clears the history'); + $this->assertEquals(array(), $client->getCookieJar()->all(), '->restart() clears the cookies'); + } + + public function testInsulatedRequests() + { + $client = new TestClient(); + $client->insulate(); + $client->setNextScript("new Symfony\Component\BrowserKit\Response('foobar')"); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + $this->assertEquals('foobar', $client->getResponse()->getContent(), '->insulate() process the request in a forked process'); + + $client->setNextScript("new Symfony\Component\BrowserKit\Response('foobar)"); + + try { + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->fail('->request() throws a \RuntimeException if the script has an error'); + } catch (\Exception $e) { + $this->assertInstanceof('RuntimeException', $e, '->request() throws a \RuntimeException if the script has an error'); + } + } + + public function testGetServerParameter() + { + $client = new TestClient(); + $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST')); + $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); + $this->assertEquals('testvalue', $client->getServerParameter('testkey', 'testvalue')); + } + + public function testSetServerParameter() + { + $client = new TestClient(); + + $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST')); + $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); + + $client->setServerParameter('HTTP_HOST', 'testhost'); + $this->assertEquals('testhost', $client->getServerParameter('HTTP_HOST')); + + $client->setServerParameter('HTTP_USER_AGENT', 'testua'); + $this->assertEquals('testua', $client->getServerParameter('HTTP_USER_AGENT')); + } + + public function testSetServerParameterInRequest() + { + $client = new TestClient(); + + $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST')); + $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); + + $client->request('GET', 'https://www.example.com/https/www.example.com', array(), array(), array( + 'HTTP_HOST' => 'testhost', + 'HTTP_USER_AGENT' => 'testua', + 'HTTPS' => false, + 'NEW_SERVER_KEY' => 'new-server-key-value', + )); + + $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST')); + $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); + + $this->assertEquals('http://testhost/https/www.example.com', $client->getRequest()->getUri()); + + $server = $client->getRequest()->getServer(); + + $this->assertArrayHasKey('HTTP_USER_AGENT', $server); + $this->assertEquals('testua', $server['HTTP_USER_AGENT']); + + $this->assertArrayHasKey('HTTP_HOST', $server); + $this->assertEquals('testhost', $server['HTTP_HOST']); + + $this->assertArrayHasKey('NEW_SERVER_KEY', $server); + $this->assertEquals('new-server-key-value', $server['NEW_SERVER_KEY']); + + $this->assertArrayHasKey('HTTPS', $server); + $this->assertFalse($server['HTTPS']); + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieJarTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieJarTest.php new file mode 100644 index 0000000..3deef66 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieJarTest.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\BrowserKit\Cookie; +use Symfony\Component\BrowserKit\Response; + +class CookieJarTest extends \PHPUnit_Framework_TestCase +{ + public function testSetGet() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie = new Cookie('foo', 'bar')); + + $this->assertEquals($cookie, $cookieJar->get('foo'), '->set() sets a cookie'); + + $this->assertNull($cookieJar->get('foobar'), '->get() returns null if the cookie does not exist'); + + $cookieJar->set($cookie = new Cookie('foo', 'bar', time() - 86400)); + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + } + + public function testExpire() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie = new Cookie('foo', 'bar')); + $cookieJar->expire('foo'); + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + } + + public function testAll() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar')); + $cookieJar->set($cookie2 = new Cookie('bar', 'foo')); + + $this->assertEquals(array($cookie1, $cookie2), $cookieJar->all(), '->all() returns all cookies in the jar'); + } + + public function testClear() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar')); + $cookieJar->set($cookie2 = new Cookie('bar', 'foo')); + + $cookieJar->clear(); + + $this->assertEquals(array(), $cookieJar->all(), '->clear() expires all cookies'); + } + + public function testUpdateFromResponse() + { + $response = new Response('', 200, array('Set-Cookie' => 'foo=foo')); + + $cookieJar = new CookieJar(); + $cookieJar->updateFromResponse($response); + + $this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromResponse() updates cookies from a Response objects'); + } + + public function testUpdateFromSetCookie() + { + $setCookies = array('foo=foo'); + + $cookieJar = new CookieJar(); + $cookieJar->set(new Cookie('bar', 'bar')); + $cookieJar->updateFromSetCookie($setCookies); + + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('foo')); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('bar')); + $this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromSetCookie() updates cookies from a Set-Cookie header'); + $this->assertEquals('bar', $cookieJar->get('bar')->getValue(), '->updateFromSetCookie() keeps existing cookies'); + } + + public function testUpdateFromEmptySetCookie() + { + $cookieJar = new CookieJar(); + $cookieJar->updateFromSetCookie(array('')); + $this->assertEquals(array(), $cookieJar->all()); + } + + public function testUpdateFromSetCookieWithMultipleCookies() + { + $timestamp = time() + 3600; + $date = gmdate('D, d M Y H:i:s \G\M\T', $timestamp); + $setCookies = array(sprintf('foo=foo; expires=%s; domain=.symfony.com; path=/, bar=bar; domain=.blog.symfony.com, PHPSESSID=id; expires=%s', $date, $date)); + + $cookieJar = new CookieJar(); + $cookieJar->updateFromSetCookie($setCookies); + + $fooCookie = $cookieJar->get('foo', '/', '.symfony.com'); + $barCookie = $cookieJar->get('bar', '/', '.blog.symfony.com'); + $phpCookie = $cookieJar->get('PHPSESSID'); + + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $fooCookie); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $barCookie); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $phpCookie); + $this->assertEquals('foo', $fooCookie->getValue()); + $this->assertEquals('bar', $barCookie->getValue()); + $this->assertEquals('id', $phpCookie->getValue()); + $this->assertEquals($timestamp, $fooCookie->getExpiresTime()); + $this->assertNull($barCookie->getExpiresTime()); + $this->assertEquals($timestamp, $phpCookie->getExpiresTime()); + } + + /** + * @dataProvider provideAllValuesValues + */ + public function testAllValues($uri, $values) + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo_nothing', 'foo')); + $cookieJar->set($cookie2 = new Cookie('foo_expired', 'foo', time() - 86400)); + $cookieJar->set($cookie3 = new Cookie('foo_path', 'foo', null, '/foo')); + $cookieJar->set($cookie4 = new Cookie('foo_domain', 'foo', null, '/', '.example.com')); + $cookieJar->set($cookie4 = new Cookie('foo_strict_domain', 'foo', null, '/', '.www4.example.com')); + $cookieJar->set($cookie5 = new Cookie('foo_secure', 'foo', null, '/', '', true)); + + $this->assertEquals($values, array_keys($cookieJar->allValues($uri)), '->allValues() returns the cookie for a given URI'); + } + + public function provideAllValuesValues() + { + return array( + array('http://www.example.com', array('foo_nothing', 'foo_domain')), + array('http://www.example.com/', array('foo_nothing', 'foo_domain')), + array('http://foo.example.com/', array('foo_nothing', 'foo_domain')), + array('http://foo.example1.com/', array('foo_nothing')), + array('https://foo.example.com/', array('foo_nothing', 'foo_secure', 'foo_domain')), + array('http://www.example.com/foo/bar', array('foo_nothing', 'foo_path', 'foo_domain')), + array('http://www4.example.com/', array('foo_nothing', 'foo_domain', 'foo_strict_domain')), + ); + } + + public function testEncodedValues() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true)); + + $this->assertEquals(array('foo' => 'bar=baz'), $cookieJar->allValues('/')); + $this->assertEquals(array('foo' => 'bar%3Dbaz'), $cookieJar->allRawValues('/')); + } + + public function testCookieExpireWithSameNameButDifferentPaths() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo')); + $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar')); + $cookieJar->expire('foo', '/foo'); + + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + $this->assertEquals(array(), $cookieJar->allValues('http://example.com/foo')); + $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar')); + } + + public function testCookieExpireWithNullPaths() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/')); + $cookieJar->expire('foo', null); + + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + } + + public function testCookieWithSameNameButDifferentPaths() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo')); + $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar')); + + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://example.com/foo')); + $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar')); + } + + public function testCookieWithSameNameButDifferentDomains() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/', 'foo.example.com')); + $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/', 'bar.example.com')); + + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://foo.example.com/')); + $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://bar.example.com/')); + } + + public function testCookieGetWithSubdomain() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar', null, '/', '.example.com')); + $cookieJar->set($cookie2 = new Cookie('foo1', 'bar', null, '/', 'test.example.com')); + + $this->assertEquals($cookie1, $cookieJar->get('foo','/','foo.example.com')); + $this->assertEquals($cookie1, $cookieJar->get('foo','/','example.com')); + $this->assertEquals($cookie2, $cookieJar->get('foo1','/','test.example.com')); + } + + public function testCookieGetWithSubdirectory() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar', null, '/test', '.example.com')); + $cookieJar->set($cookie2 = new Cookie('foo1', 'bar1', null, '/', '.example.com')); + + $this->assertNull($cookieJar->get('foo','/','.example.com')); + $this->assertNull($cookieJar->get('foo','/bar','.example.com')); + $this->assertEquals($cookie1, $cookieJar->get('foo','/test','example.com')); + $this->assertEquals($cookie2, $cookieJar->get('foo1','/','example.com')); + $this->assertEquals($cookie2, $cookieJar->get('foo1','/bar','example.com')); + } + + public function testCookieWithWildcardDomain() + { + $cookieJar = new CookieJar(); + $cookieJar->set(new Cookie('foo', 'bar', null, '/', '.example.com')); + + $this->assertEquals(array('foo' => 'bar'), $cookieJar->allValues('http://www.example.com')); + $this->assertEmpty($cookieJar->allValues('http://wwwexample.com')); + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieTest.php new file mode 100644 index 0000000..8e3578a --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieTest.php @@ -0,0 +1,179 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\Cookie; + +class CookieTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTestsForToFromString + */ + public function testToFromString($cookie, $url = null) + { + $this->assertEquals($cookie, (string) Cookie::fromString($cookie, $url)); + } + + public function getTestsForToFromString() + { + return array( + array('foo=bar; path=/'), + array('foo=bar; path=/foo'), + array('foo=bar; domain=google.com; path=/'), + array('foo=bar; domain=example.com; path=/; secure', 'https://example.com/'), + array('foo=bar; path=/; httponly'), + array('foo=bar; domain=google.com; path=/foo; secure; httponly', 'https://google.com/'), + array('foo=bar=baz; path=/'), + array('foo=bar%3Dbaz; path=/'), + ); + } + + public function testFromStringIgnoreSecureFlag() + { + $this->assertFalse(Cookie::fromString('foo=bar; secure')->isSecure()); + $this->assertFalse(Cookie::fromString('foo=bar; secure', 'http://example.com/')->isSecure()); + } + + /** + * @dataProvider getExpireCookieStrings + */ + public function testFromStringAcceptsSeveralExpiresDateFormats($cookie) + { + $this->assertEquals(1596185377, Cookie::fromString($cookie)->getExpiresTime()); + } + + public function getExpireCookieStrings() + { + return array( + array('foo=bar; expires=Fri, 31-Jul-2020 08:49:37 GMT'), + array('foo=bar; expires=Fri, 31 Jul 2020 08:49:37 GMT'), + array('foo=bar; expires=Fri, 31-07-2020 08:49:37 GMT'), + array('foo=bar; expires=Fri, 31-07-20 08:49:37 GMT'), + array('foo=bar; expires=Friday, 31-Jul-20 08:49:37 GMT'), + array('foo=bar; expires=Fri Jul 31 08:49:37 2020'), + array('foo=bar; expires=\'Fri Jul 31 08:49:37 2020\''), + array('foo=bar; expires=Friday July 31st 2020, 08:49:37 GMT'), + ); + } + + public function testFromStringWithCapitalization() + { + $this->assertEquals('Foo=Bar; path=/', (string) Cookie::fromString('Foo=Bar')); + $this->assertEquals('foo=bar; expires=Fri, 31 Dec 2010 23:59:59 GMT; path=/', (string) Cookie::fromString('foo=bar; Expires=Fri, 31 Dec 2010 23:59:59 GMT')); + $this->assertEquals('foo=bar; domain=www.example.org; path=/; httponly', (string) Cookie::fromString('foo=bar; DOMAIN=www.example.org; HttpOnly')); + } + + public function testFromStringWithUrl() + { + $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com/')); + $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com')); + $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com?foo')); + $this->assertEquals('foo=bar; domain=www.example.com; path=/foo', (string) Cookie::FromString('foo=bar', 'http://www.example.com/foo/bar')); + $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar; path=/', 'http://www.example.com/foo/bar')); + $this->assertEquals('foo=bar; domain=www.myotherexample.com; path=/', (string) Cookie::FromString('foo=bar; domain=www.myotherexample.com', 'http://www.example.com/')); + } + + public function testFromStringThrowsAnExceptionIfCookieIsNotValid() + { + $this->setExpectedException('InvalidArgumentException'); + Cookie::FromString('foo'); + } + + public function testFromStringThrowsAnExceptionIfCookieDateIsNotValid() + { + $this->setExpectedException('InvalidArgumentException'); + Cookie::FromString('foo=bar; expires=Flursday July 31st 2020, 08:49:37 GMT'); + } + + public function testFromStringThrowsAnExceptionIfUrlIsNotValid() + { + $this->setExpectedException('InvalidArgumentException'); + Cookie::FromString('foo=bar', 'foobar'); + } + + public function testGetName() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertEquals('foo', $cookie->getName(), '->getName() returns the cookie name'); + } + + public function testGetValue() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertEquals('bar', $cookie->getValue(), '->getValue() returns the cookie value'); + + $cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true); // raw value + $this->assertEquals('bar=baz', $cookie->getValue(), '->getValue() returns the urldecoded cookie value'); + } + + public function testGetRawValue() + { + $cookie = new Cookie('foo', 'bar=baz'); // decoded value + $this->assertEquals('bar%3Dbaz', $cookie->getRawValue(), '->getRawValue() returns the urlencoded cookie value'); + $cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true); // raw value + $this->assertEquals('bar%3Dbaz', $cookie->getRawValue(), '->getRawValue() returns the non-urldecoded cookie value'); + } + + public function testGetPath() + { + $cookie = new Cookie('foo', 'bar', 0); + $this->assertEquals('/', $cookie->getPath(), '->getPath() returns / is no path is defined'); + + $cookie = new Cookie('foo', 'bar', 0, '/foo'); + $this->assertEquals('/foo', $cookie->getPath(), '->getPath() returns the cookie path'); + } + + public function testGetDomain() + { + $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com'); + $this->assertEquals('foo.com', $cookie->getDomain(), '->getDomain() returns the cookie domain'); + } + + public function testIsSecure() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertFalse($cookie->isSecure(), '->isSecure() returns false if not defined'); + + $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', true); + $this->assertTrue($cookie->isSecure(), '->isSecure() returns the cookie secure flag'); + } + + public function testIsHttponly() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns false if not defined'); + + $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', false, true); + $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns the cookie httponly flag'); + } + + public function testGetExpiresTime() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertNull($cookie->getExpiresTime(), '->getExpiresTime() returns the expires time'); + + $cookie = new Cookie('foo', 'bar', $time = time() - 86400); + $this->assertEquals($time, $cookie->getExpiresTime(), '->getExpiresTime() returns the expires time'); + } + + public function testIsExpired() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertFalse($cookie->isExpired(), '->isExpired() returns false when the cookie never expires (null as expires time)'); + + $cookie = new Cookie('foo', 'bar', time() - 86400); + $this->assertTrue($cookie->isExpired(), '->isExpired() returns true when the cookie is expired'); + + $cookie = new Cookie('foo', 'bar', 0); + $this->assertFalse($cookie->isExpired()); + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/HistoryTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/HistoryTest.php new file mode 100644 index 0000000..882b730 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/HistoryTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\History; +use Symfony\Component\BrowserKit\Request; + +class HistoryTest extends \PHPUnit_Framework_TestCase +{ + public function testAdd() + { + $history = new History(); + $history->add(new Request('http://www.example1.com/', 'get')); + $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->add() adds a request to the history'); + + $history->add(new Request('http://www.example2.com/', 'get')); + $this->assertSame('http://www.example2.com/', $history->current()->getUri(), '->add() adds a request to the history'); + + $history->add(new Request('http://www.example3.com/', 'get')); + $history->back(); + $history->add(new Request('http://www.example4.com/', 'get')); + $this->assertSame('http://www.example4.com/', $history->current()->getUri(), '->add() adds a request to the history'); + + $history->back(); + $this->assertSame('http://www.example2.com/', $history->current()->getUri(), '->add() adds a request to the history'); + } + + public function testClearIsEmpty() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + + $this->assertFalse($history->isEmpty(), '->isEmpty() returns false if the history is not empty'); + + $history->clear(); + + $this->assertTrue($history->isEmpty(), '->isEmpty() true if the history is empty'); + } + + public function testCurrent() + { + $history = new History(); + + try { + $history->current(); + $this->fail('->current() throws a \LogicException if the history is empty'); + } catch (\Exception $e) { + $this->assertInstanceof('LogicException', $e, '->current() throws a \LogicException if the history is empty'); + } + + $history->add(new Request('http://www.example.com/', 'get')); + + $this->assertSame('http://www.example.com/', $history->current()->getUri(), '->current() returns the current request in the history'); + } + + public function testBack() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + + try { + $history->back(); + $this->fail('->back() throws a \LogicException if the history is already on the first page'); + } catch (\Exception $e) { + $this->assertInstanceof('LogicException', $e, '->current() throws a \LogicException if the history is already on the first page'); + } + + $history->add(new Request('http://www.example1.com/', 'get')); + $history->back(); + + $this->assertSame('http://www.example.com/', $history->current()->getUri(), '->back() returns the previous request in the history'); + } + + public function testForward() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + $history->add(new Request('http://www.example1.com/', 'get')); + + try { + $history->forward(); + $this->fail('->forward() throws a \LogicException if the history is already on the last page'); + } catch (\Exception $e) { + $this->assertInstanceof('LogicException', $e, '->forward() throws a \LogicException if the history is already on the last page'); + } + + $history->back(); + $history->forward(); + + $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->forward() returns the next request in the history'); + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/RequestTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/RequestTest.php new file mode 100644 index 0000000..b75b5fb --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/RequestTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\Request; + +class RequestTest extends \PHPUnit_Framework_TestCase +{ + public function testGetUri() + { + $request = new Request('http://www.example.com/', 'get'); + $this->assertEquals('http://www.example.com/', $request->getUri(), '->getUri() returns the URI of the request'); + } + + public function testGetMethod() + { + $request = new Request('http://www.example.com/', 'get'); + $this->assertEquals('get', $request->getMethod(), '->getMethod() returns the method of the request'); + } + + public function testGetParameters() + { + $request = new Request('http://www.example.com/', 'get', array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getParameters(), '->getParameters() returns the parameters of the request'); + } + + public function testGetFiles() + { + $request = new Request('http://www.example.com/', 'get', array(), array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getFiles(), '->getFiles() returns the uploaded files of the request'); + } + + public function testGetCookies() + { + $request = new Request('http://www.example.com/', 'get', array(), array(), array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getCookies(), '->getCookies() returns the cookies of the request'); + } + + public function testGetServer() + { + $request = new Request('http://www.example.com/', 'get', array(), array(), array(), array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getServer(), '->getServer() returns the server parameters of the request'); + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ResponseTest.php b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ResponseTest.php new file mode 100644 index 0000000..bfe3cd5 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ResponseTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\Response; + +class ResponseTest extends \PHPUnit_Framework_TestCase +{ + public function testGetUri() + { + $response = new Response('foo'); + $this->assertEquals('foo', $response->getContent(), '->getContent() returns the content of the response'); + } + + public function testGetStatus() + { + $response = new Response('foo', 304); + $this->assertEquals('304', $response->getStatus(), '->getStatus() returns the status of the response'); + } + + public function testGetHeaders() + { + $response = new Response('foo', 200, array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $response->getHeaders(), '->getHeaders() returns the headers of the response'); + } + + public function testGetHeader() + { + $response = new Response('foo', 200, array( + 'Content-Type' => 'text/html', + 'Set-Cookie' => array('foo=bar', 'bar=foo'), + )); + + $this->assertEquals('text/html', $response->getHeader('Content-Type'), '->getHeader() returns a header of the response'); + $this->assertEquals('text/html', $response->getHeader('content-type'), '->getHeader() returns a header of the response'); + $this->assertEquals('text/html', $response->getHeader('content_type'), '->getHeader() returns a header of the response'); + $this->assertEquals('foo=bar', $response->getHeader('Set-Cookie'), '->getHeader() returns the first header value'); + $this->assertEquals(array('foo=bar', 'bar=foo'), $response->getHeader('Set-Cookie', false), '->getHeader() returns all header values if first is false'); + + $this->assertNull($response->getHeader('foo'), '->getHeader() returns null if the header is not defined'); + $this->assertEquals(array(), $response->getHeader('foo', false), '->getHeader() returns an empty array if the header is not defined and first is set to false'); + } + + public function testMagicToString() + { + $response = new Response('foo', 304, array('foo' => 'bar')); + + $this->assertEquals("foo: bar\n\nfoo", $response->__toString(), '->__toString() returns the headers and the content as a string'); + } + + public function testMagicToStringWithMultipleSetCookieHeader() + { + $headers = array( + 'content-type' => 'text/html; charset=utf-8', + 'set-cookie' => array('foo=bar', 'bar=foo'), + ); + + $expected = 'content-type: text/html; charset=utf-8'."\n"; + $expected .= 'set-cookie: foo=bar'."\n"; + $expected .= 'set-cookie: bar=foo'."\n\n"; + $expected .= 'foo'; + + $response = new Response('foo', 304, $headers); + + $this->assertEquals($expected, $response->__toString(), '->__toString() returns the headers and the content as a string'); + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/composer.json b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/composer.json new file mode 100644 index 0000000..ed914dd --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/composer.json @@ -0,0 +1,39 @@ +{ + "name": "symfony/browser-kit", + "type": "library", + "description": "Symfony BrowserKit Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/dom-crawler": "~2.0" + }, + "require-dev": { + "symfony/process": "~2.0", + "symfony/css-selector": "~2.0" + }, + "suggest": { + "symfony/process": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\BrowserKit\\": "" } + }, + "target-dir": "Symfony/Component/BrowserKit", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + } +} diff --git a/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/phpunit.xml.dist b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/phpunit.xml.dist new file mode 100644 index 0000000..51a0f31 --- /dev/null +++ b/core/vendor/symfony/browser-kit/Symfony/Component/BrowserKit/phpunit.xml.dist @@ -0,0 +1,25 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/.gitignore b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/CHANGELOG.md b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/CHANGELOG.md new file mode 100644 index 0000000..48fd323 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/CHANGELOG.md @@ -0,0 +1,45 @@ +CHANGELOG +========= + +2.5.0 +----- + +* [BC BREAK] The default value for checkbox and radio inputs without a value attribute have changed + from '1' to 'on' to match the HTML specification. +* [BC BREAK] The typehints on the `Link`, `Form` and `FormField` classes have been changed from + `\DOMNode` to `DOMElement`. Using any other type of `DOMNode` was triggering fatal errors in previous + versions. Code extending these classes will need to update the typehints when overwriting these methods. + +2.4.0 +----- + + * `Crawler::addXmlContent()` removes the default document namespace again if it's an only namespace. + * added support for automatic discovery and explicit registration of document + namespaces for `Crawler::filterXPath()` and `Crawler::filter()` + * improved content type guessing in `Crawler::addContent()` + * [BC BREAK] `Crawler::addXmlContent()` no longer removes the default document + namespace + +2.3.0 +----- + + * added Crawler::html() + * [BC BREAK] Crawler::each() and Crawler::reduce() now return Crawler instances instead of DomElement instances + * added schema relative URL support to links + * added support for HTML5 'form' attribute + +2.2.0 +----- + + * added a way to set raw path to the file in FileFormField - necessary for + simulating HTTP requests + +2.1.0 +----- + + * added support for the HTTP PATCH method + * refactored the Form class internals to support multi-dimensional fields + (the public API is backward compatible) + * added a way to get parsing errors for Crawler::addHtmlContent() and + Crawler::addXmlContent() via libxml functions + * added support for submitting a form without a submit button diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php new file mode 100644 index 0000000..19eb9c6 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php @@ -0,0 +1,1000 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\CssSelector\CssSelector; + +/** + * Crawler eases navigation of a list of \DOMElement objects. + * + * @author Fabien Potencier + * + * @api + */ +class Crawler extends \SplObjectStorage +{ + /** + * @var string The current URI or the base href value + */ + protected $uri; + + /** + * @var string The default namespace prefix to be used with XPath and CSS expressions + */ + private $defaultNamespacePrefix = 'default'; + + /** + * @var array A map of manually registered namespaces + */ + private $namespaces = array(); + + /** + * Constructor. + * + * @param mixed $node A Node to use as the base for the crawling + * @param string $uri The current URI or the base href value + * + * @api + */ + public function __construct($node = null, $uri = null) + { + $this->uri = $uri; + + $this->add($node); + } + + /** + * Removes all the nodes. + * + * @api + */ + public function clear() + { + $this->removeAll($this); + } + + /** + * Adds a node to the current list of nodes. + * + * This method uses the appropriate specialized add*() method based + * on the type of the argument. + * + * @param \DOMNodeList|\DOMNode|array|string|null $node A node + * + * @throws \InvalidArgumentException When node is not the expected type. + * + * @api + */ + public function add($node) + { + if ($node instanceof \DOMNodeList) { + $this->addNodeList($node); + } elseif ($node instanceof \DOMNode) { + $this->addNode($node); + } elseif (is_array($node)) { + $this->addNodes($node); + } elseif (is_string($node)) { + $this->addContent($node); + } elseif (null !== $node) { + throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', is_object($node) ? get_class($node) : gettype($node))); + } + } + + /** + * Adds HTML/XML content. + * + * If the charset is not set via the content type, it is assumed + * to be ISO-8859-1, which is the default charset defined by the + * HTTP 1.1 specification. + * + * @param string $content A string to parse as HTML/XML + * @param null|string $type The content type of the string + */ + public function addContent($content, $type = null) + { + if (empty($type)) { + $type = 0 === strpos($content, ']+charset *= *["\']?([a-zA-Z\-0-9_:.]+)/i', $content, $matches)) { + $charset = $matches[1]; + } + + if (null === $charset) { + $charset = 'ISO-8859-1'; + } + + if ('x' === $xmlMatches[1]) { + $this->addXmlContent($content, $charset); + } else { + $this->addHtmlContent($content, $charset); + } + } + + /** + * Adds an HTML content to the list of nodes. + * + * The libxml errors are disabled when the content is parsed. + * + * If you want to get parsing errors, be sure to enable + * internal errors via libxml_use_internal_errors(true) + * and then, get the errors via libxml_get_errors(). Be + * sure to clear errors with libxml_clear_errors() afterward. + * + * @param string $content The HTML content + * @param string $charset The charset + * + * @api + */ + public function addHtmlContent($content, $charset = 'UTF-8') + { + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + + $dom = new \DOMDocument('1.0', $charset); + $dom->validateOnParse = true; + + if (function_exists('mb_convert_encoding')) { + $hasError = false; + set_error_handler(function () use (&$hasError) { + $hasError = true; + }); + $tmpContent = @mb_convert_encoding($content, 'HTML-ENTITIES', $charset); + + restore_error_handler(); + + if (!$hasError) { + $content = $tmpContent; + } + } + + if ('' !== trim($content)) { + @$dom->loadHTML($content); + } + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + $this->addDocument($dom); + + $base = $this->filterRelativeXPath('descendant-or-self::base')->extract(array('href')); + + $baseHref = current($base); + if (count($base) && !empty($baseHref)) { + if ($this->uri) { + $linkNode = $dom->createElement('a'); + $linkNode->setAttribute('href', $baseHref); + $link = new Link($linkNode, $this->uri); + $this->uri = $link->getUri(); + } else { + $this->uri = $baseHref; + } + } + } + + /** + * Adds an XML content to the list of nodes. + * + * The libxml errors are disabled when the content is parsed. + * + * If you want to get parsing errors, be sure to enable + * internal errors via libxml_use_internal_errors(true) + * and then, get the errors via libxml_get_errors(). Be + * sure to clear errors with libxml_clear_errors() afterward. + * + * @param string $content The XML content + * @param string $charset The charset + * + * @api + */ + public function addXmlContent($content, $charset = 'UTF-8') + { + // remove the default namespace if it's the only namespace to make XPath expressions simpler + if (!preg_match('/xmlns:/', $content)) { + $content = str_replace('xmlns', 'ns', $content); + } + + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + + $dom = new \DOMDocument('1.0', $charset); + $dom->validateOnParse = true; + + if ('' !== trim($content)) { + @$dom->loadXML($content, LIBXML_NONET); + } + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + $this->addDocument($dom); + } + + /** + * Adds a \DOMDocument to the list of nodes. + * + * @param \DOMDocument $dom A \DOMDocument instance + * + * @api + */ + public function addDocument(\DOMDocument $dom) + { + if ($dom->documentElement) { + $this->addNode($dom->documentElement); + } + } + + /** + * Adds a \DOMNodeList to the list of nodes. + * + * @param \DOMNodeList $nodes A \DOMNodeList instance + * + * @api + */ + public function addNodeList(\DOMNodeList $nodes) + { + foreach ($nodes as $node) { + if ($node instanceof \DOMNode) { + $this->addNode($node); + } + } + } + + /** + * Adds an array of \DOMNode instances to the list of nodes. + * + * @param \DOMNode[] $nodes An array of \DOMNode instances + * + * @api + */ + public function addNodes(array $nodes) + { + foreach ($nodes as $node) { + $this->add($node); + } + } + + /** + * Adds a \DOMNode instance to the list of nodes. + * + * @param \DOMNode $node A \DOMNode instance + * + * @api + */ + public function addNode(\DOMNode $node) + { + if ($node instanceof \DOMDocument) { + $this->attach($node->documentElement); + } else { + $this->attach($node); + } + } + + /** + * Returns a node given its position in the node list. + * + * @param int $position The position + * + * @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist. + * + * @api + */ + public function eq($position) + { + foreach ($this as $i => $node) { + if ($i == $position) { + return new static($node, $this->uri); + } + } + + return new static(null, $this->uri); + } + + /** + * Calls an anonymous function on each node of the list. + * + * The anonymous function receives the position and the node wrapped + * in a Crawler instance as arguments. + * + * Example: + * + * $crawler->filter('h1')->each(function ($node, $i) { + * return $node->text(); + * }); + * + * @param \Closure $closure An anonymous function + * + * @return array An array of values returned by the anonymous function + * + * @api + */ + public function each(\Closure $closure) + { + $data = array(); + foreach ($this as $i => $node) { + $data[] = $closure(new static($node, $this->uri), $i); + } + + return $data; + } + + /** + * Reduces the list of nodes by calling an anonymous function. + * + * To remove a node from the list, the anonymous function must return false. + * + * @param \Closure $closure An anonymous function + * + * @return Crawler A Crawler instance with the selected nodes. + * + * @api + */ + public function reduce(\Closure $closure) + { + $nodes = array(); + foreach ($this as $i => $node) { + if (false !== $closure(new static($node, $this->uri), $i)) { + $nodes[] = $node; + } + } + + return new static($nodes, $this->uri); + } + + /** + * Returns the first node of the current selection + * + * @return Crawler A Crawler instance with the first selected node + * + * @api + */ + public function first() + { + return $this->eq(0); + } + + /** + * Returns the last node of the current selection + * + * @return Crawler A Crawler instance with the last selected node + * + * @api + */ + public function last() + { + return $this->eq(count($this) - 1); + } + + /** + * Returns the siblings nodes of the current selection + * + * @return Crawler A Crawler instance with the sibling nodes + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function siblings() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return new static($this->sibling($this->getNode(0)->parentNode->firstChild), $this->uri); + } + + /** + * Returns the next siblings nodes of the current selection + * + * @return Crawler A Crawler instance with the next sibling nodes + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function nextAll() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return new static($this->sibling($this->getNode(0)), $this->uri); + } + + /** + * Returns the previous sibling nodes of the current selection + * + * @return Crawler A Crawler instance with the previous sibling nodes + * + * @throws \InvalidArgumentException + * + * @api + */ + public function previousAll() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return new static($this->sibling($this->getNode(0), 'previousSibling'), $this->uri); + } + + /** + * Returns the parents nodes of the current selection + * + * @return Crawler A Crawler instance with the parents nodes of the current selection + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function parents() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + $nodes = array(); + + while ($node = $node->parentNode) { + if (1 === $node->nodeType) { + $nodes[] = $node; + } + } + + return new static($nodes, $this->uri); + } + + /** + * Returns the children nodes of the current selection + * + * @return Crawler A Crawler instance with the children nodes + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function children() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0)->firstChild; + + return new static($node ? $this->sibling($node) : array(), $this->uri); + } + + /** + * Returns the attribute value of the first node of the list. + * + * @param string $attribute The attribute name + * + * @return string|null The attribute value or null if the attribute does not exist + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function attr($attribute) + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : null; + } + + /** + * Returns the node value of the first node of the list. + * + * @return string The node value + * + * @throws \InvalidArgumentException When current node is empty + * + * @api + */ + public function text() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->getNode(0)->nodeValue; + } + + /** + * Returns the first node of the list as HTML. + * + * @return string The node html + * + * @throws \InvalidArgumentException When current node is empty + */ + public function html() + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $html = ''; + foreach ($this->getNode(0)->childNodes as $child) { + if (PHP_VERSION_ID >= 50306) { + // node parameter was added to the saveHTML() method in PHP 5.3.6 + // @see http://php.net/manual/en/domdocument.savehtml.php + $html .= $child->ownerDocument->saveHTML($child); + } else { + $document = new \DOMDocument('1.0', 'UTF-8'); + $document->appendChild($document->importNode($child, true)); + $html .= rtrim($document->saveHTML()); + } + } + + return $html; + } + + /** + * Extracts information from the list of nodes. + * + * You can extract attributes or/and the node value (_text). + * + * Example: + * + * $crawler->filter('h1 a')->extract(array('_text', 'href')); + * + * @param array $attributes An array of attributes + * + * @return array An array of extracted values + * + * @api + */ + public function extract($attributes) + { + $attributes = (array) $attributes; + $count = count($attributes); + + $data = array(); + foreach ($this as $node) { + $elements = array(); + foreach ($attributes as $attribute) { + if ('_text' === $attribute) { + $elements[] = $node->nodeValue; + } else { + $elements[] = $node->getAttribute($attribute); + } + } + + $data[] = $count > 1 ? $elements : $elements[0]; + } + + return $data; + } + + /** + * Filters the list of nodes with an XPath expression. + * + * The XPath expression is evaluated in the context of the crawler, which + * is considered as a fake parent of the elements inside it. + * This means that a child selector "div" or "./div" will match only + * the div elements of the current crawler, not their children. + * + * @param string $xpath An XPath expression + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + * + * @api + */ + public function filterXPath($xpath) + { + $xpath = $this->relativize($xpath); + + // If we dropped all expressions in the XPath while preparing it, there would be no match + if ('' === $xpath) { + return new static(null, $this->uri); + } + + return $this->filterRelativeXPath($xpath); + } + + /** + * Filters the list of nodes with a CSS selector. + * + * This method only works if you have installed the CssSelector Symfony Component. + * + * @param string $selector A CSS selector + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + * + * @throws \RuntimeException if the CssSelector Component is not available + * + * @api + */ + public function filter($selector) + { + if (!class_exists('Symfony\\Component\\CssSelector\\CssSelector')) { + throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector is not installed (you can use filterXPath instead).'); + } + + // The CssSelector already prefixes the selector with descendant-or-self:: + return $this->filterRelativeXPath(CssSelector::toXPath($selector)); + } + + /** + * Selects links by name or alt value for clickable images. + * + * @param string $value The link text + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + * + * @api + */ + public function selectLink($value) + { + $xpath = sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) ', static::xpathLiteral(' '.$value.' ')). + sprintf('or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]]', static::xpathLiteral(' '.$value.' ')); + + return $this->filterRelativeXPath($xpath); + } + + /** + * Selects a button by name or alt value for images. + * + * @param string $value The button text + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + * + * @api + */ + public function selectButton($value) + { + $translate = 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")'; + $xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, $translate, static::xpathLiteral(' '.$value.' ')). + sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id=%s or @name=%s] ', $translate, static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)). + sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id=%s or @name=%s]', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)); + + return $this->filterRelativeXPath($xpath); + } + + /** + * Returns a Link object for the first node in the list. + * + * @param string $method The method for the link (get by default) + * + * @return Link A Link instance + * + * @throws \InvalidArgumentException If the current node list is empty + * + * @api + */ + public function link($method = 'get') + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + return new Link($node, $this->uri, $method); + } + + /** + * Returns an array of Link objects for the nodes in the list. + * + * @return Link[] An array of Link instances + * + * @api + */ + public function links() + { + $links = array(); + foreach ($this as $node) { + $links[] = new Link($node, $this->uri, 'get'); + } + + return $links; + } + + /** + * Returns a Form object for the first node in the list. + * + * @param array $values An array of values for the form fields + * @param string $method The method for the form + * + * @return Form A Form instance + * + * @throws \InvalidArgumentException If the current node list is empty + * + * @api + */ + public function form(array $values = null, $method = null) + { + if (!count($this)) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $form = new Form($this->getNode(0), $this->uri, $method); + + if (null !== $values) { + $form->setValues($values); + } + + return $form; + } + + /** + * Overloads a default namespace prefix to be used with XPath and CSS expressions. + * + * @param string $prefix + */ + public function setDefaultNamespacePrefix($prefix) + { + $this->defaultNamespacePrefix = $prefix; + } + + /** + * @param string $prefix + * @param string $namespace + */ + public function registerNamespace($prefix, $namespace) + { + $this->namespaces[$prefix] = $namespace; + } + + /** + * Converts string for XPath expressions. + * + * Escaped characters are: quotes (") and apostrophe ('). + * + * Examples: + * + * echo Crawler::xpathLiteral('foo " bar'); + * //prints 'foo " bar' + * + * echo Crawler::xpathLiteral("foo ' bar"); + * //prints "foo ' bar" + * + * echo Crawler::xpathLiteral('a\'b"c'); + * //prints concat('a', "'", 'b"c') + * + * + * @param string $s String to be escaped + * + * @return string Converted string + */ + public static function xpathLiteral($s) + { + if (false === strpos($s, "'")) { + return sprintf("'%s'", $s); + } + + if (false === strpos($s, '"')) { + return sprintf('"%s"', $s); + } + + $string = $s; + $parts = array(); + while (true) { + if (false !== $pos = strpos($string, "'")) { + $parts[] = sprintf("'%s'", substr($string, 0, $pos)); + $parts[] = "\"'\""; + $string = substr($string, $pos + 1); + } else { + $parts[] = "'$string'"; + break; + } + } + + return sprintf("concat(%s)", implode($parts, ', ')); + } + + /** + * Filters the list of nodes with an XPath expression. + * + * The XPath expression should already be processed to apply it in the context of each node. + * + * @param string $xpath + * + * @return Crawler + */ + private function filterRelativeXPath($xpath) + { + $prefixes = $this->findNamespacePrefixes($xpath); + + $crawler = new static(null, $this->uri); + + foreach ($this as $node) { + $domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes); + $crawler->add($domxpath->query($xpath, $node)); + } + + return $crawler; + } + + /** + * Make the XPath relative to the current context. + * + * The returned XPath will match elements matching the XPath inside the current crawler + * when running in the context of a node of the crawler. + * + * @param string $xpath + * + * @return string + */ + private function relativize($xpath) + { + $expressions = array(); + + $unionPattern = '/\|(?![^\[]*\])/'; + // An expression which will never match to replace expressions which cannot match in the crawler + // We cannot simply drop + $nonMatchingExpression = 'a[name() = "b"]'; + + // Split any unions into individual expressions. + foreach (preg_split($unionPattern, $xpath) as $expression) { + $expression = trim($expression); + $parenthesis = ''; + + // If the union is inside some braces, we need to preserve the opening braces and apply + // the change only inside it. + if (preg_match('/^[\(\s*]+/', $expression, $matches)) { + $parenthesis = $matches[0]; + $expression = substr($expression, strlen($parenthesis)); + } + + // BC for Symfony 2.4 and lower were elements were adding in a fake _root parent + if (0 === strpos($expression, '/_root/')) { + $expression = './'.substr($expression, 7); + } elseif (0 === strpos($expression, 'self::*/')) { + $expression = './'.substr($expression, 8); + } + + // add prefix before absolute element selector + if (empty($expression)) { + $expression = $nonMatchingExpression; + } elseif (0 === strpos($expression, '//')) { + $expression = 'descendant-or-self::'.substr($expression, 2); + } elseif (0 === strpos($expression, './/')) { + $expression = 'descendant-or-self::'.substr($expression, 3); + } elseif (0 === strpos($expression, './')) { + $expression = 'self::'.substr($expression, 2); + } elseif (0 === strpos($expression, 'child::')) { + $expression = 'self::'.substr($expression, 7); + } elseif ('/' === $expression[0] || 0 === strpos($expression, 'self::')) { + // the only direct child in Symfony 2.4 and lower is _root, which is already handled previously + // so let's drop the expression entirely + $expression = $nonMatchingExpression; + } elseif ('.' === $expression[0]) { + // '.' is the fake root element in Symfony 2.4 and lower, which is excluded from results + $expression = $nonMatchingExpression; + } elseif (0 === strpos($expression, 'descendant::')) { + $expression = 'descendant-or-self::'.substr($expression, strlen('descendant::')); + } elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) { + // the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes) + $expression = $nonMatchingExpression; + } elseif (0 !== strpos($expression, 'descendant-or-self::')) { + $expression = 'self::'.$expression; + } + $expressions[] = $parenthesis.$expression; + } + + return implode(' | ', $expressions); + } + + /** + * @param int $position + * + * @return \DOMElement|null + */ + public function getNode($position) + { + foreach ($this as $i => $node) { + if ($i == $position) { + return $node; + } + } + } + + /** + * @param \DOMElement $node + * @param string $siblingDir + * + * @return array + */ + protected function sibling($node, $siblingDir = 'nextSibling') + { + $nodes = array(); + + do { + if ($node !== $this->getNode(0) && $node->nodeType === 1) { + $nodes[] = $node; + } + } while ($node = $node->$siblingDir); + + return $nodes; + } + + /** + * @param \DOMDocument $document + * @param array $prefixes + * + * @return \DOMXPath + * + * @throws \InvalidArgumentException + */ + private function createDOMXPath(\DOMDocument $document, array $prefixes = array()) + { + $domxpath = new \DOMXPath($document); + + foreach ($prefixes as $prefix) { + $namespace = $this->discoverNamespace($domxpath, $prefix); + if (null !== $namespace) { + $domxpath->registerNamespace($prefix, $namespace); + } + } + + return $domxpath; + } + + /** + * @param \DOMXPath $domxpath + * @param string $prefix + * + * @return string + * + * @throws \InvalidArgumentException + */ + private function discoverNamespace(\DOMXPath $domxpath, $prefix) + { + if (isset($this->namespaces[$prefix])) { + return $this->namespaces[$prefix]; + } + + // ask for one namespace, otherwise we'd get a collection with an item for each node + $namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix)); + + if ($node = $namespaces->item(0)) { + return $node->nodeValue; + } + } + + /** + * @param $xpath + * + * @return array + */ + private function findNamespacePrefixes($xpath) + { + if (preg_match_all('/(?P[a-z_][a-z_0-9\-\.]*):[^"\/]/i', $xpath, $matches)) { + return array_unique($matches['prefix']); + } + + return array(); + } +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/ChoiceFormField.php new file mode 100644 index 0000000..56e74dc --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -0,0 +1,327 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * ChoiceFormField represents a choice form field. + * + * It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs. + * + * @author Fabien Potencier + * + * @api + */ +class ChoiceFormField extends FormField +{ + /** + * @var string + */ + private $type; + /** + * @var bool + */ + private $multiple; + /** + * @var array + */ + private $options; + /** + * @var bool + */ + private $validationDisabled = false; + + /** + * Returns true if the field should be included in the submitted values. + * + * @return bool true if the field should be included in the submitted values, false otherwise + */ + public function hasValue() + { + // don't send a value for unchecked checkboxes + if (in_array($this->type, array('checkbox', 'radio')) && null === $this->value) { + return false; + } + + return true; + } + + /** + * Check if the current selected option is disabled + * + * @return bool + */ + public function isDisabled() + { + foreach ($this->options as $option) { + if ($option['value'] == $this->value && $option['disabled']) { + return true; + } + } + + return false; + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + * + * @api + */ + public function select($value) + { + $this->setValue($value); + } + + /** + * Ticks a checkbox. + * + * @throws \LogicException When the type provided is not correct + * + * @api + */ + public function tick() + { + if ('checkbox' !== $this->type) { + throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); + } + + $this->setValue(true); + } + + /** + * Ticks a checkbox. + * + * @throws \LogicException When the type provided is not correct + * + * @api + */ + public function untick() + { + if ('checkbox' !== $this->type) { + throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); + } + + $this->setValue(false); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + * + * @throws \InvalidArgumentException When value type provided is not correct + */ + public function setValue($value) + { + if ('checkbox' === $this->type && false === $value) { + // uncheck + $this->value = null; + } elseif ('checkbox' === $this->type && true === $value) { + // check + $this->value = $this->options[0]['value']; + } else { + if (is_array($value)) { + if (!$this->multiple) { + throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name)); + } + + foreach ($value as $v) { + if (!$this->containsOption($v, $this->options)) { + throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues()))); + } + } + } elseif (!$this->containsOption($value, $this->options)) { + throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues()))); + } + + if ($this->multiple) { + $value = (array) $value; + } + + if (is_array($value)) { + $this->value = $value; + } else { + parent::setValue($value); + } + } + } + + /** + * Adds a choice to the current ones. + * + * This method should only be used internally. + * + * @param \DOMElement $node + * + * @throws \LogicException When choice provided is not multiple nor radio + */ + public function addChoice(\DOMElement $node) + { + if (!$this->multiple && 'radio' !== $this->type) { + throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name)); + } + + $option = $this->buildOptionValue($node); + $this->options[] = $option; + + if ($node->hasAttribute('checked')) { + $this->value = $option['value']; + } + } + + /** + * Returns the type of the choice field (radio, select, or checkbox). + * + * @return string The type + */ + public function getType() + { + return $this->type; + } + + /** + * Returns true if the field accepts multiple values. + * + * @return bool true if the field accepts multiple values, false otherwise + */ + public function isMultiple() + { + return $this->multiple; + } + + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName)); + } + + if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) { + throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type'))); + } + + $this->value = null; + $this->options = array(); + $this->multiple = false; + + if ('input' == $this->node->nodeName) { + $this->type = strtolower($this->node->getAttribute('type')); + $optionValue = $this->buildOptionValue($this->node); + $this->options[] = $optionValue; + + if ($this->node->hasAttribute('checked')) { + $this->value = $optionValue['value']; + } + } else { + $this->type = 'select'; + if ($this->node->hasAttribute('multiple')) { + $this->multiple = true; + $this->value = array(); + $this->name = str_replace('[]', '', $this->name); + } + + $found = false; + foreach ($this->xpath->query('descendant::option', $this->node) as $option) { + $optionValue = $this->buildOptionValue($option); + $this->options[] = $optionValue; + + if ($option->hasAttribute('selected')) { + $found = true; + if ($this->multiple) { + $this->value[] = $optionValue['value']; + } else { + $this->value = $optionValue['value']; + } + } + } + + // if no option is selected and if it is a simple select box, take the first option as the value + if (!$found && !$this->multiple && !empty($this->options)) { + $this->value = $this->options[0]['value']; + } + } + } + + /** + * Returns option value with associated disabled flag + * + * @param \DOMElement $node + * + * @return array + */ + private function buildOptionValue(\DOMElement $node) + { + $option = array(); + + $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : 'on'; + $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; + $option['disabled'] = $node->hasAttribute('disabled'); + + return $option; + } + + /** + * Checks whether given value is in the existing options + * + * @param string $optionValue + * @param array $options + * + * @return bool + */ + public function containsOption($optionValue, $options) + { + if ($this->validationDisabled) { + return true; + } + + foreach ($options as $option) { + if ($option['value'] == $optionValue) { + return true; + } + } + + return false; + } + + /** + * Returns list of available field options + * + * @return array + */ + public function availableOptionValues() + { + $values = array(); + + foreach ($this->options as $option) { + $values[] = $option['value']; + } + + return $values; + } + + /** + * Disables the internal validation of the field. + * + * @return self + */ + public function disableValidation() + { + $this->validationDisabled = true; + + return $this; + } +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FileFormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FileFormField.php new file mode 100644 index 0000000..bbdf467 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FileFormField.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * FileFormField represents a file form field (an HTML file input tag). + * + * @author Fabien Potencier + * + * @api + */ +class FileFormField extends FormField +{ + /** + * Sets the PHP error code associated with the field. + * + * @param int $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION) + * + * @throws \InvalidArgumentException When error code doesn't exist + */ + public function setErrorCode($error) + { + $codes = array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION); + if (!in_array($error, $codes)) { + throw new \InvalidArgumentException(sprintf('The error code %s is not valid.', $error)); + } + + $this->value = array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + * + * @api + */ + public function upload($value) + { + $this->setValue($value); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function setValue($value) + { + if (null !== $value && is_readable($value)) { + $error = UPLOAD_ERR_OK; + $size = filesize($value); + $info = pathinfo($value); + $name = $info['basename']; + + // copy to a tmp location + $tmp = sys_get_temp_dir().'/'.sha1(uniqid(mt_rand(), true)); + if (array_key_exists('extension', $info)) { + $tmp .= '.'.$info['extension']; + } + if (is_file($tmp)) { + unlink($tmp); + } + copy($value, $tmp); + $value = $tmp; + } else { + $error = UPLOAD_ERR_NO_FILE; + $size = 0; + $name = ''; + $value = ''; + } + + $this->value = array('name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size); + } + + /** + * Sets path to the file as string for simulating HTTP request + * + * @param string $path The path to the file + */ + public function setFilePath($path) + { + parent::setValue($path); + } + + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName)); + } + + if ('file' !== strtolower($this->node->getAttribute('type'))) { + throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is %s).', $this->node->getAttribute('type'))); + } + + $this->setValue(null); + } +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FormField.php new file mode 100644 index 0000000..785e301 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FormField.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * FormField is the abstract class for all form fields. + * + * @author Fabien Potencier + */ +abstract class FormField +{ + /** + * @var \DOMElement + */ + protected $node; + /** + * @var string + */ + protected $name; + /** + * @var string + */ + protected $value; + /** + * @var \DOMDocument + */ + protected $document; + /** + * @var \DOMXPath + */ + protected $xpath; + /** + * @var bool + */ + protected $disabled; + + /** + * Constructor. + * + * @param \DOMElement $node The node associated with this field + */ + public function __construct(\DOMElement $node) + { + $this->node = $node; + $this->name = $node->getAttribute('name'); + $this->xpath = new \DOMXPath($node->ownerDocument); + + $this->initialize(); + } + + /** + * Returns the name of the field. + * + * @return string The name of the field + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the field. + * + * @return string|array The value of the field + */ + public function getValue() + { + return $this->value; + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + * + * @api + */ + public function setValue($value) + { + $this->value = (string) $value; + } + + /** + * Returns true if the field should be included in the submitted values. + * + * @return bool true if the field should be included in the submitted values, false otherwise + */ + public function hasValue() + { + return true; + } + + /** + * Check if the current field is disabled + * + * @return bool + */ + public function isDisabled() + { + return $this->node->hasAttribute('disabled'); + } + + /** + * Initializes the form field. + */ + abstract protected function initialize(); +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/InputFormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/InputFormField.php new file mode 100644 index 0000000..b9bd0a4 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/InputFormField.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * InputFormField represents an input form field (an HTML input tag). + * + * For inputs with type of file, checkbox, or radio, there are other more + * specialized classes (cf. FileFormField and ChoiceFormField). + * + * @author Fabien Potencier + * + * @api + */ +class InputFormField extends FormField +{ + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName && 'button' !== $this->node->nodeName) { + throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); + } + + if ('checkbox' === strtolower($this->node->getAttribute('type'))) { + throw new \LogicException('Checkboxes should be instances of ChoiceFormField.'); + } + + if ('file' === strtolower($this->node->getAttribute('type'))) { + throw new \LogicException('File inputs should be instances of FileFormField.'); + } + + $this->value = $this->node->getAttribute('value'); + } +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/TextareaFormField.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/TextareaFormField.php new file mode 100644 index 0000000..a14e707 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/TextareaFormField.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * TextareaFormField represents a textarea form field (an HTML textarea tag). + * + * @author Fabien Potencier + * + * @api + */ +class TextareaFormField extends FormField +{ + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('textarea' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName)); + } + + $this->value = ''; + foreach ($this->node->childNodes as $node) { + $this->value .= $node->wholeText; + } + } +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php new file mode 100644 index 0000000..5caea53 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php @@ -0,0 +1,479 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\DomCrawler\Field\ChoiceFormField; +use Symfony\Component\DomCrawler\Field\FormField; + +/** + * Form represents an HTML form. + * + * @author Fabien Potencier + * + * @api + */ +class Form extends Link implements \ArrayAccess +{ + /** + * @var \DOMElement + */ + private $button; + + /** + * @var FormFieldRegistry + */ + private $fields; + + /** + * Constructor. + * + * @param \DOMElement $node A \DOMElement instance + * @param string $currentUri The URI of the page where the form is embedded + * @param string $method The method to use for the link (if null, it defaults to the method defined by the form) + * + * @throws \LogicException if the node is not a button inside a form tag + * + * @api + */ + public function __construct(\DOMElement $node, $currentUri, $method = null) + { + parent::__construct($node, $currentUri, $method); + + $this->initialize(); + } + + /** + * Gets the form node associated with this form. + * + * @return \DOMElement A \DOMElement instance + */ + public function getFormNode() + { + return $this->node; + } + + /** + * Sets the value of the fields. + * + * @param array $values An array of field values + * + * @return Form + * + * @api + */ + public function setValues(array $values) + { + foreach ($values as $name => $value) { + $this->fields->set($name, $value); + } + + return $this; + } + + /** + * Gets the field values. + * + * The returned array does not include file fields (@see getFiles). + * + * @return array An array of field values. + * + * @api + */ + public function getValues() + { + $values = array(); + foreach ($this->fields->all() as $name => $field) { + if ($field->isDisabled()) { + continue; + } + + if (!$field instanceof Field\FileFormField && $field->hasValue()) { + $values[$name] = $field->getValue(); + } + } + + return $values; + } + + /** + * Gets the file field values. + * + * @return array An array of file field values. + * + * @api + */ + public function getFiles() + { + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { + return array(); + } + + $files = array(); + + foreach ($this->fields->all() as $name => $field) { + if ($field->isDisabled()) { + continue; + } + + if ($field instanceof Field\FileFormField) { + $files[$name] = $field->getValue(); + } + } + + return $files; + } + + /** + * Gets the field values as PHP. + * + * This method converts fields with the array notation + * (like foo[bar] to arrays) like PHP does. + * + * @return array An array of field values. + * + * @api + */ + public function getPhpValues() + { + $values = array(); + foreach ($this->getValues() as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + + /** + * Gets the file field values as PHP. + * + * This method converts fields with the array notation + * (like foo[bar] to arrays) like PHP does. + * + * @return array An array of field values. + * + * @api + */ + public function getPhpFiles() + { + $values = array(); + foreach ($this->getFiles() as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + + /** + * Gets the URI of the form. + * + * The returned URI is not the same as the form "action" attribute. + * This method merges the value if the method is GET to mimics + * browser behavior. + * + * @return string The URI + * + * @api + */ + public function getUri() + { + $uri = parent::getUri(); + + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH')) && $queryString = http_build_query($this->getValues(), null, '&')) { + $sep = false === strpos($uri, '?') ? '?' : '&'; + $uri .= $sep.$queryString; + } + + return $uri; + } + + protected function getRawUri() + { + return $this->node->getAttribute('action'); + } + + /** + * Gets the form method. + * + * If no method is defined in the form, GET is returned. + * + * @return string The method + * + * @api + */ + public function getMethod() + { + if (null !== $this->method) { + return $this->method; + } + + return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET'; + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return bool true if the field exists, false otherwise + * + * @api + */ + public function has($name) + { + return $this->fields->has($name); + } + + /** + * Removes a field from the form. + * + * @param string $name The field name + * + * @throws \InvalidArgumentException when the name is malformed + * + * @api + */ + public function remove($name) + { + $this->fields->remove($name); + } + + /** + * Gets a named field. + * + * @param string $name The field name + * + * @return FormField The field instance + * + * @throws \InvalidArgumentException When field is not present in this form + * + * @api + */ + public function get($name) + { + return $this->fields->get($name); + } + + /** + * Sets a named field. + * + * @param FormField $field The field + * + * @api + */ + public function set(FormField $field) + { + $this->fields->add($field); + } + + /** + * Gets all fields. + * + * @return FormField[] An array of fields + * + * @api + */ + public function all() + { + return $this->fields->all(); + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return bool true if the field exists, false otherwise + */ + public function offsetExists($name) + { + return $this->has($name); + } + + /** + * Gets the value of a field. + * + * @param string $name The field name + * + * @return FormField The associated Field instance + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetGet($name) + { + return $this->fields->get($name); + } + + /** + * Sets the value of a field. + * + * @param string $name The field name + * @param string|array $value The value of the field + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetSet($name, $value) + { + $this->fields->set($name, $value); + } + + /** + * Removes a field from the form. + * + * @param string $name The field name + */ + public function offsetUnset($name) + { + $this->fields->remove($name); + } + + /** + * Disables validation + * + * @return self + */ + public function disableValidation() + { + foreach ($this->fields->all() as $field) { + if ($field instanceof Field\ChoiceFormField) { + $field->disableValidation(); + } + } + + return $this; + } + + /** + * Sets the node for the form. + * + * Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself. + * + * @param \DOMElement $node A \DOMElement instance + * + * @throws \LogicException If given node is not a button or input or does not have a form ancestor + */ + protected function setNode(\DOMElement $node) + { + $this->button = $node; + if ('button' === $node->nodeName || ('input' === $node->nodeName && in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image')))) { + if ($node->hasAttribute('form')) { + // if the node has the HTML5-compliant 'form' attribute, use it + $formId = $node->getAttribute('form'); + $form = $node->ownerDocument->getElementById($formId); + if (null === $form) { + throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); + } + $this->node = $form; + + return; + } + // we loop until we find a form ancestor + do { + if (null === $node = $node->parentNode) { + throw new \LogicException('The selected node does not have a form ancestor.'); + } + } while ('form' !== $node->nodeName); + } elseif ('form' !== $node->nodeName) { + throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName)); + } + + $this->node = $node; + } + + /** + * Adds form elements related to this form. + * + * Creates an internal copy of the submitted 'button' element and + * the form node or the entire document depending on whether we need + * to find non-descendant elements through HTML5 'form' attribute. + */ + private function initialize() + { + $this->fields = new FormFieldRegistry(); + + $xpath = new \DOMXPath($this->node->ownerDocument); + + // add submitted button if it has a valid name + if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) { + if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) { + $name = $this->button->getAttribute('name'); + $this->button->setAttribute('value', '0'); + + // temporarily change the name of the input node for the x coordinate + $this->button->setAttribute('name', $name.'.x'); + $this->set(new Field\InputFormField($this->button)); + + // temporarily change the name of the input node for the y coordinate + $this->button->setAttribute('name', $name.'.y'); + $this->set(new Field\InputFormField($this->button)); + + // restore the original name of the input node + $this->button->setAttribute('name', $name); + } else { + $this->set(new Field\InputFormField($this->button)); + } + } + + // find form elements corresponding to the current form + if ($this->node->hasAttribute('id')) { + // corresponding elements are either descendants or have a matching HTML5 form attribute + $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); + + $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId)); + foreach ($fieldNodes as $node) { + $this->addField($node); + } + } else { + // do the xpath query with $this->node as the context node, to only find descendant elements + // however, descendant elements with form attribute are not part of this form + $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node); + foreach ($fieldNodes as $node) { + $this->addField($node); + } + } + } + + private function addField(\DOMElement $node) + { + if (!$node->hasAttribute('name') || !$node->getAttribute('name')) { + return; + } + + $nodeName = $node->nodeName; + if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) { + $this->set(new Field\ChoiceFormField($node)); + } elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) { + // there may be other fields with the same name that are no choice + // fields already registered (see https://github.com/symfony/symfony/issues/11689) + if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) { + $this->get($node->getAttribute('name'))->addChoice($node); + } else { + $this->set(new Field\ChoiceFormField($node)); + } + } elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) { + $this->set(new Field\FileFormField($node)); + } elseif ('input' == $nodeName && !in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image'))) { + $this->set(new Field\InputFormField($node)); + } elseif ('textarea' == $nodeName) { + $this->set(new Field\TextareaFormField($node)); + } + } +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/FormFieldRegistry.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/FormFieldRegistry.php new file mode 100644 index 0000000..39d4774 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/FormFieldRegistry.php @@ -0,0 +1,220 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\DomCrawler\Field\FormField; + +/** + * This is an internal class that must not be used directly. + */ +class FormFieldRegistry +{ + private $fields = array(); + + private $base; + + /** + * Adds a field to the registry. + * + * @param FormField $field The field + * + * @throws \InvalidArgumentException when the name is malformed + */ + public function add(FormField $field) + { + $segments = $this->getSegments($field->getName()); + + $target = & $this->fields; + while ($segments) { + if (!is_array($target)) { + $target = array(); + } + $path = array_shift($segments); + if ('' === $path) { + $target = & $target[]; + } else { + $target = & $target[$path]; + } + } + $target = $field; + } + + /** + * Removes a field and its children from the registry. + * + * @param string $name The fully qualified name of the base field + * + * @throws \InvalidArgumentException when the name is malformed + */ + public function remove($name) + { + $segments = $this->getSegments($name); + $target = & $this->fields; + while (count($segments) > 1) { + $path = array_shift($segments); + if (!array_key_exists($path, $target)) { + return; + } + $target = & $target[$path]; + } + unset($target[array_shift($segments)]); + } + + /** + * Returns the value of the field and its children. + * + * @param string $name The fully qualified name of the field + * + * @return mixed The value of the field + * + * @throws \InvalidArgumentException when the name is malformed + * @throws \InvalidArgumentException if the field does not exist + */ + public function &get($name) + { + $segments = $this->getSegments($name); + $target = & $this->fields; + while ($segments) { + $path = array_shift($segments); + if (!array_key_exists($path, $target)) { + throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path)); + } + $target = & $target[$path]; + } + + return $target; + } + + /** + * Tests whether the form has the given field. + * + * @param string $name The fully qualified name of the field + * + * @return bool Whether the form has the given field + */ + public function has($name) + { + try { + $this->get($name); + + return true; + } catch (\InvalidArgumentException $e) { + return false; + } + } + + /** + * Set the value of a field and its children. + * + * @param string $name The fully qualified name of the field + * @param mixed $value The value + * + * @throws \InvalidArgumentException when the name is malformed + * @throws \InvalidArgumentException if the field does not exist + */ + public function set($name, $value) + { + $target = & $this->get($name); + if (!is_array($value) || $target instanceof Field\ChoiceFormField) { + $target->setValue($value); + } else { + $fields = self::create($name, $value); + foreach ($fields->all() as $k => $v) { + $this->set($k, $v); + } + } + } + + /** + * Returns the list of field with their value. + * + * @return FormField[] The list of fields as array((string) Fully qualified name => (mixed) value) + */ + public function all() + { + return $this->walk($this->fields, $this->base); + } + + /** + * Creates an instance of the class. + * + * This function is made private because it allows overriding the $base and + * the $values properties without any type checking. + * + * @param string $base The fully qualified name of the base field + * @param array $values The values of the fields + * + * @return FormFieldRegistry + */ + private static function create($base, array $values) + { + $registry = new static(); + $registry->base = $base; + $registry->fields = $values; + + return $registry; + } + + /** + * Transforms a PHP array in a list of fully qualified name / value. + * + * @param array $array The PHP array + * @param string $base The name of the base field + * @param array $output The initial values + * + * @return array The list of fields as array((string) Fully qualified name => (mixed) value) + */ + private function walk(array $array, $base = '', array &$output = array()) + { + foreach ($array as $k => $v) { + $path = empty($base) ? $k : sprintf("%s[%s]", $base, $k); + if (is_array($v)) { + $this->walk($v, $path, $output); + } else { + $output[$path] = $v; + } + } + + return $output; + } + + /** + * Splits a field name into segments as a web browser would do. + * + * + * getSegments('base[foo][3][]') = array('base', 'foo, '3', ''); + * + * + * @param string $name The name of the field + * + * @return string[] The list of segments + * + * @throws \InvalidArgumentException when the name is malformed + */ + private function getSegments($name) + { + if (preg_match('/^(?P[^[]+)(?P(\[.*)|$)/', $name, $m)) { + $segments = array($m['base']); + while (!empty($m['extra'])) { + if (preg_match('/^\[(?P.*?)\](?P.*)$/', $m['extra'], $m)) { + $segments[] = $m['segment']; + } else { + throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name)); + } + } + + return $segments; + } + + throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name)); + } +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/LICENSE b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/LICENSE new file mode 100644 index 0000000..0b3292c --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php new file mode 100644 index 0000000..bb2302d --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +/** + * Link represents an HTML link (an HTML a or area tag). + * + * @author Fabien Potencier + * + * @api + */ +class Link +{ + /** + * @var \DOMElement + */ + protected $node; + + /** + * @var string The method to use for the link + */ + protected $method; + + /** + * @var string The URI of the page where the link is embedded (or the base href) + */ + protected $currentUri; + + /** + * Constructor. + * + * @param \DOMElement $node A \DOMElement instance + * @param string $currentUri The URI of the page where the link is embedded (or the base href) + * @param string $method The method to use for the link (get by default) + * + * @throws \InvalidArgumentException if the node is not a link + * + * @api + */ + public function __construct(\DOMElement $node, $currentUri, $method = 'GET') + { + if (!in_array(strtolower(substr($currentUri, 0, 4)), array('http', 'file'))) { + throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri)); + } + + $this->setNode($node); + $this->method = $method ? strtoupper($method) : null; + $this->currentUri = $currentUri; + } + + /** + * Gets the node associated with this link. + * + * @return \DOMElement A \DOMElement instance + */ + public function getNode() + { + return $this->node; + } + + /** + * Gets the method associated with this link. + * + * @return string The method + * + * @api + */ + public function getMethod() + { + return $this->method; + } + + /** + * Gets the URI associated with this link. + * + * @return string The URI + * + * @api + */ + public function getUri() + { + $uri = trim($this->getRawUri()); + + // absolute URL? + if (null !== parse_url($uri, PHP_URL_SCHEME)) { + return $uri; + } + + // empty URI + if (!$uri) { + return $this->currentUri; + } + + // an anchor + if ('#' === $uri[0]) { + return $this->cleanupAnchor($this->currentUri).$uri; + } + + $baseUri = $this->cleanupUri($this->currentUri); + + if ('?' === $uri[0]) { + return $baseUri.$uri; + } + + // absolute URL with relative schema + if (0 === strpos($uri, '//')) { + return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri; + } + + $baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri); + + // absolute path + if ('/' === $uri[0]) { + return $baseUri.$uri; + } + + // relative path + $path = parse_url(substr($this->currentUri, strlen($baseUri)), PHP_URL_PATH); + $path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri); + + return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path; + } + + /** + * Returns raw URI data. + * + * @return string + */ + protected function getRawUri() + { + return $this->node->getAttribute('href'); + } + + /** + * Returns the canonicalized URI path (see RFC 3986, section 5.2.4) + * + * @param string $path URI path + * + * @return string + */ + protected function canonicalizePath($path) + { + if ('' === $path || '/' === $path) { + return $path; + } + + if ('.' === substr($path, -1)) { + $path = $path.'/'; + } + + $output = array(); + + foreach (explode('/', $path) as $segment) { + if ('..' === $segment) { + array_pop($output); + } elseif ('.' !== $segment) { + array_push($output, $segment); + } + } + + return implode('/', $output); + } + + /** + * Sets current \DOMElement instance. + * + * @param \DOMElement $node A \DOMElement instance + * + * @throws \LogicException If given node is not an anchor + */ + protected function setNode(\DOMElement $node) + { + if ('a' !== $node->nodeName && 'area' !== $node->nodeName) { + throw new \LogicException(sprintf('Unable to click on a "%s" tag.', $node->nodeName)); + } + + $this->node = $node; + } + + /** + * Removes the query string and the anchor from the given uri. + * + * @param string $uri The uri to clean + * + * @return string + */ + private function cleanupUri($uri) + { + return $this->cleanupQuery($this->cleanupAnchor($uri)); + } + + /** + * Remove the query string from the uri. + * + * @param string $uri + * + * @return string + */ + private function cleanupQuery($uri) + { + if (false !== $pos = strpos($uri, '?')) { + return substr($uri, 0, $pos); + } + + return $uri; + } + + /** + * Remove the anchor from the uri. + * + * @param string $uri + * + * @return string + */ + private function cleanupAnchor($uri) + { + if (false !== $pos = strpos($uri, '#')) { + return substr($uri, 0, $pos); + } + + return $uri; + } +} diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/README.md b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/README.md new file mode 100644 index 0000000..646573f --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/README.md @@ -0,0 +1,36 @@ +DomCrawler Component +==================== + +DomCrawler eases DOM navigation for HTML and XML documents. + +If you are familiar with jQuery, DomCrawler is a PHP equivalent: + +```php +use Symfony\Component\DomCrawler\Crawler; + +$crawler = new Crawler(); +$crawler->addContent('

Hello World!

'); + +print $crawler->filterXPath('descendant-or-self::body/p')->text(); +``` + +If you are also using the CssSelector component, you can use CSS Selectors +instead of XPath expressions: + +```php +use Symfony\Component\DomCrawler\Crawler; + +$crawler = new Crawler(); +$crawler->addContent('

Hello World!

'); + +print $crawler->filter('body > p')->text(); +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/DomCrawler/ + $ composer.phar install + $ phpunit diff --git a/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/CrawlerTest.php new file mode 100644 index 0000000..b379e36 --- /dev/null +++ b/core/vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -0,0 +1,1025 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Tests; + +use Symfony\Component\CssSelector\CssSelector; +use Symfony\Component\DomCrawler\Crawler; + +class CrawlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $crawler = new Crawler(); + $this->assertCount(0, $crawler, '__construct() returns an empty crawler'); + + $crawler = new Crawler(new \DOMNode()); + $this->assertCount(1, $crawler, '__construct() takes a node as a first argument'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::add + */ + public function testAdd() + { + $crawler = new Crawler(); + $crawler->add($this->createDomDocument()); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMDocument'); + + $crawler = new Crawler(); + $crawler->add($this->createNodeList()); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList'); + + foreach ($this->createNodeList() as $node) { + $list[] = $node; + } + $crawler = new Crawler(); + $crawler->add($list); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from an array of nodes'); + + $crawler = new Crawler(); + $crawler->add($this->createNodeList()->item(0)); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMElement'); + + $crawler = new Crawler(); + $crawler->add('Foo'); + $this->assertEquals('Foo', $crawler->filterXPath('//body')->text(), '->add() adds nodes from a string'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testAddInvalidNode() + { + $crawler = new Crawler(); + $crawler->add(1); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContent() + { + $crawler = new Crawler(); + $crawler->addHtmlContent('
', 'UTF-8'); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addHtmlContent() adds nodes from an HTML string'); + + $crawler->addHtmlContent('', 'UTF-8'); + + $this->assertEquals('http://symfony.com', $crawler->filterXPath('//base')->attr('href'), '->addHtmlContent() adds nodes from an HTML string'); + $this->assertEquals('http://symfony.com/contact', $crawler->filterXPath('//a')->link()->getUri(), '->addHtmlContent() adds nodes from an HTML string'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContentCharset() + { + $crawler = new Crawler(); + $crawler->addHtmlContent('
Tiếng Việt', 'UTF-8'); + + $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContentInvalidBaseTag() + { + $crawler = new Crawler(null, 'http://symfony.com'); + + $crawler->addHtmlContent('', 'UTF-8'); + + $this->assertEquals('http://symfony.com/contact', current($crawler->filterXPath('//a')->links())->getUri(), '->addHtmlContent() correctly handles a non-existent base tag href attribute'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContentUnsupportedCharset() + { + $crawler = new Crawler(); + $crawler->addHtmlContent(file_get_contents(__DIR__.'/Fixtures/windows-1250.html'), 'Windows-1250'); + + $this->assertEquals('ŽťÄýů', $crawler->filterXPath('//p')->text()); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContentCharsetGbk() + { + $crawler = new Crawler(); + //gbk encode of

中文

+ $crawler->addHtmlContent(base64_decode('PGh0bWw+PHA+1tDOxDwvcD48L2h0bWw+'), 'gbk'); + + $this->assertEquals('中文', $crawler->filterXPath('//p')->text()); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent + */ + public function testAddHtmlContentWithErrors() + { + $internalErrors = libxml_use_internal_errors(true); + + $crawler = new Crawler(); + $crawler->addHtmlContent(<< + + + + + + + +EOF + , 'UTF-8'); + + $errors = libxml_get_errors(); + $this->assertCount(1, $errors); + $this->assertEquals("Tag nav invalid\n", $errors[0]->message); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent + */ + public function testAddXmlContent() + { + $crawler = new Crawler(); + $crawler->addXmlContent('
', 'UTF-8'); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addXmlContent() adds nodes from an XML string'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent + */ + public function testAddXmlContentCharset() + { + $crawler = new Crawler(); + $crawler->addXmlContent('
Tiếng Việt
', 'UTF-8'); + + $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent + */ + public function testAddXmlContentWithErrors() + { + $internalErrors = libxml_use_internal_errors(true); + + $crawler = new Crawler(); + $crawler->addXmlContent(<< + + + + +
+ + +EOF + , 'UTF-8'); + + $this->assertTrue(count(libxml_get_errors()) > 1); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addContent + */ + public function testAddContent() + { + $crawler = new Crawler(); + $crawler->addContent('
', 'text/html; charset=UTF-8'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string'); + + $crawler = new Crawler(); + $crawler->addContent('
', 'text/html; charset=UTF-8; dir=RTL'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string with extended content type'); + + $crawler = new Crawler(); + $crawler->addContent('
'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() uses text/html as the default type'); + + $crawler = new Crawler(); + $crawler->addContent('
', 'text/xml; charset=UTF-8'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string'); + + $crawler = new Crawler(); + $crawler->addContent('
', 'text/xml'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string'); + + $crawler = new Crawler(); + $crawler->addContent('foo bar', 'text/plain'); + $this->assertCount(0, $crawler, '->addContent() does nothing if the type is not (x|ht)ml'); + + $crawler = new Crawler(); + $crawler->addContent('中文'); + $this->assertEquals('中文', $crawler->filterXPath('//span')->text(), '->addContent() guess wrong charset'); + + $crawler = new Crawler(); + $crawler->addContent(mb_convert_encoding('日本語', 'SJIS', 'UTF-8')); + $this->assertEquals('日本語', $crawler->filterXPath('//body')->text(), '->addContent() can recognize "Shift_JIS" in html5 meta charset tag'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addDocument + */ + public function testAddDocument() + { + $crawler = new Crawler(); + $crawler->addDocument($this->createDomDocument()); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addDocument() adds nodes from a \DOMDocument'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addNodeList + */ + public function testAddNodeList() + { + $crawler = new Crawler(); + $crawler->addNodeList($this->createNodeList()); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodeList() adds nodes from a \DOMNodeList'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addNodes + */ + public function testAddNodes() + { + foreach ($this->createNodeList() as $node) { + $list[] = $node; + } + + $crawler = new Crawler(); + $crawler->addNodes($list); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodes() adds nodes from an array of nodes'); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::addNode + */ + public function testAddNode() + { + $crawler = new Crawler(); + $crawler->addNode($this->createNodeList()->item(0)); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNode() adds nodes from a \DOMElement'); + } + + public function testClear() + { + $crawler = new Crawler(new \DOMNode()); + $crawler->clear(); + $this->assertCount(0, $crawler, '->clear() removes all the nodes from the crawler'); + } + + public function testEq() + { + $crawler = $this->createTestCrawler()->filterXPath('//li'); + $this->assertNotSame($crawler, $crawler->eq(0), '->eq() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->eq() returns a new instance of a crawler'); + + $this->assertEquals('Two', $crawler->eq(1)->text(), '->eq() returns the nth node of the list'); + $this->assertCount(0, $crawler->eq(100), '->eq() returns an empty crawler if the nth node does not exist'); + } + + public function testEach() + { + $data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each(function ($node, $i) { + return $i.'-'.$node->text(); + }); + + $this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list'); + } + + public function testReduce() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $nodes = $crawler->reduce(function ($node, $i) { + return $i == 1 ? false : true; + }); + $this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler'); + + $this->assertCount(2, $nodes, '->reduce() filters the nodes in the list'); + } + + public function testAttr() + { + $this->assertEquals('first', $this->createTestCrawler()->filterXPath('//li')->attr('class'), '->attr() returns the attribute of the first element of the node list'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->attr('class'); + $this->fail('->attr() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->attr() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testMissingAttrValueIsNull() + { + $crawler = new Crawler(); + $crawler->addContent('
', 'text/html; charset=UTF-8'); + $div = $crawler->filterXPath('//div'); + + $this->assertEquals('sample value', $div->attr('non-empty-attr'), '->attr() reads non-empty attributes correctly'); + $this->assertEquals('', $div->attr('empty-attr'), '->attr() reads empty attributes correctly'); + $this->assertNull($div->attr('missing-attr'), '->attr() reads missing attributes correctly'); + } + + public function testText() + { + $this->assertEquals('One', $this->createTestCrawler()->filterXPath('//li')->text(), '->text() returns the node value of the first element of the node list'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->text(); + $this->fail('->text() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->text() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testHtml() + { + $this->assertEquals('Bar', $this->createTestCrawler()->filterXPath('//a[5]')->html()); + $this->assertEquals('' + , trim($this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html())); + + try { + $this->createTestCrawler()->filterXPath('//ol')->html(); + $this->fail('->html() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->html() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testExtract() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + + $this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list'); + $this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list'); + + $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty'); + } + + public function testFilterXpathComplexQueries() + { + $crawler = $this->createTestCrawler()->filterXPath('//body'); + + $this->assertCount(0, $crawler->filterXPath('/input')); + $this->assertCount(0, $crawler->filterXPath('/body')); + $this->assertCount(1, $crawler->filterXPath('/_root/body')); + $this->assertCount(1, $crawler->filterXPath('./body')); + $this->assertCount(1, $crawler->filterXPath('.//body')); + $this->assertCount(5, $crawler->filterXPath('.//input')); + $this->assertCount(4, $crawler->filterXPath('//form')->filterXPath('//button | //input')); + $this->assertCount(1, $crawler->filterXPath('body')); + $this->assertCount(6, $crawler->filterXPath('//button | //input')); + $this->assertCount(1, $crawler->filterXPath('//body')); + $this->assertCount(1, $crawler->filterXPath('descendant-or-self::body')); + $this->assertCount(1, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('./div'), 'A child selection finds only the current div'); + $this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('descendant::div'), 'A descendant selector matches the current div and its child'); + $this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('//div'), 'A descendant selector matches the current div and its child'); + $this->assertCount(5, $crawler->filterXPath('(//a | //div)//img')); + $this->assertCount(7, $crawler->filterXPath('((//a | //div)//img | //ul)')); + $this->assertCount(7, $crawler->filterXPath('( ( //a | //div )//img | //ul )')); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::filterXPath + */ + public function testFilterXPath() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->filterXPath('//li'), '->filterXPath() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filterXPath() returns a new instance of a crawler'); + + $crawler = $this->createTestCrawler()->filterXPath('//ul'); + $this->assertCount(6, $crawler->filterXPath('//li'), '->filterXPath() filters the node list with the XPath expression'); + + $crawler = $this->createTestCrawler(); + $this->assertCount(3, $crawler->filterXPath('//body')->filterXPath('//button')->parents(), '->filterXpath() preserves parents when chained'); + } + + public function testFilterXPathWithDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//default:entry/default:id'); + $this->assertCount(1, $crawler, '->filterXPath() automatically registers a namespace'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterXPathWithCustomDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler(); + $crawler->setDefaultNamespacePrefix('x'); + $crawler = $crawler->filterXPath('//x:entry/x:id'); + + $this->assertCount(1, $crawler, '->filterXPath() lets to override the default namespace prefix'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterXPathWithNamespace() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//yt:accessControl'); + $this->assertCount(2, $crawler, '->filterXPath() automatically registers a namespace'); + } + + public function testFilterXPathWithMultipleNamespaces() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//media:group/yt:aspectRatio'); + $this->assertCount(1, $crawler, '->filterXPath() automatically registers multiple namespaces'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterXPathWithManuallyRegisteredNamespace() + { + $crawler = $this->createTestXmlCrawler(); + $crawler->registerNamespace('m', 'http://search.yahoo.com/mrss/'); + + $crawler = $crawler->filterXPath('//m:group/yt:aspectRatio'); + $this->assertCount(1, $crawler, '->filterXPath() uses manually registered namespace'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterXPathWithAnUrl() + { + $crawler = $this->createTestXmlCrawler(); + + $crawler = $crawler->filterXPath('//media:category[@scheme="http://gdata.youtube.com/schemas/2007/categories.cat"]'); + $this->assertCount(1, $crawler); + $this->assertSame('Music', $crawler->text()); + } + + public function testFilterXPathWithFakeRoot() + { + $crawler = $this->createTestCrawler(); + $this->assertCount(0, $crawler->filterXPath('.'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + $this->assertCount(0, $crawler->filterXPath('/_root'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + $this->assertCount(0, $crawler->filterXPath('self::*'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + $this->assertCount(0, $crawler->filterXPath('self::_root'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + } + + public function testFilterXPathWithAncestorAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('ancestor::*'), 'The fake root node has no ancestor nodes'); + } + + public function testFilterXPathWithAncestorOrSelfAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('ancestor-or-self::*'), 'The fake root node has no ancestor nodes'); + } + + public function testFilterXPathWithAttributeAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('attribute::*'), 'The fake root node has no attribute nodes'); + } + + public function testFilterXPathWithAttributeAxisAfterElementAxis() + { + $this->assertCount(3, $this->createTestCrawler()->filterXPath('//form/button/attribute::*'), '->filterXPath() handles attribute axes properly when they are preceded by an element filtering axis'); + } + + public function testFilterXPathWithChildAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]'); + + $this->assertCount(1, $crawler->filterXPath('child::div'), 'A child selection finds only the current div'); + } + + public function testFilterXPathWithFollowingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('following::div'), 'The fake root node has no following nodes'); + } + + public function testFilterXPathWithFollowingSiblingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('following-sibling::div'), 'The fake root node has no following nodes'); + } + + public function testFilterXPathWithNamespaceAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//button'); + + $this->assertCount(0, $crawler->filterXPath('namespace::*'), 'The fake root node has no namespace nodes'); + } + + public function testFilterXPathWithNamespaceAxisAfterElementAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]/namespace::*'); + + $this->assertCount(0, $crawler->filterXPath('namespace::*'), 'Namespace axes cannot be requested'); + } + + public function testFilterXPathWithParentAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//button'); + + $this->assertCount(0, $crawler->filterXPath('parent::*'), 'The fake root node has no parent nodes'); + } + + public function testFilterXPathWithPrecedingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('preceding::*'), 'The fake root node has no preceding nodes'); + } + + public function testFilterXPathWithPrecedingSiblingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('preceding-sibling::*'), 'The fake root node has no preceding nodes'); + } + + public function testFilterXPathWithSelfAxes() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('self::a'), 'The fake root node has no "real" element name'); + $this->assertCount(0, $crawler->filterXPath('self::a/img'), 'The fake root node has no "real" element name'); + $this->assertCount(9, $crawler->filterXPath('self::*/a')); + } + + /** + * @covers Symfony\Component\DomCrawler\Crawler::filter + */ + public function testFilter() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->filter('li'), '->filter() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filter() returns a new instance of a crawler'); + + $crawler = $this->createTestCrawler()->filter('ul'); + + $this->assertCount(6, $crawler->filter('li'), '->filter() filters the node list with the CSS selector'); + } + + public function testFilterWithDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler()->filter('default|entry default|id'); + $this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterWithNamespace() + { + CssSelector::disableHtmlExtension(); + + $crawler = $this->createTestXmlCrawler()->filter('yt|accessControl'); + $this->assertCount(2, $crawler, '->filter() automatically registers namespaces'); + } + + public function testFilterWithMultipleNamespaces() + { + CssSelector::disableHtmlExtension(); + + $crawler = $this->createTestXmlCrawler()->filter('media|group yt|aspectRatio'); + $this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterWithDefaultNamespaceOnly() + { + $crawler = new Crawler(' + + + http://localhost/foo + weekly + 0.5 + 2012-11-16 + + + http://localhost/bar + weekly + 0.5 + 2012-11-16 + + + '); + + $this->assertEquals(2, $crawler->filter('url')->count()); + } + + public function testSelectLink() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->selectLink('Foo'), '->selectLink() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectLink() returns a new instance of a crawler'); + + $this->assertCount(1, $crawler->selectLink('Fabien\'s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(1, $crawler->selectLink('Fabien\'s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(2, $crawler->selectLink('Fabien"s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(2, $crawler->selectLink('Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(1, $crawler->selectLink('\' Fabien"s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(1, $crawler->selectLink('\' Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(4, $crawler->selectLink('Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(4, $crawler->selectLink('Bar'), '->selectLink() selects links by the node values'); + } + + public function testSelectButton() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->selectButton('FooValue'), '->selectButton() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectButton() returns a new instance of a crawler'); + + $this->assertEquals(1, $crawler->selectButton('FooValue')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('FooName')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('FooId')->count(), '->selectButton() selects buttons'); + + $this->assertEquals(1, $crawler->selectButton('BarValue')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('BarName')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons'); + + $this->assertEquals(1, $crawler->selectButton('FooBarValue')->count(), '->selectButton() selects buttons with form attribute too'); + $this->assertEquals(1, $crawler->selectButton('FooBarName')->count(), '->selectButton() selects buttons with form attribute too'); + } + + public function testSelectButtonWithSingleQuotesInNameAttribute() + { + $html = << + + +
+
+ +
+ + +HTML; + + $crawler = new Crawler($html); + + $this->assertCount(1, $crawler->selectButton('Click \'Here\'')); + } + + public function testSelectButtonWithDoubleQuotesInNameAttribute() + { + $html = << + + +
+ Login +
+
+ +
+ + +HTML; + + $crawler = new Crawler($html); + + $this->assertCount(1, $crawler->selectButton('Click "Here"')); + } + + public function testLink() + { + $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $crawler->link(), '->link() returns a Link instance'); + + $this->assertEquals('POST', $crawler->link('post')->getMethod(), '->link() takes a method as its argument'); + + $crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('GetLink'); + $this->assertEquals('http://example.com/bar?get=param', $crawler->link()->getUri(), '->link() returns a Link instance'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->link(); + $this->fail('->link() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->link() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testSelectLinkAndLinkFiltered() + { + $html = << + + +
+ Login +
+
+ +
+ + +HTML; + + $crawler = new Crawler($html); + $filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'login-form']"); + + $this->assertCount(0, $filtered->selectLink('Login')); + $this->assertCount(1, $filtered->selectButton('Submit')); + + $filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'action']"); + + $this->assertCount(1, $filtered->selectLink('Login')); + $this->assertCount(0, $filtered->selectButton('Submit')); + + $this->assertCount(1, $crawler->selectLink('Login')->selectLink('Login')); + $this->assertCount(1, $crawler->selectButton('Submit')->selectButton('Submit')); + } + + public function testChaining() + { + $crawler = new Crawler('
'); + + $this->assertEquals('a', $crawler->filterXPath('//div')->filterXPath('div')->filterXPath('div')->attr('name')); + } + + public function testLinks() + { + $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); + $this->assertInternalType('array', $crawler->links(), '->links() returns an array'); + + $this->assertCount(4, $crawler->links(), '->links() returns an array'); + $links = $crawler->links(); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $links[0], '->links() returns an array of Link instances'); + + $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty'); + } + + public function testForm() + { + $testCrawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler = $testCrawler->selectButton('FooValue'); + $crawler2 = $testCrawler->selectButton('FooBarValue'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler2->form(), '->form() returns a Form instance'); + + $this->assertEquals($crawler->form()->getFormNode()->getAttribute('id'), $crawler2->form()->getFormNode()->getAttribute('id'), '->form() works on elements with form attribute'); + + $this->assertEquals(array('FooName' => 'FooBar', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument'); + $this->assertEquals(array('FooName' => 'FooValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form()->getValues(), '->getValues() returns correct form values'); + $this->assertEquals(array('FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler2->form()->getValues(), '->getValues() returns correct form values'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->form(); + $this->fail('->form() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->form() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testLast() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $this->assertNotSame($crawler, $crawler->last(), '->last() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->last() returns a new instance of a crawler'); + + $this->assertEquals('Three', $crawler->last()->text()); + } + + public function testFirst() + { + $crawler = $this->createTestCrawler()->filterXPath('//li'); + $this->assertNotSame($crawler, $crawler->first(), '->first() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->first() returns a new instance of a crawler'); + + $this->assertEquals('One', $crawler->first()->text()); + } + + public function testSiblings() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1); + $this->assertNotSame($crawler, $crawler->siblings(), '->siblings() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->siblings() returns a new instance of a crawler'); + + $nodes = $crawler->siblings(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('One', $nodes->eq(0)->text()); + $this->assertEquals('Three', $nodes->eq(1)->text()); + + $nodes = $this->createTestCrawler()->filterXPath('//li')->eq(0)->siblings(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('Two', $nodes->eq(0)->text()); + $this->assertEquals('Three', $nodes->eq(1)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->siblings(); + $this->fail('->siblings() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->siblings() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testNextAll() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1); + $this->assertNotSame($crawler, $crawler->nextAll(), '->nextAll() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->nextAll() returns a new instance of a crawler'); + + $nodes = $crawler->nextAll(); + $this->assertEquals(1, $nodes->count()); + $this->assertEquals('Three', $nodes->eq(0)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->nextAll(); + $this->fail('->nextAll() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->nextAll() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testPreviousAll() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(2); + $this->assertNotSame($crawler, $crawler->previousAll(), '->previousAll() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->previousAll() returns a new instance of a crawler'); + + $nodes = $crawler->previousAll(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('Two', $nodes->eq(0)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->previousAll(); + $this->fail('->previousAll() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->previousAll() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testChildren() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul'); + $this->assertNotSame($crawler, $crawler->children(), '->children() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->children() returns a new instance of a crawler'); + + $nodes = $crawler->children(); + $this->assertEquals(3, $nodes->count()); + $this->assertEquals('One', $nodes->eq(0)->text()); + $this->assertEquals('Two', $nodes->eq(1)->text()); + $this->assertEquals('Three', $nodes->eq(2)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->children(); + $this->fail('->children() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->children() throws an \InvalidArgumentException if the node list is empty'); + } + + try { + $crawler = new Crawler('

'); + $crawler->filter('p')->children(); + $this->assertTrue(true, '->children() does not trigger a notice if the node has no children'); + } catch (\PHPUnit_Framework_Error_Notice $e) { + $this->fail('->children() does not trigger a notice if the node has no children'); + } + } + + public function testParents() + { + $crawler = $this->createTestCrawler()->filterXPath('//li[1]'); + $this->assertNotSame($crawler, $crawler->parents(), '->parents() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->parents() returns a new instance of a crawler'); + + $nodes = $crawler->parents(); + $this->assertEquals(3, $nodes->count()); + + $nodes = $this->createTestCrawler()->filterXPath('//html')->parents(); + $this->assertEquals(0, $nodes->count()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->parents(); + $this->fail('->parents() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->parents() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testBaseTag() + { + $crawler = new Crawler(''); + $this->assertEquals('http://base.com/link', $crawler->filterXPath('//a')->link()->getUri()); + + $crawler = new Crawler('', 'https://domain.com'); + $this->assertEquals('https://base.com/link', $crawler->filterXPath('//a')->link()->getUri(), ' tag can use a schema-less URL'); + + $crawler = new Crawler('', 'https://domain.com'); + $this->assertEquals('https://domain.com/path/link', $crawler->filterXPath('//a')->link()->getUri(), ' tag can set a path'); + } + + public function createTestCrawler($uri = null) + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + + + Foo + Fabien\'s Foo + Fabien"s Foo + \' Fabien"s Foo + + Bar +    Fabien\'s Bar   + Fabien"s Bar + \' Fabien"s Bar + + GetLink + +
+ + + +
', + array('bar' => array('InputFormField', 'bar')), + ), + array( + 'appends the submitted button value but not other submit buttons', + ' + ', + array('foobar' => array('InputFormField', 'foobar')), + ), + array( + 'turns an image input into x and y fields', + '', + array('bar.x' => array('InputFormField', '0'), 'bar.y' => array('InputFormField', '0')), + ), + array( + 'returns textareas', + ' + ', + array('foo' => array('TextareaFormField', 'foo')), + ), + array( + 'returns inputs', + ' + ', + array('foo' => array('InputFormField', 'foo')), + ), + array( + 'returns checkboxes', + ' + ', + array('foo' => array('ChoiceFormField', 'foo')), + ), + array( + 'returns not-checked checkboxes', + ' + ', + array('foo' => array('ChoiceFormField', false)), + ), + array( + 'returns radio buttons', + ' + + ', + array('foo' => array('ChoiceFormField', 'bar')), + ), + array( + 'returns file inputs', + ' + ', + array('foo' => array('FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), + ), + ); + } + + public function testGetFormNode() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
'); + + $form = new Form($dom->getElementsByTagName('input')->item(0), 'http://example.com'); + + $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testGetFormNodeFromNamedForm() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
'); + + $form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com'); + + $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testGetMethod() + { + $form = $this->createForm('
'); + $this->assertEquals('GET', $form->getMethod(), '->getMethod() returns get if no method is defined'); + + $form = $this->createForm('
'); + $this->assertEquals('POST', $form->getMethod(), '->getMethod() returns the method attribute value of the form'); + + $form = $this->createForm('
', 'put'); + $this->assertEquals('PUT', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + + $form = $this->createForm('
', 'delete'); + $this->assertEquals('DELETE', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + + $form = $this->createForm('
', 'patch'); + $this->assertEquals('PATCH', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + } + + public function testGetSetValue() + { + $form = $this->createForm('
'); + + $this->assertEquals('foo', $form['foo']->getValue(), '->offsetGet() returns the value of a form field'); + + $form['foo'] = 'bar'; + + $this->assertEquals('bar', $form['foo']->getValue(), '->offsetSet() changes the value of a form field'); + + try { + $form['foobar'] = 'bar'; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } + + try { + $form['foobar']; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } + } + + public function testSetValueOnMultiValuedFieldsWithMalformedName() + { + $form = $this->createForm('
'); + + try { + $form['foo[bar'] = 'bar'; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.'); + } + } + + public function testDisableValidation() + { + $form = $this->createForm('
+ + + +
'); + + $form->disableValidation(); + + $form['foo[bar]']->select('foo'); + $form['foo[baz]']->select('bar'); + $this->assertEquals('foo', $form['foo[bar]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.'); + $this->assertEquals('bar', $form['foo[baz]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.'); + } + + public function testOffsetUnset() + { + $form = $this->createForm('
'); + unset($form['foo']); + $this->assertFalse(isset($form['foo']), '->offsetUnset() removes a field'); + } + + public function testOffsetExists() + { + $form = $this->createForm('
'); + + $this->assertTrue(isset($form['foo']), '->offsetExists() return true if the field exists'); + $this->assertFalse(isset($form['bar']), '->offsetExists() return false if the field does not exist'); + } + + public function testGetValues() + { + $form = $this->createForm('
'); + $this->assertEquals(array('foo[bar]' => 'foo', 'bar' => 'bar', 'baz' => array()), $form->getValues(), '->getValues() returns all form field values'); + + $form = $this->createForm('
'); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include not-checked checkboxes'); + + $form = $this->createForm('
'); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include file input fields'); + + $form = $this->createForm('
'); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include disabled fields'); + } + + public function testSetValues() + { + $form = $this->createForm('
'); + $form->setValues(array('foo' => false, 'bar' => 'foo')); + $this->assertEquals(array('bar' => 'foo'), $form->getValues(), '->setValues() sets the values of fields'); + } + + public function testMultiselectSetValues() + { + $form = $this->createForm('
'); + $form->setValues(array('multi' => array("foo", "bar"))); + $this->assertEquals(array('multi' => array('foo', 'bar')), $form->getValues(), '->setValue() sets the values of select'); + } + + public function testGetPhpValues() + { + $form = $this->createForm('
'); + $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays'); + + $form = $this->createForm('
'); + $this->assertEquals(array('fo.o' => array('ba.r' => 'foo'), 'ba r' => 'bar'), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names'); + + $form = $this->createForm('
'); + $this->assertEquals(array('fo.o' => array('ba.r' => array('foo', 'ba.z' => 'bar'))), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names recursively'); + + $form = $this->createForm('
'); + $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), "->getPhpValues() doesn't return empty values"); + } + + public function testGetFiles() + { + $form = $this->createForm('
'); + $this->assertEquals(array(), $form->getFiles(), '->getFiles() returns an empty array if method is get'); + + $form = $this->createForm('
'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for POST'); + + $form = $this->createForm('
', 'put'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PUT'); + + $form = $this->createForm('
', 'delete'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for DELETE'); + + $form = $this->createForm('
', 'patch'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PATCH'); + + $form = $this->createForm('
'); + $this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include disabled file fields'); + } + + public function testGetPhpFiles() + { + $form = $this->createForm('
'); + $this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays'); + + $form = $this->createForm('
'); + $this->assertEquals(array('f.o o' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names'); + + $form = $this->createForm('
'); + $this->assertEquals(array('f.o o' => array('bar' => array('ba.z' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0), array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names recursively'); + } + + /** + * @dataProvider provideGetUriValues + */ + public function testGetUri($message, $form, $values, $uri, $method = null) + { + $form = $this->createForm($form, $method); + $form->setValues($values); + + $this->assertEquals('http://example.com'.$uri, $form->getUri(), '->getUri() '.$message); + } + + public function testGetBaseUri() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
'); + + $nodes = $dom->getElementsByTagName('input'); + $form = new Form($nodes->item($nodes->length - 1), 'http://www.foo.com/'); + $this->assertEquals('http://www.foo.com/foo.php', $form->getUri()); + } + + public function testGetUriWithAnchor() + { + $form = $this->createForm('
', null, 'http://example.com/id/123'); + + $this->assertEquals('http://example.com/id/123#foo', $form->getUri()); + } + + public function testGetUriActionAbsolute() + { + $formHtml = '
'; + + $form = $this->createForm($formHtml); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://login.foo.com'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://login.foo.com/bar/'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + // The action URI haven't the same domain Host have an another domain as Host + $form = $this->createForm($formHtml, null, 'https://www.foo.com'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://www.foo.com/bar/'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + } + + public function testGetUriAbsolute() + { + $form = $this->createForm('
', null, 'http://localhost/foo/'); + $this->assertEquals('http://localhost/foo/foo', $form->getUri(), '->getUri() returns absolute URIs'); + + $form = $this->createForm('
', null, 'http://localhost/foo/'); + $this->assertEquals('http://localhost/foo', $form->getUri(), '->getUri() returns absolute URIs'); + } + + public function testGetUriWithOnlyQueryString() + { + $form = $this->createForm('
', null, 'http://localhost/foo/bar'); + $this->assertEquals('http://localhost/foo/bar?get=param', $form->getUri(), '->getUri() returns absolute URIs only if the host has been defined in the constructor'); + } + + public function testGetUriWithoutAction() + { + $form = $this->createForm('
', null, 'http://localhost/foo/bar'); + $this->assertEquals('http://localhost/foo/bar', $form->getUri(), '->getUri() returns path if no action defined'); + } + + public function provideGetUriValues() + { + return array( + array( + 'returns the URI of the form', + '
', + array(), + '/foo', + ), + array( + 'appends the form values if the method is get', + '
', + array(), + '/foo?foo=foo', + ), + array( + 'appends the form values and merges the submitted values', + '
', + array('foo' => 'bar'), + '/foo?foo=bar', + ), + array( + 'does not append values if the method is post', + '
', + array(), + '/foo', + ), + array( + 'does not append values if the method is patch', + '
', + array(), + '/foo', + 'PUT', + ), + array( + 'does not append values if the method is delete', + '
', + array(), + '/foo', + 'DELETE', + ), + array( + 'does not append values if the method is put', + '
', + array(), + '/foo', + 'PATCH', + ), + array( + 'appends the form values to an existing query string', + '
', + array(), + '/foo?bar=bar&foo=foo', + ), + array( + 'returns an empty URI if the action is empty', + '
', + array(), + '/', + ), + array( + 'appends the form values even if the action is empty', + '
', + array(), + '/?foo=foo', + ), + array( + 'chooses the path if the action attribute value is a sharp (#)', + '
', + array(), + '/#', + ), + ); + } + + public function testHas() + { + $form = $this->createForm('
'); + + $this->assertFalse($form->has('foo'), '->has() returns false if a field is not in the form'); + $this->assertTrue($form->has('bar'), '->has() returns true if a field is in the form'); + } + + public function testRemove() + { + $form = $this->createForm('
'); + $form->remove('bar'); + $this->assertFalse($form->has('bar'), '->remove() removes a field'); + } + + public function testGet() + { + $form = $this->createForm('
'); + + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $form->get('bar'), '->get() returns the field object associated with the given name'); + + try { + $form->get('foo'); + $this->fail('->get() throws an \InvalidArgumentException if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->get() throws an \InvalidArgumentException if the field does not exist'); + } + } + + public function testAll() + { + $form = $this->createForm('
'); + + $fields = $form->all(); + $this->assertCount(1, $fields, '->all() return an array of form field objects'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $fields['bar'], '->all() return an array of form field objects'); + } + + public function testSubmitWithoutAFormButton() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + +
+ +
+ + '); + + $nodes = $dom->getElementsByTagName('form'); + $form = new Form($nodes->item(0), 'http://example.com'); + $this->assertSame($nodes->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testTypeAttributeIsCaseInsensitive() + { + $form = $this->createForm('
'); + $this->assertTrue($form->has('example.x'), '->has() returns true if the image input was correctly turned into an x and a y fields'); + $this->assertTrue($form->has('example.y'), '->has() returns true if the image input was correctly turned into an x and a y fields'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryAddThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('[foo]')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryRemoveThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->remove('[foo]'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryGetThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->get('[foo]'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryGetThrowAnExceptionWhenTheFieldDoesNotExist() + { + $registry = new FormFieldRegistry(); + $registry->get('foo'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistrySetThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->set('[foo]', null); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistrySetThrowAnExceptionWhenTheFieldDoesNotExist() + { + $registry = new FormFieldRegistry(); + $registry->set('foo', null); + } + + public function testFormFieldRegistryHasReturnsTrueWhenTheFQNExists() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo[bar]')); + + $this->assertTrue($registry->has('foo')); + $this->assertTrue($registry->has('foo[bar]')); + $this->assertFalse($registry->has('bar')); + $this->assertFalse($registry->has('foo[foo]')); + } + + public function testFormRegistryFieldsCanBeRemoved() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo')); + $registry->remove('foo'); + $this->assertFalse($registry->has('foo')); + } + + public function testFormRegistrySupportsMultivaluedFields() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo[]')); + $registry->add($this->getFormFieldMock('foo[]')); + $registry->add($this->getFormFieldMock('bar[5]')); + $registry->add($this->getFormFieldMock('bar[]')); + $registry->add($this->getFormFieldMock('bar[baz]')); + + $this->assertEquals( + array('foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'), + array_keys($registry->all()) + ); + } + + public function testFormRegistrySetValues() + { + $registry = new FormFieldRegistry(); + $registry->add($f2 = $this->getFormFieldMock('foo[2]')); + $registry->add($f3 = $this->getFormFieldMock('foo[3]')); + $registry->add($fbb = $this->getFormFieldMock('foo[bar][baz]')); + + $f2 + ->expects($this->exactly(2)) + ->method('setValue') + ->with(2) + ; + + $f3 + ->expects($this->exactly(2)) + ->method('setValue') + ->with(3) + ; + + $fbb + ->expects($this->exactly(2)) + ->method('setValue') + ->with('fbb') + ; + + $registry->set('foo[2]', 2); + $registry->set('foo[3]', 3); + $registry->set('foo[bar][baz]', 'fbb'); + + $registry->set('foo', array( + 2 => 2, + 3 => 3, + 'bar' => array( + 'baz' => 'fbb', + ), + )); + } + + public function testDifferentFieldTypesWithSameName() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + + +
+ + + + + + +
+ + + '); + $form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com'); + + $this->assertInstanceOf('Symfony\Component\DomCrawler\Field\ChoiceFormField', $form->get('option')); + } + + protected function getFormFieldMock($name, $value = null) + { + $field = $this + ->getMockBuilder('Symfony\\Component\\DomCrawler\\Field\\FormField') + ->setMethods(array('getName', 'getValue', 'setValue', 'initialize')) + ->disableOriginalConstructor() + ->getMock() + ; + + $field + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)) + ; + + $field + ->expects($this->any()) + ->method('getValue') + ->will($this->returnValue($value)) + ; + + return $field; + } + + protected function createForm($form, $method = null, $currentUri = null) + { + $dom = new \DOMDocument(); + $dom->loadHTML(''.$form.''); + + $xPath = new \DOMXPath($dom); + $nodes = $xPath->query('//input | //button'); + + if (null === $currentUri) { + $currentUri = 'http://example.com/'; + } + + return new Form($nodes->item($nodes->length - 1), $currentUri, $method); + } + + protected function createTestHtml5Form() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + +

Hello form

+
+
+ +
+ + +
+ +
+
+
+ + + +
+ +
+ +
+
+
+ + +
+
+ + +
+
+
+ + + +
+