Update core 8.3.0
This commit is contained in:
parent
da7a7918f8
commit
cd7a898e66
6144 changed files with 132297 additions and 87747 deletions
|
@ -5,25 +5,22 @@ namespace Drupal\Tests;
|
|||
use Behat\Mink\Driver\GoutteDriver;
|
||||
use Behat\Mink\Element\Element;
|
||||
use Behat\Mink\Mink;
|
||||
use Behat\Mink\Selector\SelectorsHandler;
|
||||
use Behat\Mink\Session;
|
||||
use Drupal\Component\FileCache\FileCacheFactory;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Config\Testing\ConfigSchemaChecker;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\DrupalKernel;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Session\AnonymousUserSession;
|
||||
use Drupal\Core\Session\UserSession;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\Core\Test\FunctionalTestSetupTrait;
|
||||
use Drupal\Core\Test\TestRunnerKernel;
|
||||
use Drupal\Core\Test\TestSetupTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Test\TestDatabase;
|
||||
use Drupal\Core\Utility\Error;
|
||||
use Drupal\FunctionalTests\AssertLegacyTrait;
|
||||
use Drupal\simpletest\AssertHelperTrait;
|
||||
use Drupal\simpletest\ContentTypeCreationTrait;
|
||||
|
@ -32,6 +29,8 @@ use Drupal\simpletest\NodeCreationTrait;
|
|||
use Drupal\simpletest\UserCreationTrait;
|
||||
use Symfony\Component\CssSelector\CssSelectorConverter;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Provides a test case for functional Drupal tests.
|
||||
|
@ -43,6 +42,9 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
* @ingroup testing
|
||||
*/
|
||||
abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
use FunctionalTestSetupTrait;
|
||||
use TestSetupTrait;
|
||||
use AssertHelperTrait;
|
||||
use BlockCreationTrait {
|
||||
placeBlock as drupalPlaceBlock;
|
||||
|
@ -62,20 +64,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
createRole as drupalCreateRole;
|
||||
createUser as drupalCreateUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class loader.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $classLoader;
|
||||
|
||||
/**
|
||||
* The site directory of this test run.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $siteDirectory;
|
||||
use XdebugRequestTrait;
|
||||
|
||||
/**
|
||||
* The database prefix of this test run.
|
||||
|
@ -84,13 +73,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
protected $databasePrefix;
|
||||
|
||||
/**
|
||||
* The site directory of the original parent site.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $originalSiteDirectory;
|
||||
|
||||
/**
|
||||
* Time limit in seconds for the test.
|
||||
*
|
||||
|
@ -98,37 +80,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
protected $timeLimit = 500;
|
||||
|
||||
/**
|
||||
* The public file directory for the test environment.
|
||||
*
|
||||
* This is set in BrowserTestBase::prepareEnvironment().
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $publicFilesDirectory;
|
||||
|
||||
/**
|
||||
* The private file directory for the test environment.
|
||||
*
|
||||
* This is set in BrowserTestBase::prepareEnvironment().
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $privateFilesDirectory;
|
||||
|
||||
/**
|
||||
* The temp file directory for the test environment.
|
||||
*
|
||||
* This is set in BrowserTestBase::prepareEnvironment(). This value has to
|
||||
* match the temporary directory created in install_base_system() for test
|
||||
* installs.
|
||||
*
|
||||
* @see install_base_system()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tempFilesDirectory;
|
||||
|
||||
/**
|
||||
* The translation file directory for the test environment.
|
||||
*
|
||||
|
@ -138,20 +89,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
protected $translationFilesDirectory;
|
||||
|
||||
/**
|
||||
* The DrupalKernel instance used in the test.
|
||||
*
|
||||
* @var \Drupal\Core\DrupalKernel
|
||||
*/
|
||||
protected $kernel;
|
||||
|
||||
/**
|
||||
* The dependency injection container used in the test.
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* The config importer that can be used in a test.
|
||||
*
|
||||
|
@ -159,15 +96,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Set to TRUE to strict check all configuration saved.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $strictConfigSchema = TRUE;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
|
@ -181,22 +109,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
protected static $modules = [];
|
||||
|
||||
/**
|
||||
* An array of config object names that are excluded from schema checking.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $configSchemaCheckerExclusions = array(
|
||||
// Following are used to test lack of or partial schema. Where partial
|
||||
// schema is provided, that is explicitly tested in specific tests.
|
||||
'config_schema_test.noschema',
|
||||
'config_schema_test.someschema',
|
||||
'config_schema_test.schema_data_types',
|
||||
'config_schema_test.no_schema_data_types',
|
||||
// Used to test application of schema to filtering of configuration.
|
||||
'config_test.dynamic.system',
|
||||
);
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
|
@ -211,20 +123,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
*/
|
||||
protected $loggedInUser = FALSE;
|
||||
|
||||
/**
|
||||
* The root user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\UserSession
|
||||
*/
|
||||
protected $rootUser;
|
||||
|
||||
/**
|
||||
* The config directories used in this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $configDirectories = array();
|
||||
|
||||
/**
|
||||
* An array of custom translations suitable for drupal_rewrite_settings().
|
||||
*
|
||||
|
@ -356,13 +254,26 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
|
||||
if ($driver instanceof GoutteDriver) {
|
||||
// Turn off curl timeout. Having a timeout is not a problem in a normal
|
||||
// test running, but it is a problem when debugging.
|
||||
// test running, but it is a problem when debugging. Also, disable SSL
|
||||
// peer verification so that testing under HTTPS always works.
|
||||
/** @var \GuzzleHttp\Client $client */
|
||||
$client = $this->container->get('http_client_factory')->fromOptions(['timeout' => NULL]);
|
||||
$client = $this->container->get('http_client_factory')->fromOptions([
|
||||
'timeout' => NULL,
|
||||
'verify' => FALSE,
|
||||
]);
|
||||
|
||||
// Inject a Guzzle middleware to generate debug output for every request
|
||||
// performed in the test.
|
||||
$handler_stack = $client->getConfig('handler');
|
||||
$handler_stack->push($this->getResponseLogHandler());
|
||||
|
||||
$driver->getClient()->setClient($client);
|
||||
}
|
||||
|
||||
$session = new Session($driver);
|
||||
$selectors_handler = new SelectorsHandler([
|
||||
'hidden_field_selector' => new HiddenFieldSelector()
|
||||
]);
|
||||
$session = new Session($driver, $selectors_handler);
|
||||
$this->mink = new Mink();
|
||||
$this->mink->registerSession('default', $session);
|
||||
$this->mink->setDefaultSessionName('default');
|
||||
|
@ -418,6 +329,43 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
return $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a Guzzle middleware handler to log every response received.
|
||||
*
|
||||
* @return callable
|
||||
* The callable handler that will do the logging.
|
||||
*/
|
||||
protected function getResponseLogHandler() {
|
||||
return function (callable $handler) {
|
||||
return function (RequestInterface $request, array $options) use ($handler) {
|
||||
return $handler($request, $options)
|
||||
->then(function (ResponseInterface $response) use ($request) {
|
||||
if ($this->htmlOutputEnabled) {
|
||||
|
||||
$caller = $this->getTestMethodCaller();
|
||||
$html_output = 'Called from ' . $caller['function'] . ' line ' . $caller['line'];
|
||||
$html_output .= '<hr />' . $request->getMethod() . ' request to: ' . $request->getUri();
|
||||
|
||||
// On redirect responses (status code starting with '3') we need
|
||||
// to remove the meta tag that would do a browser refresh. We
|
||||
// don't want to redirect developers away when they look at the
|
||||
// debug output file in their browser.
|
||||
$body = $response->getBody();
|
||||
$status_code = (string) $response->getStatusCode();
|
||||
if ($status_code[0] === '3') {
|
||||
$body = preg_replace('#<meta http-equiv="refresh" content=.+/>#', '', $body, 1);
|
||||
}
|
||||
$html_output .= '<hr />' . $body;
|
||||
$html_output .= $this->formatHtmlOutputHeaders($response->getHeaders());
|
||||
|
||||
$this->htmlOutput($html_output);
|
||||
}
|
||||
return $response;
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers additional Mink sessions.
|
||||
*
|
||||
|
@ -482,29 +430,10 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
// 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));
|
||||
}
|
||||
$cookies = $this->extractCookiesFromRequest(\Drupal::request());
|
||||
foreach ($cookies as $cookie_name => $values) {
|
||||
foreach ($values as $value) {
|
||||
$session->setCookie($cookie_name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,7 +493,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
}
|
||||
|
||||
// Delete test site directory.
|
||||
file_unmanaged_delete_recursive($this->siteDirectory, array($this, 'filePreDeleteCallback'));
|
||||
file_unmanaged_delete_recursive($this->siteDirectory, [$this, 'filePreDeleteCallback']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -643,7 +572,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
* @return string
|
||||
* An absolute URL stsring.
|
||||
*/
|
||||
protected function buildUrl($path, array $options = array()) {
|
||||
protected function buildUrl($path, array $options = []) {
|
||||
if ($path instanceof Url) {
|
||||
$url_options = $path->getOptions();
|
||||
$options = $url_options + $options;
|
||||
|
@ -691,7 +620,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
* @return string
|
||||
* The retrieved HTML string, also available as $this->getRawContent()
|
||||
*/
|
||||
protected function drupalGet($path, array $options = array(), array $headers = array()) {
|
||||
protected function drupalGet($path, array $options = [], array $headers = []) {
|
||||
$options['absolute'] = TRUE;
|
||||
$url = $this->buildUrl($path, $options);
|
||||
|
||||
|
@ -708,7 +637,9 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
// Ensure that any changes to variables in the other thread are picked up.
|
||||
$this->refreshVariables();
|
||||
|
||||
if ($this->htmlOutputEnabled) {
|
||||
// Log only for JavascriptTestBase tests because for Goutte we log with
|
||||
// ::getResponseLogHandler.
|
||||
if ($this->htmlOutputEnabled && !($this->getSession()->getDriver() instanceof GoutteDriver)) {
|
||||
$html_output = 'GET request to: ' . $url .
|
||||
'<hr />Ending URL: ' . $this->getSession()->getCurrentUrl();
|
||||
$html_output .= '<hr />' . $out;
|
||||
|
@ -782,16 +713,16 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
$this->drupalLogout();
|
||||
}
|
||||
|
||||
$this->drupalGet('user');
|
||||
$this->drupalGet('user/login');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->submitForm(array(
|
||||
$this->submitForm([
|
||||
'name' => $account->getUsername(),
|
||||
'pass' => $account->passRaw,
|
||||
), t('Log in'));
|
||||
], t('Log in'));
|
||||
|
||||
// @see BrowserTestBase::drupalUserIsLoggedIn()
|
||||
$account->sessionId = $this->getSession()->getCookie($this->getSessionName());
|
||||
$this->assertTrue($this->drupalUserIsLoggedIn($account), SafeMarkup::format('User %name successfully logged in.', array('name' => $account->getUsername())));
|
||||
$this->assertTrue($this->drupalUserIsLoggedIn($account), new FormattableMarkup('User %name successfully logged in.', ['%name' => $account->getAccountName()]));
|
||||
|
||||
$this->loggedInUser = $account;
|
||||
$this->container->get('current_user')->setAccount($account);
|
||||
|
@ -807,7 +738,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
// 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')));
|
||||
$this->drupalGet('user/logout', ['query' => ['destination' => 'user']]);
|
||||
$assert_session->statusCodeEquals(200);
|
||||
$assert_session->fieldExists('name');
|
||||
$assert_session->fieldExists('pass');
|
||||
|
@ -878,7 +809,10 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
|
||||
// Ensure that any changes to variables in the other thread are picked up.
|
||||
$this->refreshVariables();
|
||||
if ($this->htmlOutputEnabled) {
|
||||
|
||||
// Log only for JavascriptTestBase tests because for Goutte we log with
|
||||
// ::getResponseLogHandler.
|
||||
if ($this->htmlOutputEnabled && !($this->getSession()->getDriver() instanceof GoutteDriver)) {
|
||||
$out = $this->getSession()->getPage()->getContent();
|
||||
$html_output = 'POST request to: ' . $action .
|
||||
'<hr />Ending URL: ' . $this->getSession()->getCurrentUrl();
|
||||
|
@ -886,6 +820,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
$html_output .= $this->getHtmlOutputHeaders();
|
||||
$this->htmlOutput($html_output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -931,6 +866,8 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
* $edit = array();
|
||||
* $edit['name[]'] = array('value1', 'value2');
|
||||
* @endcode
|
||||
* @todo change $edit to disallow NULL as a value for Drupal 9.
|
||||
* https://www.drupal.org/node/2802401
|
||||
* @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
|
||||
|
@ -956,11 +893,14 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
* @param array $options
|
||||
* Options to be forwarded to the url generator.
|
||||
*/
|
||||
protected function drupalPostForm($path, array $edit, $submit, array $options = array()) {
|
||||
protected function drupalPostForm($path, $edit, $submit, array $options = []) {
|
||||
if (is_object($submit)) {
|
||||
// Cast MarkupInterface objects to string.
|
||||
$submit = (string) $submit;
|
||||
}
|
||||
if ($edit === NULL) {
|
||||
$edit = [];
|
||||
}
|
||||
if (is_array($edit)) {
|
||||
$edit = $this->castSafeStrings($edit);
|
||||
}
|
||||
|
@ -1001,157 +941,14 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
* Installs Drupal into the Simpletest site.
|
||||
*/
|
||||
public function installDrupal() {
|
||||
// Define information about the user 1 account.
|
||||
$this->rootUser = new UserSession(array(
|
||||
'uid' => 1,
|
||||
'name' => 'admin',
|
||||
'mail' => 'admin@example.com',
|
||||
'passRaw' => $this->randomMachineName(),
|
||||
));
|
||||
|
||||
// The child site derives its session name from the database prefix when
|
||||
// running web tests.
|
||||
$this->generateSessionName($this->databasePrefix);
|
||||
|
||||
// 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');
|
||||
|
||||
// The public file system path is created during installation. Additionally,
|
||||
// during tests:
|
||||
// - The temporary directory is set and created by install_base_system().
|
||||
// - The private file directory is created post install by this method.
|
||||
// @see system_requirements()
|
||||
// @see TestBase::prepareEnvironment()
|
||||
// @see install_base_system()
|
||||
$settings['settings']['file_public_path'] = (object) array(
|
||||
'value' => $this->publicFilesDirectory,
|
||||
'required' => TRUE,
|
||||
);
|
||||
$settings['settings']['file_private_path'] = (object) [
|
||||
'value' => $this->privateFilesDirectory,
|
||||
'required' => TRUE,
|
||||
];
|
||||
$this->writeSettings($settings);
|
||||
// Allow for test-specific overrides.
|
||||
$settings_testing_file = DRUPAL_ROOT . '/' . $this->originalSiteDirectory . '/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->originalSiteDirectory . '/testing.services.yml';
|
||||
if (!file_exists($settings_services_file)) {
|
||||
// Otherwise, use the default services as a starting point for overrides.
|
||||
$settings_services_file = DRUPAL_ROOT . '/sites/default/default.services.yml';
|
||||
}
|
||||
// Copy the testing-specific service overrides in place.
|
||||
copy($settings_services_file, $directory . '/services.yml');
|
||||
if ($this->strictConfigSchema) {
|
||||
// Add a listener to validate configuration schema on save.
|
||||
$content = file_get_contents($directory . '/services.yml');
|
||||
$services = Yaml::decode($content);
|
||||
$services['services']['simpletest.config_schema_checker'] = [
|
||||
'class' => ConfigSchemaChecker::class,
|
||||
'arguments' => ['@config.typed', $this->getConfigSchemaExclusions()],
|
||||
'tags' => [['name' => 'event_subscriber']]
|
||||
];
|
||||
file_put_contents($directory . '/services.yml', Yaml::encode($services));
|
||||
}
|
||||
|
||||
// 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(DRUPAL_ROOT, $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(DRUPAL_ROOT, $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);
|
||||
|
||||
// During tests, cacheable responses should get the debugging cacheability
|
||||
// headers by default.
|
||||
$this->setContainerParameter('http.response.debug_cacheability_headers', TRUE);
|
||||
|
||||
$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 accidentally load the parent site.
|
||||
$container = $this->kernel->rebuildContainer();
|
||||
|
||||
$config = $container->get('config.factory');
|
||||
|
||||
// Manually create the private directory.
|
||||
file_prepare_directory($this->privateFilesDirectory, FILE_CREATE_DIRECTORY);
|
||||
|
||||
// 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->getEditable('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://www.drupal.org/node/2259167
|
||||
$config->getEditable('system.logging')
|
||||
->set('error_level', 'verbose')
|
||||
->save();
|
||||
$config->getEditable('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_installer')->install($modules, TRUE);
|
||||
$this->assertTrue($success, SafeMarkup::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. StreamWrapperManagerInterface::getWrappers().
|
||||
// @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);
|
||||
$this->initUserSession();
|
||||
$this->prepareSettings();
|
||||
$this->doInstall();
|
||||
$this->initSettings();
|
||||
$container = $this->initKernel(\Drupal::request());
|
||||
$this->initConfig($container);
|
||||
$this->installModulesFromClassProperty($container);
|
||||
$this->rebuildAll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1168,99 +965,38 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
unset($connection_info['default']['namespace']);
|
||||
unset($connection_info['default']['pdo']);
|
||||
unset($connection_info['default']['init_commands']);
|
||||
$parameters = array(
|
||||
$parameters = [
|
||||
'interactive' => FALSE,
|
||||
'parameters' => array(
|
||||
'parameters' => [
|
||||
'profile' => $this->profile,
|
||||
'langcode' => 'en',
|
||||
),
|
||||
'forms' => array(
|
||||
'install_settings_form' => array(
|
||||
],
|
||||
'forms' => [
|
||||
'install_settings_form' => [
|
||||
'driver' => $driver,
|
||||
$driver => $connection_info['default'],
|
||||
),
|
||||
'install_configure_form' => array(
|
||||
],
|
||||
'install_configure_form' => [
|
||||
'site_name' => 'Drupal',
|
||||
'site_mail' => 'simpletest@example.com',
|
||||
'account' => array(
|
||||
'account' => [
|
||||
'name' => $this->rootUser->name,
|
||||
'mail' => $this->rootUser->getEmail(),
|
||||
'pass' => array(
|
||||
'pass1' => $this->rootUser->passRaw,
|
||||
'pass2' => $this->rootUser->passRaw,
|
||||
),
|
||||
),
|
||||
'pass' => [
|
||||
'pass1' => $this->rootUser->pass_raw,
|
||||
'pass2' => $this->rootUser->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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
'enable_update_status_module' => NULL,
|
||||
'enable_update_status_emails' => 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 by the cookie value of
|
||||
* SIMPLETEST_USER_AGENT by the Mink controlled browser. During early Drupal
|
||||
* bootstrap, the cookie is parsed, and if it matches, all database queries
|
||||
* use the database table prefix that has been generated here.
|
||||
*
|
||||
* @see drupal_valid_test_ua()
|
||||
* @see BrowserTestBase::prepareEnvironment()
|
||||
*/
|
||||
private function prepareDatabasePrefix() {
|
||||
$test_db = new TestDatabase();
|
||||
$this->siteDirectory = $test_db->getTestSitePath();
|
||||
$this->databasePrefix = $test_db->getDatabasePrefix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the database connection to the prefixed one.
|
||||
*
|
||||
* @see BrowserTestBase::prepareEnvironment()
|
||||
*/
|
||||
private function changeDatabasePrefix() {
|
||||
if (empty($this->databasePrefix)) {
|
||||
$this->prepareDatabasePrefix();
|
||||
}
|
||||
|
||||
// If the test is run with argument dburl then use it.
|
||||
$db_url = getenv('SIMPLETEST_DB');
|
||||
if (!empty($db_url)) {
|
||||
$database = Database::convertDbUrlToConnectionInfo($db_url, DRUPAL_ROOT);
|
||||
Database::addConnectionInfo('default', 'default', $database);
|
||||
}
|
||||
|
||||
// Clone the current connection and replace the current prefix.
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
if (is_null($connection_info)) {
|
||||
throw new \InvalidArgumentException('There is no database connection so no tests can be run. You must provide a SIMPLETEST_DB environment variable to run PHPUnit based functional tests outside of run-tests.sh.');
|
||||
}
|
||||
else {
|
||||
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.
|
||||
*
|
||||
|
@ -1282,7 +1018,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
$kernel->prepareLegacyRequest($request);
|
||||
$this->prepareDatabasePrefix();
|
||||
|
||||
$this->originalSiteDirectory = $kernel->findSitePath($request);
|
||||
$this->originalSite = $kernel->findSitePath($request);
|
||||
|
||||
// Create test directory ahead of installation so fatal errors and debug
|
||||
// information can be logged during installation process.
|
||||
|
@ -1327,10 +1063,10 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
drupal_valid_test_ua($this->databasePrefix);
|
||||
|
||||
// Reset settings.
|
||||
new Settings(array(
|
||||
new Settings([
|
||||
// For performance, simply use the database prefix as hash salt.
|
||||
'hash_salt' => $this->databasePrefix,
|
||||
));
|
||||
]);
|
||||
|
||||
drupal_set_time_limit($this->timeLimit);
|
||||
|
||||
|
@ -1343,163 +1079,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
$callbacks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return TestDatabase::getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Mink controlled 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() {
|
||||
// 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);
|
||||
// Ensure the request time is REQUEST_TIME to ensure that API calls
|
||||
// in the test use the right timestamp.
|
||||
$request->server->set('REQUEST_TIME', REQUEST_TIME);
|
||||
$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\BrowserTestBase::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->submitForm() 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.
|
||||
$this->container->get('cache_tags.invalidator')->resetChecksums();
|
||||
// @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.
|
||||
*
|
||||
|
@ -1576,15 +1155,28 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
* HTML output headers.
|
||||
*/
|
||||
protected function getHtmlOutputHeaders() {
|
||||
$headers = array_map(function($headers) {
|
||||
if (is_array($headers)) {
|
||||
return implode(';', array_map('trim', $headers));
|
||||
return $this->formatHtmlOutputHeaders($this->getSession()->getResponseHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats HTTP headers as string for HTML output logging.
|
||||
*
|
||||
* @param array[] $headers
|
||||
* Headers that should be formatted.
|
||||
*
|
||||
* @return string
|
||||
* The formatted HTML string.
|
||||
*/
|
||||
protected function formatHtmlOutputHeaders(array $headers) {
|
||||
$flattened_headers = array_map(function($header) {
|
||||
if (is_array($header)) {
|
||||
return implode(';', array_map('trim', $header));
|
||||
}
|
||||
else {
|
||||
return $headers;
|
||||
return $header;
|
||||
}
|
||||
}, $this->getSession()->getResponseHeaders());
|
||||
return '<hr />Headers: <pre>' . Html::escape(var_export($headers, TRUE)) . '</pre>';
|
||||
}, $headers);
|
||||
return '<hr />Headers: <pre>' . Html::escape(var_export($flattened_headers, TRUE)) . '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1680,6 +1272,19 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
return $this->container->get('config.factory')->getEditable($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all response headers.
|
||||
*
|
||||
* @return array
|
||||
* The HTTP headers values.
|
||||
*
|
||||
* @deprecated Scheduled for removal in Drupal 9.0.0.
|
||||
* Use $this->getSession()->getResponseHeaders() instead.
|
||||
*/
|
||||
protected function drupalGetHeaders() {
|
||||
return $this->getSession()->getResponseHeaders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an HTTP response header.
|
||||
*
|
||||
|
@ -1734,43 +1339,34 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Changes parameters in the services.yml file.
|
||||
* Retrieves the current calling line in the class under test.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the parameter.
|
||||
* @param mixed $value
|
||||
* The value of the parameter.
|
||||
* @return array
|
||||
* An associative array with keys 'file', 'line' and 'function'.
|
||||
*/
|
||||
protected function setContainerParameter($name, $value) {
|
||||
$filename = $this->siteDirectory . '/services.yml';
|
||||
chmod($filename, 0666);
|
||||
|
||||
$services = Yaml::decode(file_get_contents($filename));
|
||||
$services['parameters'][$name] = $value;
|
||||
file_put_contents($filename, Yaml::encode($services));
|
||||
|
||||
// Ensure that the cache is deleted for the yaml file loader.
|
||||
$file_cache = FileCacheFactory::get('container_yaml_loader');
|
||||
$file_cache->delete($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the config schema exclusions for this test.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of config object names that are excluded from schema checking.
|
||||
*/
|
||||
protected function getConfigSchemaExclusions() {
|
||||
$class = get_class($this);
|
||||
$exceptions = [];
|
||||
while ($class) {
|
||||
if (property_exists($class, 'configSchemaCheckerExclusions')) {
|
||||
$exceptions = array_merge($exceptions, $class::$configSchemaCheckerExclusions);
|
||||
protected function getTestMethodCaller() {
|
||||
$backtrace = debug_backtrace();
|
||||
// Find the test class that has the test method.
|
||||
while ($caller = Error::getLastCaller($backtrace)) {
|
||||
if (isset($caller['class']) && $caller['class'] === get_class($this)) {
|
||||
break;
|
||||
}
|
||||
$class = get_parent_class($class);
|
||||
// If the test method is implemented by a test class's parent then the
|
||||
// class name of $this will not be part of the backtrace.
|
||||
// In that case we process the backtrace until the caller is not a
|
||||
// subclass of $this and return the previous caller.
|
||||
if (isset($last_caller) && (!isset($caller['class']) || !is_subclass_of($this, $caller['class']))) {
|
||||
// Return the last caller since that has to be the test class.
|
||||
$caller = $last_caller;
|
||||
break;
|
||||
}
|
||||
// Otherwise we have not reached our test class yet: save the last caller
|
||||
// and remove an element from to backtrace to process the next call.
|
||||
$last_caller = $caller;
|
||||
array_shift($backtrace);
|
||||
}
|
||||
// Filter out any duplicates.
|
||||
return array_unique($exceptions);
|
||||
|
||||
return $caller;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Reference in a new issue