use Drupal\Core\Database\Database;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AnonymousUserSession;
-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\Utility\Error;
use Drupal\FunctionalTests\AssertLegacyTrait;
-use Drupal\simpletest\AssertHelperTrait;
-use Drupal\simpletest\ContentTypeCreationTrait;
-use Drupal\simpletest\BlockCreationTrait;
-use Drupal\simpletest\NodeCreationTrait;
-use Drupal\simpletest\UserCreationTrait;
-use Symfony\Component\CssSelector\CssSelectorConverter;
-use Symfony\Component\HttpFoundation\Request;
+use Drupal\Tests\block\Traits\BlockCreationTrait;
+use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
+use Drupal\Tests\node\Traits\NodeCreationTrait;
+use Drupal\Tests\user\Traits\UserCreationTrait;
+use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
+use Symfony\Component\CssSelector\CssSelectorConverter;
/**
* Provides a test case for functional Drupal tests.
* Drupal\Tests\yourmodule\Functional namespace and live in the
* modules/yourmodule/tests/src/Functional directory.
*
+ * Tests extending this base class should only translate text when testing
+ * translation functionality. For example, avoid wrapping test text with t()
+ * or TranslatableMarkup().
+ *
* @ingroup testing
*/
-abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
+abstract class BrowserTestBase extends TestCase {
use FunctionalTestSetupTrait;
use TestSetupTrait;
createContentType as drupalCreateContentType;
}
use ConfigTestTrait;
+ use TestRequirementsTrait;
use UserCreationTrait {
createRole as drupalCreateRole;
createUser as drupalCreateUser;
}
use XdebugRequestTrait;
+ use PhpunitCompatibilityTrait;
/**
* The database prefix of this test run.
*/
protected $metaRefreshCount = 0;
+ /**
+ * The app root.
+ *
+ * @var string
+ */
+ protected $root;
+
+ /**
+ * The original container.
+ *
+ * Move this to \Drupal\Core\Test\FunctionalTestSetupTrait once TestBase no
+ * longer provides the same value.
+ *
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $originalContainer;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct($name = NULL, array $data = [], $dataName = '') {
+ parent::__construct($name, $data, $dataName);
+
+ $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
+ }
+
/**
* Initializes Mink sessions.
*/
* When provided default Mink driver class can't be instantiated.
*/
protected function getDefaultDriverInstance() {
- // Get default driver params from environment if availables.
- if ($arg_json = getenv('MINK_DRIVER_ARGS')) {
- $this->minkDefaultDriverArgs = json_decode($arg_json);
+ // Get default driver params from environment if available.
+ if ($arg_json = $this->getMinkDriverArgs()) {
+ $this->minkDefaultDriverArgs = json_decode($arg_json, TRUE);
}
- // Get and check default driver class from environment if availables.
+ // Get and check default driver class from environment if available.
if ($minkDriverClass = getenv('MINK_DRIVER_CLASS')) {
if (class_exists($minkDriverClass)) {
$this->minkDefaultDriverClass = $minkDriverClass;
return $driver;
}
+ /**
+ * Creates the directory to store browser output.
+ *
+ * Creates the directory to store browser output in if a file to write
+ * URLs to has been created by \Drupal\Tests\Listeners\HtmlOutputPrinter.
+ */
+ protected function initBrowserOutputFile() {
+ $browser_output_file = getenv('BROWSERTEST_OUTPUT_FILE');
+ $this->htmlOutputEnabled = is_file($browser_output_file);
+ if ($this->htmlOutputEnabled) {
+ $this->htmlOutputFile = $browser_output_file;
+ $this->htmlOutputClassName = str_replace("\\", "_", get_called_class());
+ $this->htmlOutputDirectory = DRUPAL_ROOT . '/sites/simpletest/browser_output';
+ if (file_prepare_directory($this->htmlOutputDirectory, FILE_CREATE_DIRECTORY) && !file_exists($this->htmlOutputDirectory . '/.htaccess')) {
+ file_put_contents($this->htmlOutputDirectory . '/.htaccess', "<IfModule mod_expires.c>\nExpiresActive Off\n</IfModule>\n");
+ }
+ $this->htmlOutputCounterStorage = $this->htmlOutputDirectory . '/' . $this->htmlOutputClassName . '.counter';
+ $this->htmlOutputTestId = str_replace('sites/simpletest/', '', $this->siteDirectory);
+ if (is_file($this->htmlOutputCounterStorage)) {
+ $this->htmlOutputCounter = max(1, (int) file_get_contents($this->htmlOutputCounterStorage)) + 1;
+ }
+ }
+ }
+
+ /**
+ * Get the Mink driver args from an environment variable, if it is set. Can
+ * be overridden in a derived class so it is possible to use a different
+ * value for a subset of tests, e.g. the JavaScript tests.
+ *
+ * @return string|false
+ * The JSON-encoded argument string. False if it is not set.
+ */
+ protected function getMinkDriverArgs() {
+ return getenv('MINK_DRIVER_ARGS');
+ }
+
/**
* Provides a Guzzle middleware handler to log every response received.
*
* {@inheritdoc}
*/
protected 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 \Exception(
- 'You must provide a SIMPLETEST_BASE_URL environment variable to run some PHPUnit based functional tests.'
- );
+ // Installing Drupal creates 1000s of objects. Garbage collection of these
+ // objects is expensive. This appears to be causing random segmentation
+ // faults in PHP 5.x due to https://bugs.php.net/bug.php?id=72286. Once
+ // Drupal is installed is rebuilt, garbage collection is re-enabled.
+ $disable_gc = version_compare(PHP_VERSION, '7', '<') && gc_enabled();
+ if ($disable_gc) {
+ gc_collect_cycles();
+ gc_disable();
}
+ parent::setUp();
- // 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;
-
- $this->baseUrl = $base_url;
-
- // 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';
+ $this->setupBaseUrl();
// Install Drupal test site.
$this->prepareEnvironment();
}
}
- // Creates the directory to store browser output in if a file to write
- // URLs to has been created by \Drupal\Tests\Listeners\HtmlOutputPrinter.
- $browser_output_file = getenv('BROWSERTEST_OUTPUT_FILE');
- $this->htmlOutputEnabled = is_file($browser_output_file);
- if ($this->htmlOutputEnabled) {
- $this->htmlOutputFile = $browser_output_file;
- $this->htmlOutputClassName = str_replace("\\", "_", get_called_class());
- $this->htmlOutputDirectory = DRUPAL_ROOT . '/sites/simpletest/browser_output';
- if (file_prepare_directory($this->htmlOutputDirectory, FILE_CREATE_DIRECTORY) && !file_exists($this->htmlOutputDirectory . '/.htaccess')) {
- file_put_contents($this->htmlOutputDirectory . '/.htaccess', "<IfModule mod_expires.c>\nExpiresActive Off\n</IfModule>\n");
- }
- $this->htmlOutputCounterStorage = $this->htmlOutputDirectory . '/' . $this->htmlOutputClassName . '.counter';
- $this->htmlOutputTestId = str_replace('sites/simpletest/', '', $this->siteDirectory);
- if (is_file($this->htmlOutputCounterStorage)) {
- $this->htmlOutputCounter = max(1, (int) file_get_contents($this->htmlOutputCounterStorage)) + 1;
- }
+ // Set up the browser test output file.
+ $this->initBrowserOutputFile();
+ // If garbage collection was disabled prior to rebuilding container,
+ // re-enable it.
+ if ($disable_gc) {
+ gc_enable();
}
+
+ // Ensure that the test is not marked as risky because of no assertions. In
+ // PHPUnit 6 tests that only make assertions using $this->assertSession()
+ // can be marked as risky.
+ $this->addToAssertionCount(1);
}
/**
}
$this->drupalGet('user/login');
- $this->assertSession()->statusCodeEquals(200);
$this->submitForm([
'name' => $account->getUsername(),
'pass' => $account->passRaw,
// screen.
$assert_session = $this->assertSession();
$this->drupalGet('user/logout', ['query' => ['destination' => 'user']]);
- $assert_session->statusCodeEquals(200);
$assert_session->fieldExists('name');
$assert_session->fieldExists('pass');
* 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.
+ * 'Save'. The processing of the request depends on this value. For example,
+ * a form may have one button with the value 'Save' and another button with
+ * the value '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
* @code
* // First step in form.
* $edit = array(...);
- * $this->drupalPostForm('some_url', $edit, t('Save'));
+ * $this->drupalPostForm('some_url', $edit, 'Save');
*
* // Second step in form.
* $edit = array(...);
- * $this->drupalPostForm(NULL, $edit, t('Save'));
+ * $this->drupalPostForm(NULL, $edit, 'Save');
* @endcode
* @param array $edit
* Field data in an associative array. Changes the current input fields
* 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
- * 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.
+ * 'Save'. The processing of the request depends on this value. For example,
+ * a form may have one button with the value 'Save' and another button with
+ * the value 'Delete', and execute different code depending on which one is
+ * clicked.
*
* This function can also be called to emulate an Ajax submission. In this
* case, this value needs to be an array with the following keys:
* POST data.
* @param array $options
* Options to be forwarded to the url generator.
+ *
+ * @return string
+ * (deprecated) The response content after submit form. It is necessary for
+ * backwards compatibility and will be removed before Drupal 9.0. You should
+ * just use the webAssert object for your assertions.
*/
protected function drupalPostForm($path, $edit, $submit, array $options = []) {
if (is_object($submit)) {
}
$this->submitForm($edit, $submit);
+
+ return $this->getSession()->getPage()->getContent();
}
/**
$this->rebuildAll();
}
- /**
- * 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 = [
- 'interactive' => FALSE,
- 'parameters' => [
- 'profile' => $this->profile,
- 'langcode' => 'en',
- ],
- 'forms' => [
- 'install_settings_form' => [
- 'driver' => $driver,
- $driver => $connection_info['default'],
- ],
- 'install_configure_form' => [
- 'site_name' => 'Drupal',
- 'site_mail' => 'simpletest@example.com',
- 'account' => [
- 'name' => $this->rootUser->name,
- 'mail' => $this->rootUser->getEmail(),
- '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.
- 'enable_update_status_module' => NULL,
- 'enable_update_status_emails' => NULL,
- ],
- ],
- ];
- return $parameters;
- }
-
- /**
- * Prepares the current environment for running the test.
- *
- * 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
- * BrowserTestBase::setUp() (multiple invocations for the same test would have
- * unpredictable consequences) and it must not be callable or overridable by
- * test classes.
- */
- protected function prepareEnvironment() {
- // Bootstrap Drupal so we can use Drupal's built in functions.
- $this->classLoader = require __DIR__ . '/../../../../autoload.php';
- $request = Request::createFromGlobals();
- $kernel = TestRunnerKernel::createFromRequest($request, $this->classLoader);
- // TestRunnerKernel expects the working directory to be DRUPAL_ROOT.
- chdir(DRUPAL_ROOT);
- $kernel->prepareLegacyRequest($request);
- $this->prepareDatabasePrefix();
-
- $this->originalSite = $kernel->findSitePath($request);
-
- // 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->publicFilesDirectory = $this->siteDirectory . '/files';
- $this->privateFilesDirectory = $this->siteDirectory . '/private';
- $this->tempFilesDirectory = $this->siteDirectory . '/temp';
- $this->translationFilesDirectory = $this->siteDirectory . '/translations';
-
- // Ensure the configImporter is refreshed for each test.
- $this->configImporter = NULL;
-
- // Unregister all custom stream wrappers of the parent site.
- $wrappers = \Drupal::service('stream_wrapper_manager')->getWrappers(StreamWrapperInterface::ALL);
- foreach ($wrappers as $scheme => $info) {
- stream_wrapper_unregister($scheme);
- }
-
- // Reset statics.
- drupal_static_reset();
-
- // Ensure there is no service container.
- $this->container = NULL;
- \Drupal::unsetContainer();
-
- // 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);
-
- // Reset settings.
- new Settings([
- // For performance, simply use the database prefix as hash salt.
- 'hash_salt' => $this->databasePrefix,
- ]);
-
- drupal_set_time_limit($this->timeLimit);
-
- // Save and clean the shutdown callbacks array because it is static cached
- // and will be changed by the test run. Otherwise it will contain callbacks
- // from both environments and the testing environment will try to call the
- // handlers defined by the original one.
- $callbacks = &drupal_register_shutdown_function();
- $this->originalShutdownCallbacks = $callbacks;
- $callbacks = [];
- }
-
/**
* Returns whether a given user account is logged in.
*
* The formatted HTML string.
*/
protected function formatHtmlOutputHeaders(array $headers) {
- $flattened_headers = array_map(function($header) {
+ $flattened_headers = array_map(function ($header) {
if (is_array($header)) {
return implode(';', array_map('trim', $header));
}
* Checks for meta refresh tag and if found call drupalGet() recursively.
*
* This function looks for the http-equiv attribute to be set to "Refresh" and
- * is case-sensitive.
+ * is case-insensitive.
*
* @return string|false
* Either the new page content or FALSE.
*/
protected function checkForMetaRefresh() {
- $refresh = $this->cssSelect('meta[http-equiv="Refresh"]');
+ $refresh = $this->cssSelect('meta[http-equiv="Refresh"], meta[http-equiv="refresh"]');
if (!empty($refresh) && (!isset($this->maximumMetaRefreshCount) || $this->metaRefreshCount < $this->maximumMetaRefreshCount)) {
// Parse the content attribute of the meta tag for the format:
// "[delay]: URL=[page_to_redirect_to]".