diff --git a/composer.json b/composer.json index 7ce3db6..9d8e373 100644 --- a/composer.json +++ b/composer.json @@ -3,8 +3,17 @@ "description": "Drupal is an open source content management platform powering millions of websites and applications.", "type": "drupal-core", "license": "GPL-2.0+", + "repositories": [ + { + "type":"git", + "url": "https://github.com/larowlan/MinkGoutteDriver.git" + } + ], "require": { "php": ">=5.4.2", + "behat/mink": "~1.5", + "behat/mink-goutte-driver": "dev-master", + "fabpot/goutte": "dev-master", "sdboyer/gliph": "0.1.*", "symfony/class-loader": "2.4.*", "symfony/css-selector": "2.4.*", diff --git a/composer.lock b/composer.lock index fdc46fe..224a646 100644 --- a/composer.lock +++ b/composer.lock @@ -4,9 +4,164 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "e1a7eeed7d09f5c76f642f07b7a0a579", + "hash": "14e02fec8f9fb930688b0709cf0bc5f3", "packages": [ { + "name": "behat/mink", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Mink.git", + "reference": "0769e6d9726c140a54dbf827a438c0f9912749fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Mink/zipball/0769e6d9726c140a54dbf827a438c0f9912749fe", + "reference": "0769e6d9726c140a54dbf827a438c0f9912749fe", + "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-develop": "1.5.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": "2013-04-13 23:39:27" + }, + { + "name": "behat/mink-browserkit-driver", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/MinkBrowserKitDriver.git", + "reference": "63960c8fcad4529faad1ff33e950217980baa64c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/MinkBrowserKitDriver/zipball/63960c8fcad4529faad1ff33e950217980baa64c", + "reference": "63960c8fcad4529faad1ff33e950217980baa64c", + "shasum": "" + }, + "require": { + "behat/mink": "~1.5.0", + "php": ">=5.3.1", + "symfony/browser-kit": "~2.0", + "symfony/dom-crawler": "~2.0" + }, + "require-dev": { + "silex/silex": "@dev" + }, + "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": "Symfony2 BrowserKit driver for Mink framework", + "homepage": "http://mink.behat.org/", + "keywords": [ + "Mink", + "Symfony2", + "browser", + "testing" + ], + "time": "2013-04-13 23:46:30" + }, + { + "name": "behat/mink-goutte-driver", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/larowlan/MinkGoutteDriver.git", + "reference": "488f7f02b1e907888f4b156b635693daf51d760c" + }, + "require": { + "behat/mink": "~1.5@dev", + "behat/mink-browserkit-driver": "~1.1@dev", + "fabpot/goutte": "dev-master", + "php": ">=5.4" + }, + "type": "mink-driver", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Mink\\Driver": "src/" + } + }, + "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-06-04 04:35:02" + }, + { "name": "doctrine/annotations", "version": "dev-master", "source": { @@ -458,6 +613,55 @@ "time": "2013-12-30 22:31:37" }, { + "name": "fabpot/goutte", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Goutte.git", + "reference": "b12c3f7ec68d8814b50444cfe142fd0a056557f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Goutte/zipball/b12c3f7ec68d8814b50444cfe142fd0a056557f9", + "reference": "b12c3f7ec68d8814b50444cfe142fd0a056557f9", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "4.*", + "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-0": { + "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/fabpot/Goutte", + "keywords": [ + "scraper" + ], + "time": "2014-07-22 13:24:11" + }, + { "name": "guzzlehttp/guzzle", "version": "4.1.3", "source": { @@ -1464,6 +1668,63 @@ "time": "2013-10-14 15:32:46" }, { + "name": "symfony/browser-kit", + "version": "v2.5.2", + "target-dir": "Symfony/Component/BrowserKit", + "source": { + "type": "git", + "url": "https://github.com/symfony/BrowserKit.git", + "reference": "b5e807a669333ac9e7def19ef39a6e542786010d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/b5e807a669333ac9e7def19ef39a6e542786010d", + "reference": "b5e807a669333ac9e7def19ef39a6e542786010d", + "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": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "http://symfony.com", + "time": "2014-07-09 09:05:48" + }, + { "name": "symfony/class-loader", "version": "v2.4.1", "target-dir": "Symfony/Component/ClassLoader", @@ -1680,6 +1941,61 @@ "time": "2014-01-01 09:02:49" }, { + "name": "symfony/dom-crawler", + "version": "v2.5.2", + "target-dir": "Symfony/Component/DomCrawler", + "source": { + "type": "git", + "url": "https://github.com/symfony/DomCrawler.git", + "reference": "b3d748f9d7ae77890d935bb97445c666b83dd59e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/b3d748f9d7ae77890d935bb97445c666b83dd59e", + "reference": "b3d748f9d7ae77890d935bb97445c666b83dd59e", + "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": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "http://symfony.com", + "time": "2014-07-09 09:05:48" + }, + { "name": "symfony/event-dispatcher", "version": "v2.4.1", "target-dir": "Symfony/Component/EventDispatcher", @@ -2423,14 +2739,12 @@ "time": "2013-06-12 19:46:58" } ], - "packages-dev": [ - - ], - "aliases": [ - - ], + "packages-dev": [], + "aliases": [], "minimum-stability": "stable", "stability-flags": { + "behat/mink-goutte-driver": 20, + "fabpot/goutte": 20, "symfony/yaml": 20, "doctrine/common": 20, "doctrine/annotations": 20, @@ -2441,7 +2755,5 @@ "platform": { "php": ">=5.4.2" }, - "platform-dev": [ - - ] + "platform-dev": [] } diff --git a/core/modules/block/src/Tests/BlockHtmlTest.php b/core/modules/block/src/Tests/BlockHtmlTest.php index 5e508e5..0bb68f7 100644 --- a/core/modules/block/src/Tests/BlockHtmlTest.php +++ b/core/modules/block/src/Tests/BlockHtmlTest.php @@ -7,14 +7,14 @@ namespace Drupal\block\Tests; -use Drupal\simpletest\WebTestBase; +use Drupal\simpletest\BrowserTestBase; /** * Tests block HTML ID validity. * * @group block */ -class BlockHtmlTest extends WebTestBase { +class BlockHtmlTest extends BrowserTestBase { /** * Modules to enable. @@ -45,11 +45,10 @@ function testHtml() { // Ensure that a block's ID is converted to an HTML valid ID, and that // block-specific attributes are added to the same DOM element. - $this->assertFieldByXPath('//div[@id="block-test-html-block" and @data-custom-attribute="foo"]', NULL, 'HTML ID and attributes for test block are valid and on the same DOM element.'); + $this->assertXPath('//div[@id="block-test-html-block" and @data-custom-attribute="foo"]', 'HTML ID and attributes for test block are valid and on the same DOM element.'); // Ensure expected markup for a menu block. - $elements = $this->xpath('//div[contains(@class, :div-class)]/div/ul[contains(@class, :ul-class)]/li', array(':div-class' => 'block-system', ':ul-class' => 'menu')); - $this->assertTrue(!empty($elements), 'The proper block markup was found.'); + $this->assertXPath('//div[contains(@class, "block-system")]/div/ul[contains(@class, "menu")]/li', 'The proper block markup was found.'); } } diff --git a/core/modules/simpletest/src/BrowserTestBase.php b/core/modules/simpletest/src/BrowserTestBase.php new file mode 100644 index 0000000..458db38 --- /dev/null +++ b/core/modules/simpletest/src/BrowserTestBase.php @@ -0,0 +1,261 @@ +skipClasses[__CLASS__] = TRUE; + } + + protected function setUp() { + parent::setUp(); + if (!isset($this->mink)) { + $driver = new GoutteDriver(); + $session = new Session($driver); + $this->mink = new Mink(); + $this->mink->registerSession('goutte', $session); + $this->mink->setDefaultSessionName('goutte'); + } + } + + protected function tearDown() { + parent::tearDown(); + $this->mink->resetSessions(); + } + + /** + * 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. + * @param array $headers + * An array containing additional HTTP request headers + * + * @return string + * The retrieved HTML string, also available as $this->getRawContent() + */ + protected function drupalGet($path, array $options = array(), array $headers = 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->mink->getSession(); + + // Set request headers. + $session->setRequestHeader('user-agent', drupal_generate_test_ua($this->databasePrefix)); + foreach ($headers as $name => $value) { + $session->setRequestHeader($name, $value); + } + + $session->visit($url); + + // Restore default request headers. + foreach ($headers as $name => $value) { + $session->setRequestHeader($name, NULL); + } + + $out = $session->getPage()->getContent(); + + // Ensure that any changes to variables in the other thread are picked up. + $this->refreshVariables(); + + $verbose = 'GET request to: ' . $path . + '
' . String::checkPlain(var_export(array_map('trim', $headers), TRUE)) . ''; + } + $verbose .= '
' . print_r($mail, TRUE) . ''); + } + } + + /** + * Sets up a Drupal site for running functional and integration tests. + * + * Installs Drupal with the installation profile specified in + * \Drupal\simpletest\WebTestBase::$profile into the prefixed database. + + * Afterwards, installs any additional modules specified in the static + * \Drupal\simpletest\WebTestBase::$modules property of each class in the + * class hierarchy. + * + * After installation all caches are flushed and several configuration values + * are reset to the values of the parent site executing the test, since the + * default values may be incompatible with the environment in which tests are + * being executed. + */ + protected function setUp() { + // When running tests through the Simpletest UI (vs. on the command line), + // Simpletest's batch conflicts with the installer's batch. Batch API does + // not support the concept of nested batches (in which the nested is not + // progressive), so we need to temporarily pretend there was no batch. + // Backup the currently running Simpletest batch. + $this->originalBatch = batch_get(); + + // 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 = $this->originalSessionName; + + // Reset the static batch to remove Simpletest's batch operations. + $batch = &batch_get(); + $batch = array(); + + // 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'); + + // 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, + ); + // Save the original site directory path, so that extensions in the + // site-specific directory can still be discovered in the test site + // environment. + // @see \Drupal\Core\SystemListing::scan() + $settings['settings']['test_parent_site'] = (object) array( + 'value' => $this->originalSite, + 'required' => TRUE, + ); + // Add the parent profile's search path to the child site's search paths. + // @see \Drupal\Core\Extension\ExtensionDiscovery::getProfileDirectories() + $settings['conf']['simpletest.settings']['parent_profile'] = (object) array( + 'value' => $this->originalProfile, + '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); + + // 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); + 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, drupal_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(); + + // Restore the original Simpletest batch. + $batch = &batch_get(); + $batch = $this->originalBatch; + + // 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); + + // Temporary fix so that when running from run-tests.sh we don't get an + // empty current path which would indicate we're on the home page. + $path = current_path(); + if (empty($path)) { + _current_path('run-tests'); + } + } + + /** + * 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; + } + + /** + * 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'; + // 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); + } + + /** + * Queues custom translations to be written to settings.php. + * + * Use WebTestBase::writeCustomTranslations() to apply and write the queued + * translations. + * + * @param string $langcode + * The langcode to add translations for. + * @param array $values + * Array of values containing the untranslated string and its translation. + * For example: + * @code + * array( + * '' => array('Sunday' => 'domingo'), + * 'Long month name' => array('March' => 'marzo'), + * ); + * @endcode + * Pass an empty array to remove all existing custom translations for the + * given $langcode. + */ + protected function addCustomTranslations($langcode, array $values) { + // If $values is empty, then the test expects all custom translations to be + // cleared. + if (empty($values)) { + $this->customTranslations[$langcode] = array(); + } + // Otherwise, $values are expected to be merged into previously passed + // values, while retaining keys that are not explicitly set. + else { + foreach ($values as $context => $translations) { + foreach ($translations as $original => $translation) { + $this->customTranslations[$langcode][$context][$original] = $translation; + } + } + } + } + + /** + * Writes custom translations to the test site's settings.php. + * + * Use TestBase::addCustomTranslations() to queue custom translations before + * calling this method. + */ + protected function writeCustomTranslations() { + $settings = array(); + foreach ($this->customTranslations as $langcode => $values) { + $settings_key = 'locale_custom_strings_' . $langcode; + + // Update in-memory settings directly. + $this->settingsSet($settings_key, $values); + + $settings['settings'][$settings_key] = (object) array( + 'value' => $values, + 'required' => TRUE, + ); + } + // Only rewrite settings if there are any translation changes to write. + if (!empty($settings)) { + $this->writeSettings($settings); + } + } + + /** + * 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 TestBase::prepareEnvironment() + * @see TestBase::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(); + + // 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); + + // Make sure the url generator has a request object, otherwise calls to + // $this->drupalGet() will fail. + $this->prepareRequestForGenerator(); + } + + /** + * 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. + drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache'); + drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::deletedTags'); + drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::invalidatedTags'); + + $this->container->get('config.factory')->reset(); + $this->container->get('state')->resetCache(); + } + + /** + * Cleans up after testing. + * + * Deletes created files and temporary files directory, deletes the tables + * created by setUp(), and resets the database prefix. + */ + protected function tearDown() { + // Destroy the testing kernel. + if (isset($this->kernel)) { + $this->kernel->shutdown(); + } + parent::tearDown(); + // Ensure that internal logged in variable is reset. + $this->loggedInUser = FALSE; + } + + /** + * 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 $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()) { + $generator = $this->container->get('url_generator'); + $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); + $generator->updateFromRequest(); + return $request; + } + + /** + * Returns the session name in use on the child site. + * + * @return string + * The name of the session cookie. + */ + public function getSessionName() { + return $this->session_name; + } + + /** + * Takes a path and returns an absolute path. + * + * @param $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; + } +} diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php index e6fe068..15c6fa1 100644 --- a/core/modules/simpletest/src/TestBase.php +++ b/core/modules/simpletest/src/TestBase.php @@ -55,6 +55,11 @@ protected $databasePrefix = NULL; /** + * Whether the files were copied to the test files directory. + */ + protected $generatedTestFiles = FALSE; + + /** * The site directory of the original parent site. * * @var string @@ -148,10 +153,82 @@ /** * The settings array. + * + * @var array */ protected $originalSettings; /** + * The global config array of the original parent site. + * + * @var array + */ + protected $originalConfig; + + /** + * The global conf array of the original parent site. + * + * @var array + */ + protected $originalConf; + + /** + * The global config_directories of the original parent site. + * + * @var array + */ + protected $originalConfigDirectories; + + /** + * The container of the original parent site. + * + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + protected $originalContainer; + + /** + * The shutdown callbacks of the original parent site. + * + * @var array + */ + protected $originalShutdownCallbacks; + + /** + * The profile of the original parent site. + * + * @var string|null + */ + protected $originalProfile; + + /** + * The language of the original parent site. + * + * @var \Drupal\Core\Language\LanguageInterface + */ + protected $originalLanguage; + + /** + * The user of the original parent site. + * + * @var \Drupal\Core\Session\AccountProxyInterface + */ + protected $originalUser; + + /** + * HTTP auth method for simpletest browser. + * + * @var int + */ + protected $httpauth_method; + + /** + * HTTP auth credentials for simpletest browser. + * + * @var string + */ + protected $httpauth_credentials; + + /** * The public file directory for the test environment. * * This is set in TestBase::prepareEnvironment(). @@ -161,6 +238,33 @@ protected $public_files_directory; /** + * The private file directory for the test environment. + * + * This is set in TestBase::prepareEnvironment(). + * + * @var string + */ + protected $private_files_directory; + + /** + * The temp file directory for the test environment. + * + * This is set in TestBase::prepareEnvironment(). + * + * @var string + */ + protected $temp_files_directory; + + /** + * The translation file directory for the test environment. + * + * This is set in TestBase::prepareEnvironment(). + * + * @var string + */ + protected $translation_files_directory; + + /** * Whether to die in case any test assertion fails. * * @var boolean @@ -220,7 +324,7 @@ public function __construct($test_id = NULL) { /** * Checks the matching requirements for Test. * - * @return + * @return array * Array of errors containing a list of unmet requirements. */ protected function checkRequirements() { @@ -249,6 +353,9 @@ protected function checkRequirements() { * by passing in an associative array as $caller. Key 'file' is * the name of the source file, 'line' is the line number and 'function' * is the caller function itself. + * + * @return bool + * TRUE if the assert passed, FALSE otherwise. */ protected function assert($status, $message = '', $group = 'Other', array $caller = NULL) { // Convert boolean status to string status. @@ -305,7 +412,14 @@ protected function assert($status, $message = '', $group = 'Other', array $calle * the method behaves just like \Drupal\simpletest\TestBase::assert() in terms * of storing the assertion. * - * @return + * @param $test_id + * @param $test_class + * @param $status + * @param string $message + * @param string $group + * @param array $caller + * + * @return int * Message ID of the stored assertion. * * @see \Drupal\simpletest\TestBase::assert() @@ -346,7 +460,7 @@ public static function insertAssert($test_id, $test_class, $status, $message = ' * @param $message_id * Message ID of the assertion to delete. * - * @return + * @return bool * TRUE if the assertion was deleted, FALSE otherwise. * * @see \Drupal\simpletest\TestBase::insertAssert() @@ -390,7 +504,7 @@ public static function getDatabaseConnection() { /** * Cycles through backtrace until the first non-assertion method is found. * - * @return + * @return array * Array representing the true caller. */ protected function getAssertionCall() { @@ -427,7 +541,7 @@ protected function getAssertionCall() { * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertTrue($value, $message = '', $group = 'Other') { @@ -452,7 +566,7 @@ protected function assertTrue($value, $message = '', $group = 'Other') { * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertFalse($value, $message = '', $group = 'Other') { @@ -475,7 +589,7 @@ protected function assertFalse($value, $message = '', $group = 'Other') { * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertNull($value, $message = '', $group = 'Other') { @@ -498,7 +612,7 @@ protected function assertNull($value, $message = '', $group = 'Other') { * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertNotNull($value, $message = '', $group = 'Other') { @@ -523,7 +637,7 @@ protected function assertNotNull($value, $message = '', $group = 'Other') { * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertEqual($first, $second, $message = '', $group = 'Other') { @@ -548,7 +662,7 @@ protected function assertEqual($first, $second, $message = '', $group = 'Other') * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertNotEqual($first, $second, $message = '', $group = 'Other') { @@ -573,7 +687,7 @@ protected function assertNotEqual($first, $second, $message = '', $group = 'Othe * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertIdentical($first, $second, $message = '', $group = 'Other') { @@ -598,7 +712,7 @@ protected function assertIdentical($first, $second, $message = '', $group = 'Oth * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertNotIdentical($first, $second, $message = '', $group = 'Other') { @@ -623,7 +737,7 @@ protected function assertNotIdentical($first, $second, $message = '', $group = ' * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE if the assertion succeeded, FALSE otherwise. */ protected function assertIdenticalObject($object1, $object2, $message = '', $group = 'Other') { @@ -667,7 +781,7 @@ protected function assertNoErrorsLogged() { * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * TRUE. */ protected function pass($message = NULL, $group = 'Other') { @@ -688,7 +802,7 @@ protected function pass($message = NULL, $group = 'Other') { * translate this string. Defaults to 'Other'; most tests do not override * this default. * - * @return + * @return bool * FALSE. */ protected function fail($message = NULL, $group = 'Other') { @@ -711,7 +825,7 @@ protected function fail($message = NULL, $group = 'Other') { * @param $caller * The caller of the error. * - * @return + * @return bool * FALSE. */ protected function error($message = '', $group = 'Other', array $caller = NULL) { @@ -1260,6 +1374,8 @@ public function errorHandler($severity, $message, $file = NULL, $line = NULL) { /** * Handle exceptions. * + * @param \Exception $exception + * * @see set_exception_handler */ protected function exceptionHandler($exception) { @@ -1422,7 +1538,7 @@ protected function getRandomGenerator() { * An associative array of parameters, keyed by parameter name, and whose * values are arrays of parameter values. * - * @return + * @return array * A list of permutations, which is an array of arrays. Each inner array * contains the full list of parameters that have been passed, but with a * single value only. diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php index ac2f0a0..532c9fb 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -9,39 +9,21 @@ use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\Crypt; -use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\String; -use Drupal\Core\DrupalKernel; -use Drupal\Core\Database\Database; -use Drupal\Core\Database\ConnectionNotDefinedException; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AnonymousUserSession; -use Drupal\Core\Session\UserSession; -use Drupal\Core\Site\Settings; -use Drupal\Core\StreamWrapper\PublicStream; -use Drupal\Core\Datetime\DrupalDateTime; use Drupal\block\Entity\Block; -use Symfony\Component\HttpFoundation\Request; /** * Test case for typical Drupal tests. * * @ingroup testing */ -abstract class WebTestBase extends TestBase { +abstract class WebTestBase extends FunctionalTestBase { use AssertContentTrait; /** - * The profile to install as a basis for testing. - * - * @var string - */ - protected $profile = 'testing'; - - /** * The URL currently loaded in the internal browser. * * @var string @@ -73,13 +55,6 @@ protected $dumpHeaders = FALSE; /** - * The current user logged in using the internal browser. - * - * @var bool - */ - protected $loggedInUser = FALSE; - - /** * The current cookie file used by cURL. * * We do not reuse the cookies in further runs, so we do not need a file @@ -96,45 +71,11 @@ protected $additionalCurlOptions = array(); /** - * The original user, before it was changed to a clean uid = 1 for testing. - * - * @var object - */ - protected $originalUser = NULL; - - /** - * The original shutdown handlers array, before it was cleaned for testing. - * - * @var array - */ - protected $originalShutdownCallbacks = array(); - - /** - * HTTP authentication method. - */ - protected $httpauth_method = CURLAUTH_BASIC; - - /** - * HTTP authentication credentials (
' . print_r($mail, TRUE) . ''); - } - } - - /** - * 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 $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()) { - $generator = $this->container->get('url_generator'); - $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); - $generator->updateFromRequest(); - return $request; - } } diff --git a/core/vendor/behat/mink-browserkit-driver/.gitignore b/core/vendor/behat/mink-browserkit-driver/.gitignore new file mode 100644 index 0000000..7579f74 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/.gitignore @@ -0,0 +1,2 @@ +vendor +composer.lock 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..c67da81 --- /dev/null +++ b/core/vendor/behat/mink-browserkit-driver/.travis.yml @@ -0,0 +1,14 @@ +language: php + +php: [5.3, 5.4, 5.5] + +env: + - SYMFONY_VERSION='2.1.*' + - SYMFONY_VERSION='2.2.*' + +before_script: + - curl http://getcomposer.org/installer | php + - php composer.phar require --no-update symfony/symfony=$SYMFONY_VERSION + - php composer.phar install --dev --prefer-source + +script: phpunit -v 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