Update core 8.3.0

This commit is contained in:
Rob Davies 2017-04-13 15:53:35 +01:00
parent da7a7918f8
commit cd7a898e66
6144 changed files with 132297 additions and 87747 deletions

View file

@ -0,0 +1,48 @@
<?php
namespace Drupal\Tests\system\Functional\Bootstrap;
use Drupal\Tests\BrowserTestBase;
/**
* Tests drupal_set_message() and related functions.
*
* @group Bootstrap
*/
class DrupalSetMessageTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system_test'];
/**
* Tests drupal_set_message().
*/
public function testDrupalSetMessage() {
// The page at system-test/drupal-set-message sets two messages and then
// removes the first before it is displayed.
$this->drupalGet('system-test/drupal-set-message');
$this->assertNoText('First message (removed).');
$this->assertRaw(t('Second message with <em>markup!</em> (not removed).'));
// Ensure duplicate messages are handled as expected.
$this->assertUniqueText('Non Duplicated message');
$this->assertNoUniqueText('Duplicated message');
// Ensure Markup objects are rendered as expected.
$this->assertRaw('Markup with <em>markup!</em>');
$this->assertUniqueText('Markup with markup!');
$this->assertRaw('Markup2 with <em>markup!</em>');
// Ensure when the same message is of different types it is not duplicated.
$this->assertUniqueText('Non duplicate Markup / string.');
$this->assertNoUniqueText('Duplicate Markup / string.');
// Ensure that strings that are not marked as safe are escaped.
$this->assertEscaped('<em>This<span>markup will be</span> escaped</em>.');
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace Drupal\Tests\system\Functional\Cache;
use Drupal\Tests\BrowserTestBase;
/**
* Provides helper methods for cache tests.
*/
abstract class CacheTestBase extends BrowserTestBase {
protected $defaultBin = 'render';
protected $defaultCid = 'test_temporary';
protected $defaultValue = 'CacheTest';
/**
* Checks whether or not a cache entry exists.
*
* @param $cid
* The cache id.
* @param $var
* The variable the cache should contain.
* @param $bin
* The bin the cache item was stored in.
* @return
* TRUE on pass, FALSE on fail.
*/
protected function checkCacheExists($cid, $var, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->defaultBin;
}
$cached = \Drupal::cache($bin)->get($cid);
return isset($cached->data) && $cached->data == $var;
}
/**
* Asserts that a cache entry exists.
*
* @param $message
* Message to display.
* @param $var
* The variable the cache should contain.
* @param $cid
* The cache id.
* @param $bin
* The bin the cache item was stored in.
*/
protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->defaultBin;
}
if ($cid == NULL) {
$cid = $this->defaultCid;
}
if ($var == NULL) {
$var = $this->defaultValue;
}
$this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
}
/**
* Asserts that a cache entry has been removed.
*
* @param $message
* Message to display.
* @param $cid
* The cache id.
* @param $bin
* The bin the cache item was stored in.
*/
public function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->defaultBin;
}
if ($cid == NULL) {
$cid = $this->defaultCid;
}
$cached = \Drupal::cache($bin)->get($cid);
$this->assertFalse($cached, $message);
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Drupal\Tests\system\Functional\Cache;
/**
* Tests our clearing is done the proper way.
*
* @group Cache
*/
use Drupal\Core\Cache\Cache;
class ClearTest extends CacheTestBase {
protected function setUp() {
$this->defaultBin = 'render';
$this->defaultValue = $this->randomMachineName(10);
parent::setUp();
}
/**
* Tests drupal_flush_all_caches().
*/
public function testFlushAllCaches() {
// Create cache entries for each flushed cache bin.
$bins = Cache::getBins();
$this->assertTrue($bins, 'Cache::getBins() returned bins to flush.');
foreach ($bins as $bin => $cache_backend) {
$cid = 'test_cid_clear' . $bin;
$cache_backend->set($cid, $this->defaultValue);
}
// Remove all caches then make sure that they are cleared.
drupal_flush_all_caches();
foreach ($bins as $bin => $cache_backend) {
$cid = 'test_cid_clear' . $bin;
$this->assertFalse($this->checkCacheExists($cid, $this->defaultValue, $bin), format_string('All cache entries removed from @bin.', ['@bin' => $bin]));
}
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace Drupal\Tests\system\Functional\Cache;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
use Drupal\Component\Utility\SafeMarkup;
/**
* Provides helper methods for page cache tags tests.
*/
abstract class PageCacheTagsTestBase extends BrowserTestBase {
/**
* {@inheritdoc}
*
* Always enable header dumping in page cache tags tests, this aids debugging.
*/
protected $dumpHeaders = TRUE;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Enable page caching.
$config = $this->config('system.performance');
$config->set('cache.page.max_age', 3600);
$config->save();
}
/**
* Verify that when loading a given page, it's a page cache hit or miss.
*
* @param \Drupal\Core\Url $url
* The page for this URL will be loaded.
* @param string $hit_or_miss
* 'HIT' if a page cache hit is expected, 'MISS' otherwise.
*
* @param array|false $tags
* When expecting a page cache hit, you may optionally specify an array of
* expected cache tags. While FALSE, the cache tags will not be verified.
*/
protected function verifyPageCache(Url $url, $hit_or_miss, $tags = FALSE) {
$this->drupalGet($url);
$message = SafeMarkup::format('Page cache @hit_or_miss for %path.', ['@hit_or_miss' => $hit_or_miss, '%path' => $url->toString()]);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), $hit_or_miss, $message);
if ($hit_or_miss === 'HIT' && is_array($tags)) {
$absolute_url = $url->setAbsolute()->toString();
$cid_parts = [$absolute_url, 'html'];
$cid = implode(':', $cid_parts);
$cache_entry = \Drupal::cache('render')->get($cid);
sort($cache_entry->tags);
$tags = array_unique($tags);
sort($tags);
$this->assertIdentical($cache_entry->tags, $tags);
}
}
}

View file

@ -0,0 +1,119 @@
<?php
namespace Drupal\Tests\system\Functional\Datetime;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\User;
/**
* Tests DrupalDateTime functionality.
*
* @group Datetime
*/
class DrupalDateTimeTest extends BrowserTestBase {
/**
* Set up required modules.
*/
public static $modules = [];
/**
* Test setup.
*/
protected function setUp() {
parent::setUp();
}
/**
* Test that the AJAX Timezone Callback can deal with various formats.
*/
public function testSystemTimezone() {
$options = [
'query' => [
'date' => 'Tue+Sep+17+2013+21%3A35%3A31+GMT%2B0100+(BST)#',
]
];
// Query the AJAX Timezone Callback with a long-format date.
$response = $this->drupalGet('system/timezone/BST/3600/1', $options);
$this->assertEqual($response, '"Europe\/London"', 'Timezone AJAX callback successfully identifies and responds to a long-format date.');
}
/**
* Test that DrupalDateTime can detect the right timezone to use.
* Test with a variety of less commonly used timezone names to
* help ensure that the system timezone will be different than the
* stated timezones.
*/
public function testDateTimezone() {
$date_string = '2007-01-31 21:00:00';
// Make sure no site timezone has been set.
$this->config('system.date')
->set('timezone.user.configurable', 0)
->set('timezone.default', NULL)
->save();
// Detect the system timezone.
$system_timezone = date_default_timezone_get();
// Create a date object with an unspecified timezone, which should
// end up using the system timezone.
$date = new DrupalDateTime($date_string);
$timezone = $date->getTimezone()->getName();
$this->assertTrue($timezone == $system_timezone, 'DrupalDateTime uses the system timezone when there is no site timezone.');
// Create a date object with a specified timezone.
$date = new DrupalDateTime($date_string, 'America/Yellowknife');
$timezone = $date->getTimezone()->getName();
$this->assertTrue($timezone == 'America/Yellowknife', 'DrupalDateTime uses the specified timezone if provided.');
// Set a site timezone.
$this->config('system.date')->set('timezone.default', 'Europe/Warsaw')->save();
// Create a date object with an unspecified timezone, which should
// end up using the site timezone.
$date = new DrupalDateTime($date_string);
$timezone = $date->getTimezone()->getName();
$this->assertTrue($timezone == 'Europe/Warsaw', 'DrupalDateTime uses the site timezone if provided.');
// Create user.
$this->config('system.date')->set('timezone.user.configurable', 1)->save();
$test_user = $this->drupalCreateUser([]);
$this->drupalLogin($test_user);
// Set up the user with a different timezone than the site.
$edit = ['mail' => $test_user->getEmail(), 'timezone' => 'Asia/Manila'];
$this->drupalPostForm('user/' . $test_user->id() . '/edit', $edit, t('Save'));
// Reload the user and reset the timezone in AccountProxy::setAccount().
\Drupal::entityManager()->getStorage('user')->resetCache();
$this->container->get('current_user')->setAccount(User::load($test_user->id()));
// Create a date object with an unspecified timezone, which should
// end up using the user timezone.
$date = new DrupalDateTime($date_string);
$timezone = $date->getTimezone()->getName();
$this->assertTrue($timezone == 'Asia/Manila', 'DrupalDateTime uses the user timezone, if configurable timezones are used and it is set.');
}
/**
* Tests the ability to override the time zone in the format method.
*/
public function testTimezoneFormat() {
// Create a date in UTC
$date = DrupalDateTime::createFromTimestamp(87654321, 'UTC');
// Verify that the date format method displays the default time zone.
$this->assertEqual($date->format('Y/m/d H:i:s e'), '1972/10/11 12:25:21 UTC', 'Date has default UTC time zone and correct date/time.');
// Verify that the format method can override the time zone.
$this->assertEqual($date->format('Y/m/d H:i:s e', ['timezone' => 'America/New_York']), '1972/10/11 08:25:21 America/New_York', 'Date displayed overidden time zone and correct date/time');
// Verify that the date format method still displays the default time zone
// for the date object.
$this->assertEqual($date->format('Y/m/d H:i:s e'), '1972/10/11 12:25:21 UTC', 'Date still has default UTC time zone and correct date/time');
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\system\Functional\DrupalKernel;
use Drupal\Tests\BrowserTestBase;
/**
* Tests content negotiation.
*
* @group DrupalKernel
*/
class ContentNegotiationTest extends BrowserTestBase {
/**
* Verifies HTML responses for bogus Accept headers.
*
* Drupal does not fully support older browsers, but a page output is still
* expected.
*
* @see https://www.drupal.org/node/1716790
*/
public function testBogusAcceptHeader() {
$tests = [
// See https://bugs.webkit.org/show_bug.cgi?id=27267.
'Firefox 3.5 (2009)' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'IE8 (2009)' => 'image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, */*',
'Opera (2009)' => 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
'Chrome (2009)' => 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
// See https://github.com/symfony/symfony/pull/564.
'Firefox 3.6 (2010)' => 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8',
'Safari (2010)' => '*/*',
'Opera (2010)' => 'image/jpeg,image/gif,image/x-xbitmap,text/html,image/webp,image/png,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.1',
// See https://www.drupal.org/node/1716790.
'Safari (2010), iOS 4.2.1 (2012)' => 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
'Android #1 (2012)' => 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
'Android #2 (2012)' => 'text/xml,text/html,application/xhtml+xml,image/png,text/plain,*/*;q=0.8',
];
foreach ($tests as $case => $header) {
$this->drupalGet('', [], ['Accept: ' . $header]);
$this->assertNoText('Unsupported Media Type', '"Unsupported Media Type" not found for ' . $case);
$this->assertText(t('Log in'), '"Log in" found for ' . $case);
}
}
}

View file

@ -0,0 +1,234 @@
<?php
namespace Drupal\Tests\system\Functional\Entity;
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
use Drupal\filter\Entity\FilterFormat;
use Drupal\image\Entity\ImageStyle;
use Drupal\search\Entity\SearchPage;
use Drupal\Tests\BrowserTestBase;
use Drupal\system\Entity\Action;
/**
* Tests ConfigEntity importing.
*
* @group Entity
*/
class ConfigEntityImportTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['action', 'block', 'filter', 'image', 'search', 'search_extra_type'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
}
/**
* Runs test methods for each module within a single test run.
*/
public function testConfigUpdateImport() {
$this->doActionUpdate();
$this->doBlockUpdate();
$this->doFilterFormatUpdate();
$this->doImageStyleUpdate();
$this->doSearchPageUpdate();
}
/**
* Tests updating a action during import.
*/
protected function doActionUpdate() {
// Create a test action with a known label.
$name = 'system.action.apple';
$entity = Action::create([
'id' => 'apple',
'plugin' => 'action_message_action',
]);
$entity->save();
$this->checkSinglePluginConfigSync($entity, 'configuration', 'message', '');
// Read the existing data, and prepare an altered version in sync.
$custom_data = $original_data = $this->container->get('config.storage')->read($name);
$custom_data['configuration']['message'] = 'Granny Smith';
$this->assertConfigUpdateImport($name, $original_data, $custom_data);
}
/**
* Tests updating a block during import.
*/
protected function doBlockUpdate() {
// Create a test block with a known label.
$name = 'block.block.apple';
$block = $this->drupalPlaceBlock('system_powered_by_block', [
'id' => 'apple',
'label' => 'Red Delicious',
]);
$this->checkSinglePluginConfigSync($block, 'settings', 'label', 'Red Delicious');
// Read the existing data, and prepare an altered version in sync.
$custom_data = $original_data = $this->container->get('config.storage')->read($name);
$custom_data['settings']['label'] = 'Granny Smith';
$this->assertConfigUpdateImport($name, $original_data, $custom_data);
}
/**
* Tests updating a filter format during import.
*/
protected function doFilterFormatUpdate() {
// Create a test filter format with a known label.
$name = 'filter.format.plain_text';
/** @var $entity \Drupal\filter\Entity\FilterFormat */
$entity = FilterFormat::load('plain_text');
$plugin_collection = $entity->getPluginCollections()['filters'];
$filters = $entity->get('filters');
$this->assertIdentical(72, $filters['filter_url']['settings']['filter_url_length']);
$filters['filter_url']['settings']['filter_url_length'] = 100;
$entity->set('filters', $filters);
$entity->save();
$this->assertIdentical($filters, $entity->get('filters'));
$this->assertIdentical($filters, $plugin_collection->getConfiguration());
$filters['filter_url']['settings']['filter_url_length'] = -100;
$entity->getPluginCollections()['filters']->setConfiguration($filters);
$entity->save();
$this->assertIdentical($filters, $entity->get('filters'));
$this->assertIdentical($filters, $plugin_collection->getConfiguration());
// Read the existing data, and prepare an altered version in sync.
$custom_data = $original_data = $this->container->get('config.storage')->read($name);
$custom_data['filters']['filter_url']['settings']['filter_url_length'] = 100;
$this->assertConfigUpdateImport($name, $original_data, $custom_data);
}
/**
* Tests updating an image style during import.
*/
protected function doImageStyleUpdate() {
// Create a test image style with a known label.
$name = 'image.style.thumbnail';
/** @var $entity \Drupal\image\Entity\ImageStyle */
$entity = ImageStyle::load('thumbnail');
$plugin_collection = $entity->getPluginCollections()['effects'];
$effects = $entity->get('effects');
$effect_id = key($effects);
$this->assertIdentical(100, $effects[$effect_id]['data']['height']);
$effects[$effect_id]['data']['height'] = 50;
$entity->set('effects', $effects);
$entity->save();
// Ensure the entity and plugin have the correct configuration.
$this->assertIdentical($effects, $entity->get('effects'));
$this->assertIdentical($effects, $plugin_collection->getConfiguration());
$effects[$effect_id]['data']['height'] = -50;
$entity->getPluginCollections()['effects']->setConfiguration($effects);
$entity->save();
// Ensure the entity and plugin have the correct configuration.
$this->assertIdentical($effects, $entity->get('effects'));
$this->assertIdentical($effects, $plugin_collection->getConfiguration());
// Read the existing data, and prepare an altered version in sync.
$custom_data = $original_data = $this->container->get('config.storage')->read($name);
$effect_name = key($original_data['effects']);
$custom_data['effects'][$effect_name]['data']['upscale'] = FALSE;
$this->assertConfigUpdateImport($name, $original_data, $custom_data);
}
/**
* Tests updating a search page during import.
*/
protected function doSearchPageUpdate() {
// Create a test search page with a known label.
$name = 'search.page.apple';
$entity = SearchPage::create([
'id' => 'apple',
'plugin' => 'search_extra_type_search',
]);
$entity->save();
$this->checkSinglePluginConfigSync($entity, 'configuration', 'boost', 'bi');
// Read the existing data, and prepare an altered version in sync.
$custom_data = $original_data = $this->container->get('config.storage')->read($name);
$custom_data['configuration']['boost'] = 'asdf';
$this->assertConfigUpdateImport($name, $original_data, $custom_data);
}
/**
* Tests that a single set of plugin config stays in sync.
*
* @param \Drupal\Core\Entity\EntityWithPluginCollectionInterface $entity
* The entity.
* @param string $config_key
* Where the plugin config is stored.
* @param string $setting_key
* The setting within the plugin config to change.
* @param mixed $expected
* The expected default value of the plugin config setting.
*/
protected function checkSinglePluginConfigSync(EntityWithPluginCollectionInterface $entity, $config_key, $setting_key, $expected) {
$plugin_collection = $entity->getPluginCollections()[$config_key];
$settings = $entity->get($config_key);
// Ensure the default config exists.
$this->assertIdentical($expected, $settings[$setting_key]);
// Change the plugin config by setting it on the entity.
$settings[$setting_key] = $this->randomString();
$entity->set($config_key, $settings);
$entity->save();
$this->assertIdentical($settings, $entity->get($config_key));
$this->assertIdentical($settings, $plugin_collection->getConfiguration());
// Change the plugin config by setting it on the plugin.
$settings[$setting_key] = $this->randomString();
$plugin_collection->setConfiguration($settings);
$entity->save();
$this->assertIdentical($settings, $entity->get($config_key));
$this->assertIdentical($settings, $plugin_collection->getConfiguration());
}
/**
* Asserts that config entities are updated during import.
*
* @param string $name
* The name of the config object.
* @param array $original_data
* The original data stored in the config object.
* @param array $custom_data
* The new data to store in the config object.
*/
public function assertConfigUpdateImport($name, $original_data, $custom_data) {
$this->container->get('config.storage.sync')->write($name, $custom_data);
// Verify the active configuration still returns the default values.
$config = $this->config($name);
$this->assertIdentical($config->get(), $original_data);
// Import.
$this->configImporter()->import();
// Verify the values were updated.
$this->container->get('config.factory')->reset($name);
$config = $this->config($name);
$this->assertIdentical($config->get(), $custom_data);
}
}

View file

@ -0,0 +1,150 @@
<?php
namespace Drupal\Tests\system\Functional\Entity;
use Drupal\entity_test\Entity\EntityTestBundle;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\entity_test\Entity\EntityTestWithBundle;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the /add and /add/{type} controllers.
*
* @group entity
*/
class EntityAddUITest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test'];
/**
* Tests the add page for an entity type using bundle entities.
*/
public function testAddPageWithBundleEntities() {
$admin_user = $this->drupalCreateUser([
'administer entity_test_with_bundle content',
]);
$this->drupalLogin($admin_user);
// Users without create access for bundles do not have access to the add
// page if there are no bundles.
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertResponse(403);
$bundle_admin_user = $this->drupalCreateUser([
'administer entity_test_with_bundle content',
'administer entity_test_bundle content',
]);
$this->drupalLogin($bundle_admin_user);
// No bundles exist, the add bundle message should be present as the user
// has the necessary permissions.
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertText('There is no test entity bundle yet.');
$this->assertLink('Add a new test entity bundle.');
// One bundle exists, confirm redirection to the add-form.
EntityTestBundle::create([
'id' => 'test',
'label' => 'Test label',
'description' => 'My test description',
])->save();
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertUrl('/entity_test_with_bundle/add/test');
// Two bundles exist, confirm both are shown.
EntityTestBundle::create([
'id' => 'test2',
'label' => 'Test2 label',
'description' => 'My test2 description',
])->save();
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertLink('Test label');
$this->assertLink('Test2 label');
$this->assertText('My test description');
$this->assertText('My test2 description');
$this->clickLink('Test2 label');
$this->drupalGet('/entity_test_with_bundle/add/test2');
$this->drupalPostForm(NULL, ['name[0][value]' => 'test name'], t('Save'));
$entity = EntityTestWithBundle::load(1);
$this->assertEqual('test name', $entity->label());
// Create a new user that only has bundle specific permissions.
$user = $this->drupalCreateUser([
'create test entity_test_with_bundle entities',
'create test2 entity_test_with_bundle entities',
]);
$this->drupalLogin($user);
// Create a bundle that the user does not have create permissions for.
EntityTestBundle::create([
'id' => 'test3',
'label' => 'Test3 label',
'description' => 'My test3 description',
])->save();
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertLink('Test label');
$this->assertLink('Test2 label');
$this->assertNoLink('Test3 label');
$this->clickLink(t('Test label'));
$this->assertResponse(200);
// Without any permissions, access must be denied.
$this->drupalLogout();
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertResponse(403);
// Create a new user that has bundle create permissions.
$user = $this->drupalCreateUser([
'administer entity_test_bundle content',
]);
$this->drupalLogin($user);
// User has access to the add page but no bundles are shown because the user
// does not have bundle specific permissions. The add bundle message is
// present as the user has bundle create permissions.
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertNoLink('Test label');
$this->assertNoLink('Test2 label');
$this->assertNoLink('Test3 label');
$this->assertLink('Add a new test entity bundle.');
}
/**
* Tests the add page for an entity type not using bundle entities.
*/
public function testAddPageWithoutBundleEntities() {
$admin_user = $this->drupalCreateUser([
'administer entity_test content',
]);
$this->drupalLogin($admin_user);
entity_test_create_bundle('test', 'Test label', 'entity_test_mul');
// Delete the default bundle, so that we can rely on our own.
entity_test_delete_bundle('entity_test_mul', 'entity_test_mul');
// One bundle exists, confirm redirection to the add-form.
$this->drupalGet('/entity_test_mul/add');
$this->assertUrl('/entity_test_mul/add/test');
// Two bundles exist, confirm both are shown.
entity_test_create_bundle('test2', 'Test2 label', 'entity_test_mul');
$this->drupalGet('/entity_test_mul/add');
$this->assertLink('Test label');
$this->assertLink('Test2 label');
$this->clickLink('Test2 label');
$this->drupalGet('/entity_test_mul/add/test2');
$this->drupalPostForm(NULL, ['name[0][value]' => 'test name'], t('Save'));
$entity = EntityTestMul::load(1);
$this->assertEqual('test name', $entity->label());
}
}

View file

@ -0,0 +1,720 @@
<?php
namespace Drupal\Tests\system\Functional\Entity;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
use Drupal\system\Tests\Cache\PageCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Provides helper methods for Entity cache tags tests.
*/
abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['entity_test', 'field_test'];
/**
* The main entity used for testing.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entity;
/**
* The entity instance referencing the main entity.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $referencingEntity;
/**
* The entity instance not referencing the main entity.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $nonReferencingEntity;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Give anonymous users permission to view test entities, so that we can
// verify the cache tags of cached versions of test entity pages.
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
$user_role->grantPermission('view test entity');
$user_role->save();
// Create an entity.
$this->entity = $this->createEntity();
// If this is an entity with field UI enabled, then add a configurable
// field. We will use this configurable field in later tests to ensure that
// field configuration invalidate render cache entries.
if ($this->entity->getEntityType()->get('field_ui_base_route')) {
// Add field, so we can modify the field storage and field entities to
// verify that changes to those indeed clear cache tags.
FieldStorageConfig::create([
'field_name' => 'configurable_field',
'entity_type' => $this->entity->getEntityTypeId(),
'type' => 'test_field',
'settings' => [],
])->save();
FieldConfig::create([
'entity_type' => $this->entity->getEntityTypeId(),
'bundle' => $this->entity->bundle(),
'field_name' => 'configurable_field',
'label' => 'Configurable field',
'settings' => [],
])->save();
// Reload the entity now that a new field has been added to it.
$storage = $this->container
->get('entity.manager')
->getStorage($this->entity->getEntityTypeId());
$storage->resetCache();
$this->entity = $storage->load($this->entity->id());
}
// Create a referencing and a non-referencing entity.
list(
$this->referencingEntity,
$this->nonReferencingEntity,
) = $this->createReferenceTestEntities($this->entity);
}
/**
* Generates standardized entity cache tags test info.
*
* @param string $entity_type_label
* The label of the entity type whose cache tags to test.
* @param string $group
* The test group.
*
* @return array
*
* @see \Drupal\simpletest\TestBase::getInfo()
*/
protected static function generateStandardizedInfo($entity_type_label, $group) {
return [
'name' => "$entity_type_label entity cache tags",
'description' => "Test the $entity_type_label entity's cache tags.",
'group' => $group,
];
}
/**
* Creates the entity to be tested.
*
* @return \Drupal\Core\Entity\EntityInterface
* The entity to be tested.
*/
abstract protected function createEntity();
/**
* Returns the access cache contexts for the tested entity.
*
* Only list cache contexts that aren't part of the required cache contexts.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be tested, as created by createEntity().
*
* @return string[]
* An array of the additional cache contexts.
*
* @see \Drupal\Core\Entity\EntityAccessControlHandlerInterface
*/
protected function getAccessCacheContextsForEntity(EntityInterface $entity) {
return [];
}
/**
* Returns the additional (non-standard) cache contexts for the tested entity.
*
* Only list cache contexts that aren't part of the required cache contexts.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be tested, as created by createEntity().
*
* @return string[]
* An array of the additional cache contexts.
*
* @see \Drupal\system\Tests\Entity\EntityCacheTagsTestBase::createEntity()
*/
protected function getAdditionalCacheContextsForEntity(EntityInterface $entity) {
return [];
}
/**
* Returns the additional (non-standard) cache tags for the tested entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be tested, as created by createEntity().
* @return array
* An array of the additional cache tags.
*
* @see \Drupal\system\Tests\Entity\EntityCacheTagsTestBase::createEntity()
*/
protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) {
return [];
}
/**
* Returns the additional cache tags for the tested entity's listing by type.
*
* @return string[]
* An array of the additional cache contexts.
*/
protected function getAdditionalCacheContextsForEntityListing() {
return [];
}
/**
* Returns the additional cache tags for the tested entity's listing by type.
*
* Necessary when there are unavoidable default entities of this type, e.g.
* the anonymous and administrator User entities always exist.
*
* @return array
* An array of the additional cache tags.
*/
protected function getAdditionalCacheTagsForEntityListing() {
return [];
}
/**
* Selects the preferred view mode for the given entity type.
*
* Prefers 'full', picks the first one otherwise, and if none are available,
* chooses 'default'.
*/
protected function selectViewMode($entity_type) {
$view_modes = \Drupal::entityManager()
->getStorage('entity_view_mode')
->loadByProperties(['targetEntityType' => $entity_type]);
if (empty($view_modes)) {
return 'default';
}
else {
// Prefer the "full" display mode.
if (isset($view_modes[$entity_type . '.full'])) {
return 'full';
}
else {
$view_modes = array_keys($view_modes);
return substr($view_modes[0], strlen($entity_type) + 1);
}
}
}
/**
* Creates a referencing and a non-referencing entity for testing purposes.
*
* @param \Drupal\Core\Entity\EntityInterface $referenced_entity
* The entity that the referencing entity should reference.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* An array containing a referencing entity and a non-referencing entity.
*/
protected function createReferenceTestEntities($referenced_entity) {
// All referencing entities should be of the type 'entity_test'.
$entity_type = 'entity_test';
// Create a "foo" bundle for the given entity type.
$bundle = 'foo';
entity_test_create_bundle($bundle, NULL, $entity_type);
// Add a field of the given type to the given entity type's "foo" bundle.
$field_name = $referenced_entity->getEntityTypeId() . '_reference';
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'type' => 'entity_reference',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
'settings' => [
'target_type' => $referenced_entity->getEntityTypeId(),
],
])->save();
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'settings' => [
'handler' => 'default',
'handler_settings' => [
'target_bundles' => [
$referenced_entity->bundle() => $referenced_entity->bundle(),
],
'sort' => ['field' => '_none'],
'auto_create' => FALSE,
],
],
])->save();
if (!$this->entity->getEntityType()->hasHandlerClass('view_builder')) {
entity_get_display($entity_type, $bundle, 'full')
->setComponent($field_name, [
'type' => 'entity_reference_label',
])
->save();
}
else {
$referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId());
entity_get_display($entity_type, $bundle, 'full')
->setComponent($field_name, [
'type' => 'entity_reference_entity_view',
'settings' => [
'view_mode' => $referenced_entity_view_mode,
],
])
->save();
}
// Create an entity that does reference the entity being tested.
$label_key = \Drupal::entityManager()->getDefinition($entity_type)->getKey('label');
$referencing_entity = $this->container->get('entity_type.manager')
->getStorage($entity_type)
->create([
$label_key => 'Referencing ' . $entity_type,
'status' => 1,
'type' => $bundle,
$field_name => ['target_id' => $referenced_entity->id()],
]);
$referencing_entity->save();
// Create an entity that does not reference the entity being tested.
$non_referencing_entity = $this->container->get('entity_type.manager')
->getStorage($entity_type)
->create([
$label_key => 'Non-referencing ' . $entity_type,
'status' => 1,
'type' => $bundle,
]);
$non_referencing_entity->save();
return [
$referencing_entity,
$non_referencing_entity,
];
}
/**
* Tests cache tags presence and invalidation of the entity when referenced.
*
* Tests the following cache tags:
* - entity type view cache tag: "<entity type>_view"
* - entity cache tag: "<entity type>:<entity ID>"
* - entity type list cache tag: "<entity type>_list"
* - referencing entity type view cache tag: "<referencing entity type>_view"
* - referencing entity type cache tag: "<referencing entity type>:<referencing entity ID>"
*/
public function testReferencedEntity() {
$entity_type = $this->entity->getEntityTypeId();
$referencing_entity_url = $this->referencingEntity->urlInfo('canonical');
$non_referencing_entity_url = $this->nonReferencingEntity->urlInfo('canonical');
$listing_url = Url::fromRoute('entity.entity_test.collection_referencing_entities', [
'entity_reference_field_name' => $entity_type . '_reference',
'referenced_entity_type' => $entity_type,
'referenced_entity_id' => $this->entity->id(),
]);
$empty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_empty', ['entity_type_id' => $entity_type]);
$nonempty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_labels_alphabetically', ['entity_type_id' => $entity_type]);
// The default cache contexts for rendered entities.
$default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
$entity_cache_contexts = $default_cache_contexts;
$page_cache_contexts = Cache::mergeContexts($default_cache_contexts, ['url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT]);
// Cache tags present on every rendered page.
// 'user.permissions' is a required cache context, and responses that vary
// by this cache context when requested by anonymous users automatically
// also get this cache tag, to ensure correct invalidation.
$page_cache_tags = Cache::mergeTags(['http_response', 'rendered'], ['config:user.role.anonymous']);
// If the block module is used, the Block page display variant is used,
// which adds the block config entity type's list cache tags.
$page_cache_tags = Cache::mergeTags($page_cache_tags, \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list'] : []);
$page_cache_tags_referencing_entity = in_array('user.permissions', $this->getAccessCacheContextsForEntity($this->referencingEntity)) ? ['config:user.role.anonymous'] : [];
$view_cache_tag = [];
if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) {
$view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)
->getCacheTags();
}
$context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($entity_cache_contexts);
$cache_context_tags = $context_metadata->getCacheTags();
// Generate the cache tags for the (non) referencing entities.
$referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
// Includes the main entity's cache tags, since this entity references it.
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->entity->getCacheTags());
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $view_cache_tag);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $cache_context_tags);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']);
$non_referencing_entity_cache_tags = Cache::mergeTags($this->nonReferencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
$non_referencing_entity_cache_tags = Cache::mergeTags($non_referencing_entity_cache_tags, ['rendered']);
// Generate the cache tags for all two possible entity listing paths.
// 1. list cache tag only (listing query has no match)
// 2. list cache tag plus entity cache tag (listing query has a match)
$empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $page_cache_tags);
$nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags());
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $this->getAdditionalCacheTagsForEntityListing($this->entity));
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags);
$this->pass("Test referencing entity.", 'Debug');
$this->verifyPageCache($referencing_entity_url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags);
$expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity);
$this->verifyPageCache($referencing_entity_url, 'HIT', $expected_tags);
// Also verify the existence of an entity render cache entry.
$cache_keys = ['entity_view', 'entity_test', $this->referencingEntity->id(), 'full'];
$cid = $this->createCacheId($cache_keys, $entity_cache_contexts);
$access_cache_contexts = $this->getAccessCacheContextsForEntity($this->entity);
$additional_cache_contexts = $this->getAdditionalCacheContextsForEntity($this->referencingEntity);
$redirected_cid = NULL;
if (count($access_cache_contexts) || count($additional_cache_contexts)) {
$cache_contexts = Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts);
$cache_contexts = Cache::mergeContexts($cache_contexts, $access_cache_contexts);
$redirected_cid = $this->createCacheId($cache_keys, $cache_contexts);
$context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $context_metadata->getCacheTags());
}
$this->verifyRenderCache($cid, $referencing_entity_cache_tags, $redirected_cid);
$this->pass("Test non-referencing entity.", 'Debug');
$this->verifyPageCache($non_referencing_entity_url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$this->verifyPageCache($non_referencing_entity_url, 'HIT', Cache::mergeTags($non_referencing_entity_cache_tags, $page_cache_tags));
// Also verify the existence of an entity render cache entry.
$cache_keys = ['entity_view', 'entity_test', $this->nonReferencingEntity->id(), 'full'];
$cid = $this->createCacheId($cache_keys, $entity_cache_contexts);
$this->verifyRenderCache($cid, $non_referencing_entity_cache_tags);
$this->pass("Test listing of referencing entities.", 'Debug');
// Prime the page cache for the listing of referencing entities.
$this->verifyPageCache($listing_url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags);
$expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity);
$this->verifyPageCache($listing_url, 'HIT', $expected_tags);
$this->pass("Test empty listing.", 'Debug');
// Prime the page cache for the empty listing.
$this->verifyPageCache($empty_entity_listing_url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags);
// Verify the entity type's list cache contexts are present.
$contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
$this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
$this->pass("Test listing containing referenced entity.", 'Debug');
// Prime the page cache for the listing containing the referenced entity.
$this->verifyPageCache($nonempty_entity_listing_url, 'MISS', $nonempty_entity_listing_cache_tags);
// Verify a cache hit, but also the presence of the correct cache tags.
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags);
// Verify the entity type's list cache contexts are present.
$contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
$this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
// Verify that after modifying the referenced entity, there is a cache miss
// for every route except the one for the non-referencing entity.
$this->pass("Test modification of referenced entity.", 'Debug');
$this->entity->save();
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($empty_entity_listing_url, 'MISS');
$this->verifyPageCache($nonempty_entity_listing_url, 'MISS');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
// Verify cache hits.
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
// Verify that after modifying the referencing entity, there is a cache miss
// for every route except the ones for the non-referencing entity and the
// empty entity listing.
$this->pass("Test modification of referencing entity.", 'Debug');
$this->referencingEntity->save();
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
// Verify cache hits.
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
// Verify that after modifying the non-referencing entity, there is a cache
// miss only for the non-referencing entity route.
$this->pass("Test modification of non-referencing entity.", 'Debug');
$this->nonReferencingEntity->save();
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
$this->verifyPageCache($non_referencing_entity_url, 'MISS');
// Verify cache hits.
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) {
// Verify that after modifying the entity's display, there is a cache miss
// for both the referencing entity, and the listing of referencing
// entities, but not for any other routes.
$referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId());
$this->pass("Test modification of referenced entity's '$referenced_entity_view_mode' display.", 'Debug');
$entity_display = entity_get_display($entity_type, $this->entity->bundle(), $referenced_entity_view_mode);
$entity_display->save();
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
// Verify cache hits.
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
}
if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) {
// Verify that after modifying the corresponding bundle entity, there is a
// cache miss for both the referencing entity, and the listing of
// referencing entities, but not for any other routes.
$this->pass("Test modification of referenced entity's bundle entity.", 'Debug');
$bundle_entity = $this->container->get('entity_type.manager')
->getStorage($bundle_entity_type_id)
->load($this->entity->bundle());
$bundle_entity->save();
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
// Special case: entity types may choose to use their bundle entity type
// cache tags, to avoid having excessively granular invalidation.
$is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getEntityType()->getListCacheTags() == $this->entity->getEntityType()->getListCacheTags();
if ($is_special_case) {
$this->verifyPageCache($empty_entity_listing_url, 'MISS');
$this->verifyPageCache($nonempty_entity_listing_url, 'MISS');
}
else {
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
}
// Verify cache hits.
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
if ($is_special_case) {
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
}
}
if ($this->entity->getEntityType()->get('field_ui_base_route')) {
// Verify that after modifying a configurable field on the entity, there
// is a cache miss.
$this->pass("Test modification of referenced entity's configurable field.", 'Debug');
$field_storage_name = $this->entity->getEntityTypeId() . '.configurable_field';
$field_storage = FieldStorageConfig::load($field_storage_name);
$field_storage->save();
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
// Verify cache hits.
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
// Verify that after modifying a configurable field on the entity, there
// is a cache miss.
$this->pass("Test modification of referenced entity's configurable field.", 'Debug');
$field_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field';
$field = FieldConfig::load($field_name);
$field->save();
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
// Verify cache hits.
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
}
// Verify that after invalidating the entity's cache tag directly, there is
// a cache miss for every route except the ones for the non-referencing
// entity and the empty entity listing.
$this->pass("Test invalidation of referenced entity's cache tag.", 'Debug');
Cache::invalidateTags($this->entity->getCacheTagsToInvalidate());
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($nonempty_entity_listing_url, 'MISS');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
// Verify cache hits.
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
// Verify that after invalidating the entity's list cache tag directly,
// there is a cache miss for both the empty entity listing and the non-empty
// entity listing routes, but not for other routes.
$this->pass("Test invalidation of referenced entity's list cache tag.", 'Debug');
Cache::invalidateTags($this->entity->getEntityType()->getListCacheTags());
$this->verifyPageCache($empty_entity_listing_url, 'MISS');
$this->verifyPageCache($nonempty_entity_listing_url, 'MISS');
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
// Verify cache hits.
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
if (!empty($view_cache_tag)) {
// Verify that after invalidating the generic entity type's view cache tag
// directly, there is a cache miss for both the referencing entity, and the
// listing of referencing entities, but not for other routes.
$this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug');
Cache::invalidateTags($view_cache_tag);
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
$this->verifyPageCache($empty_entity_listing_url, 'HIT');
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT');
// Verify cache hits.
$this->verifyPageCache($referencing_entity_url, 'HIT');
$this->verifyPageCache($listing_url, 'HIT');
}
// Verify that after deleting the entity, there is a cache miss for every
// route except for the non-referencing entity one.
$this->pass('Test deletion of referenced entity.', 'Debug');
$this->entity->delete();
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($empty_entity_listing_url, 'MISS');
$this->verifyPageCache($nonempty_entity_listing_url, 'MISS');
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
// Verify cache hits.
$referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['http_response', 'rendered']);
$nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing());
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags);
$this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags));
$this->verifyPageCache($listing_url, 'HIT', $page_cache_tags);
$this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags);
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags);
}
/**
* Creates a cache ID from a list of cache keys and a set of cache contexts.
*
* @param string[] $keys
* A list of cache keys.
* @param string[] $contexts
* A set of cache contexts.
*
* @return string
* The cache ID string.
*/
protected function createCacheId(array $keys, array $contexts) {
$cid_parts = $keys;
$contexts = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($contexts);
$cid_parts = array_merge($cid_parts, $contexts->getKeys());
return implode(':', $cid_parts);
}
/**
* Verify that a given render cache entry exists, with the correct cache tags.
*
* @param string $cid
* The render cache item ID.
* @param array $tags
* An array of expected cache tags.
* @param string|null $redirected_cid
* (optional) The redirected render cache item ID.
*/
protected function verifyRenderCache($cid, array $tags, $redirected_cid = NULL) {
// Also verify the existence of an entity render cache entry.
$cache_entry = \Drupal::cache('render')->get($cid);
$this->assertTrue($cache_entry, 'A render cache entry exists.');
sort($cache_entry->tags);
sort($tags);
$this->assertIdentical($cache_entry->tags, $tags);
$is_redirecting_cache_item = isset($cache_entry->data['#cache_redirect']);
if ($redirected_cid === NULL) {
$this->assertFalse($is_redirecting_cache_item, 'Render cache entry is not a redirect.');
// If this is a redirecting cache item unlike we expected, log it.
if ($is_redirecting_cache_item) {
debug($cache_entry->data);
}
}
else {
// Verify that $cid contains a cache redirect.
$this->assertTrue($is_redirecting_cache_item, 'Render cache entry is a redirect.');
// If this is not a redirecting cache item unlike we expected, log it.
if (!$is_redirecting_cache_item) {
debug($cache_entry->data);
}
// Verify that the cache redirect points to the expected CID.
$redirect_cache_metadata = $cache_entry->data['#cache'];
$actual_redirection_cid = $this->createCacheId(
$redirect_cache_metadata['keys'],
$redirect_cache_metadata['contexts']
);
$this->assertIdentical($redirected_cid, $actual_redirection_cid);
// Finally, verify that the redirected CID exists and has the same cache
// tags.
$this->verifyRenderCache($redirected_cid, $tags);
}
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace Drupal\Tests\system\Functional\Entity;
use Drupal\Core\Language\LanguageInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\BrowserTestBase;
/**
* Tests entity list builder functionality.
*
* @group Entity
*/
class EntityListBuilderTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create and log in user.
$this->webUser = $this->drupalCreateUser([
'administer entity_test content',
]);
$this->drupalLogin($this->webUser);
}
/**
* Test paging.
*/
public function testPager() {
// Create 51 test entities.
for ($i = 1; $i < 52; $i++) {
EntityTest::create(['name' => 'Test entity ' . $i])->save();
}
// Load the listing page.
$this->drupalGet('entity_test/list');
// Item 51 should not be present.
$this->assertRaw('Test entity 50', 'Item 50 is shown.');
$this->assertNoRaw('Test entity 51', 'Item 51 is on the next page.');
// Browse to the next page.
$this->clickLink(t('Page 2'));
$this->assertNoRaw('Test entity 50', 'Test entity 50 is on the previous page.');
$this->assertRaw('Test entity 51', 'Test entity 51 is shown.');
}
/**
* Tests that the correct cache contexts are set.
*/
public function testCacheContexts() {
/** @var \Drupal\Core\Entity\EntityListBuilderInterface $list_builder */
$list_builder = $this->container->get('entity.manager')->getListBuilder('entity_test');
$build = $list_builder->render();
$this->container->get('renderer')->renderRoot($build);
$this->assertEqual(['entity_test_view_grants', 'languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url.query_args.pagers:0', 'user.permissions'], $build['#cache']['contexts']);
}
/**
* Tests if the list cache tags are set.
*/
public function testCacheTags() {
$this->drupalGet('entity_test/list');
$this->assertCacheTag('entity_test_list');
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace Drupal\Tests\system\Functional\Entity;
use Drupal\Tests\BrowserTestBase;
/**
* Tests that operations can be injected from the hook.
*
* @group Entity
*/
class EntityOperationsTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['entity_test'];
protected function setUp() {
parent::setUp();
// Create and log in user.
$this->drupalLogin($this->drupalCreateUser(['administer permissions']));
}
/**
* Checks that hook_entity_operation_alter() can add an operation.
*
* @see entity_test_entity_operation_alter()
*/
public function testEntityOperationAlter() {
// Check that role listing contain our test_operation operation.
$this->drupalGet('admin/people/roles');
$roles = user_roles();
foreach ($roles as $role) {
$this->assertLinkByHref($role->url() . '/test_operation');
$this->assertLink(format_string('Test Operation: @label', ['@label' => $role->label()]));
}
}
/**
* {@inheritdoc}
*/
protected function createRole(array $permissions, $rid = NULL, $name = NULL, $weight = NULL) {
// WebTestBase::drupalCreateRole() by default uses random strings which may
// include HTML entities for the entity label. Since in this test the entity
// label is used to generate a link, and AssertContentTrait::assertLink() is
// not designed to deal with links potentially containing HTML entities this
// causes random failures. Use a random HTML safe string instead.
$name = $name ?: $this->randomMachineName();
return parent::createRole($permissions, $rid, $name, $weight);
}
}

View file

@ -0,0 +1,516 @@
<?php
namespace Drupal\Tests\system\Functional\Entity\EntityReferenceSelection;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Component\Utility\Html;
use Drupal\Core\Language\LanguageInterface;
use Drupal\comment\CommentInterface;
use Drupal\node\Entity\Node;
use Drupal\Tests\BrowserTestBase;
use Drupal\node\NodeInterface;
use Drupal\user\Entity\User;
use Drupal\comment\Entity\Comment;
/**
* Tests for the base handlers provided by Entity Reference.
*
* @group entity_reference
*/
class EntityReferenceSelectionAccessTest extends BrowserTestBase {
use CommentTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'comment'];
protected function setUp() {
parent::setUp();
// Create an Article node type.
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
}
/**
* Checks that a selection plugin returns the expected results.
*
* @param array $selection_options
* An array of options as required by entity reference selection plugins.
* @param array $tests
* An array of tests to run.
* @param string $handler_name
* The name of the entity type selection handler being tested.
*/
protected function assertReferenceable(array $selection_options, $tests, $handler_name) {
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($selection_options);
foreach ($tests as $test) {
foreach ($test['arguments'] as $arguments) {
$result = call_user_func_array([$handler, 'getReferenceableEntities'], $arguments);
$this->assertEqual($result, $test['result'], format_string('Valid result set returned by @handler.', ['@handler' => $handler_name]));
$result = call_user_func_array([$handler, 'countReferenceableEntities'], $arguments);
if (!empty($test['result'])) {
$bundle = key($test['result']);
$count = count($test['result'][$bundle]);
}
else {
$count = 0;
}
$this->assertEqual($result, $count, format_string('Valid count returned by @handler.', ['@handler' => $handler_name]));
}
}
}
/**
* Test the node-specific overrides of the entity handler.
*/
public function testNodeHandler() {
$selection_options = [
'target_type' => 'node',
'handler' => 'default',
'handler_settings' => [
'target_bundles' => NULL,
],
];
// Build a set of test data.
// Titles contain HTML-special characters to test escaping.
$node_values = [
'published1' => [
'type' => 'article',
'status' => NodeInterface::PUBLISHED,
'title' => 'Node published1 (<&>)',
'uid' => 1,
],
'published2' => [
'type' => 'article',
'status' => NodeInterface::PUBLISHED,
'title' => 'Node published2 (<&>)',
'uid' => 1,
],
'unpublished' => [
'type' => 'article',
'status' => NodeInterface::NOT_PUBLISHED,
'title' => 'Node unpublished (<&>)',
'uid' => 1,
],
];
$nodes = [];
$node_labels = [];
foreach ($node_values as $key => $values) {
$node = Node::create($values);
$node->save();
$nodes[$key] = $node;
$node_labels[$key] = Html::escape($node->label());
}
// Test as a non-admin.
$normal_user = $this->drupalCreateUser(['access content']);
\Drupal::currentUser()->setAccount($normal_user);
$referenceable_tests = [
[
'arguments' => [
[NULL, 'CONTAINS'],
],
'result' => [
'article' => [
$nodes['published1']->id() => $node_labels['published1'],
$nodes['published2']->id() => $node_labels['published2'],
],
],
],
[
'arguments' => [
['published1', 'CONTAINS'],
['Published1', 'CONTAINS'],
],
'result' => [
'article' => [
$nodes['published1']->id() => $node_labels['published1'],
],
],
],
[
'arguments' => [
['published2', 'CONTAINS'],
['Published2', 'CONTAINS'],
],
'result' => [
'article' => [
$nodes['published2']->id() => $node_labels['published2'],
],
],
],
[
'arguments' => [
['invalid node', 'CONTAINS'],
],
'result' => [],
],
[
'arguments' => [
['Node unpublished', 'CONTAINS'],
],
'result' => [],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'Node handler');
// Test as an admin.
$admin_user = $this->drupalCreateUser(['access content', 'bypass node access']);
\Drupal::currentUser()->setAccount($admin_user);
$referenceable_tests = [
[
'arguments' => [
[NULL, 'CONTAINS'],
],
'result' => [
'article' => [
$nodes['published1']->id() => $node_labels['published1'],
$nodes['published2']->id() => $node_labels['published2'],
$nodes['unpublished']->id() => $node_labels['unpublished'],
],
],
],
[
'arguments' => [
['Node unpublished', 'CONTAINS'],
],
'result' => [
'article' => [
$nodes['unpublished']->id() => $node_labels['unpublished'],
],
],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'Node handler (admin)');
}
/**
* Test the user-specific overrides of the entity handler.
*/
public function testUserHandler() {
$selection_options = [
'target_type' => 'user',
'handler' => 'default',
'handler_settings' => [
'target_bundles' => NULL,
'include_anonymous' => TRUE,
],
];
// Build a set of test data.
$user_values = [
'anonymous' => User::load(0),
'admin' => User::load(1),
'non_admin' => [
'name' => 'non_admin <&>',
'mail' => 'non_admin@example.com',
'roles' => [],
'pass' => user_password(),
'status' => 1,
],
'blocked' => [
'name' => 'blocked <&>',
'mail' => 'blocked@example.com',
'roles' => [],
'pass' => user_password(),
'status' => 0,
],
];
$user_values['anonymous']->name = $this->config('user.settings')->get('anonymous');
$users = [];
$user_labels = [];
foreach ($user_values as $key => $values) {
if (is_array($values)) {
$account = User::create($values);
$account->save();
}
else {
$account = $values;
}
$users[$key] = $account;
$user_labels[$key] = Html::escape($account->getUsername());
}
// Test as a non-admin.
\Drupal::currentUser()->setAccount($users['non_admin']);
$referenceable_tests = [
[
'arguments' => [
[NULL, 'CONTAINS'],
],
'result' => [
'user' => [
$users['admin']->id() => $user_labels['admin'],
$users['non_admin']->id() => $user_labels['non_admin'],
],
],
],
[
'arguments' => [
['non_admin', 'CONTAINS'],
['NON_ADMIN', 'CONTAINS'],
],
'result' => [
'user' => [
$users['non_admin']->id() => $user_labels['non_admin'],
],
],
],
[
'arguments' => [
['invalid user', 'CONTAINS'],
],
'result' => [],
],
[
'arguments' => [
['blocked', 'CONTAINS'],
],
'result' => [],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'User handler');
\Drupal::currentUser()->setAccount($users['admin']);
$referenceable_tests = [
[
'arguments' => [
[NULL, 'CONTAINS'],
],
'result' => [
'user' => [
$users['anonymous']->id() => $user_labels['anonymous'],
$users['admin']->id() => $user_labels['admin'],
$users['non_admin']->id() => $user_labels['non_admin'],
$users['blocked']->id() => $user_labels['blocked'],
],
],
],
[
'arguments' => [
['blocked', 'CONTAINS'],
],
'result' => [
'user' => [
$users['blocked']->id() => $user_labels['blocked'],
],
],
],
[
'arguments' => [
['Anonymous', 'CONTAINS'],
['anonymous', 'CONTAINS'],
],
'result' => [
'user' => [
$users['anonymous']->id() => $user_labels['anonymous'],
],
],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'User handler (admin)');
// Test the 'include_anonymous' option.
$selection_options['handler_settings']['include_anonymous'] = FALSE;
$referenceable_tests = [
[
'arguments' => [
['Anonymous', 'CONTAINS'],
['anonymous', 'CONTAINS'],
],
'result' => [],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'User handler (does not include anonymous)');
// Check that the Anonymous user is not included in the results when no
// label matching is done, for example when using the 'options_select'
// widget.
$referenceable_tests = [
[
'arguments' => [
[NULL],
],
'result' => [
'user' => [
$users['admin']->id() => $user_labels['admin'],
$users['non_admin']->id() => $user_labels['non_admin'],
$users['blocked']->id() => $user_labels['blocked'],
],
],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'User handler (does not include anonymous)');
}
/**
* Test the comment-specific overrides of the entity handler.
*/
public function testCommentHandler() {
$selection_options = [
'target_type' => 'comment',
'handler' => 'default',
'handler_settings' => [
'target_bundles' => NULL,
],
];
// Build a set of test data.
$node_values = [
'published' => [
'type' => 'article',
'status' => 1,
'title' => 'Node published',
'uid' => 1,
],
'unpublished' => [
'type' => 'article',
'status' => 0,
'title' => 'Node unpublished',
'uid' => 1,
],
];
$nodes = [];
foreach ($node_values as $key => $values) {
$node = Node::create($values);
$node->save();
$nodes[$key] = $node;
}
// Create comment field on article.
$this->addDefaultCommentField('node', 'article');
$comment_values = [
'published_published' => [
'entity_id' => $nodes['published']->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'uid' => 1,
'cid' => NULL,
'pid' => 0,
'status' => CommentInterface::PUBLISHED,
'subject' => 'Comment Published <&>',
'language' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
'published_unpublished' => [
'entity_id' => $nodes['published']->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'uid' => 1,
'cid' => NULL,
'pid' => 0,
'status' => CommentInterface::NOT_PUBLISHED,
'subject' => 'Comment Unpublished <&>',
'language' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
'unpublished_published' => [
'entity_id' => $nodes['unpublished']->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'uid' => 1,
'cid' => NULL,
'pid' => 0,
'status' => CommentInterface::NOT_PUBLISHED,
'subject' => 'Comment Published on Unpublished node <&>',
'language' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
];
$comments = [];
$comment_labels = [];
foreach ($comment_values as $key => $values) {
$comment = Comment::create($values);
$comment->save();
$comments[$key] = $comment;
$comment_labels[$key] = Html::escape($comment->label());
}
// Test as a non-admin.
$normal_user = $this->drupalCreateUser(['access content', 'access comments']);
\Drupal::currentUser()->setAccount($normal_user);
$referenceable_tests = [
[
'arguments' => [
[NULL, 'CONTAINS'],
],
'result' => [
'comment' => [
$comments['published_published']->cid->value => $comment_labels['published_published'],
],
],
],
[
'arguments' => [
['Published', 'CONTAINS'],
],
'result' => [
'comment' => [
$comments['published_published']->cid->value => $comment_labels['published_published'],
],
],
],
[
'arguments' => [
['invalid comment', 'CONTAINS'],
],
'result' => [],
],
[
'arguments' => [
['Comment Unpublished', 'CONTAINS'],
],
'result' => [],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler');
// Test as a comment admin.
$admin_user = $this->drupalCreateUser(['access content', 'access comments', 'administer comments']);
\Drupal::currentUser()->setAccount($admin_user);
$referenceable_tests = [
[
'arguments' => [
[NULL, 'CONTAINS'],
],
'result' => [
'comment' => [
$comments['published_published']->cid->value => $comment_labels['published_published'],
$comments['published_unpublished']->cid->value => $comment_labels['published_unpublished'],
],
],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler (comment admin)');
// Test as a node and comment admin.
$admin_user = $this->drupalCreateUser(['access content', 'access comments', 'administer comments', 'bypass node access']);
\Drupal::currentUser()->setAccount($admin_user);
$referenceable_tests = [
[
'arguments' => [
[NULL, 'CONTAINS'],
],
'result' => [
'comment' => [
$comments['published_published']->cid->value => $comment_labels['published_published'],
$comments['published_unpublished']->cid->value => $comment_labels['published_unpublished'],
$comments['unpublished_published']->cid->value => $comment_labels['unpublished_published'],
],
],
],
];
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler (comment + node admin)');
}
}

View file

@ -0,0 +1,162 @@
<?php
namespace Drupal\Tests\system\Functional\Entity;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Create a entity with revisions and test viewing, saving, reverting, and
* deleting revisions.
*
* @group Entity
*/
class EntityRevisionsTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['entity_test', 'language'];
/**
* A user with permission to administer entity_test content.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
protected function setUp() {
parent::setUp();
// Create and log in user.
$this->webUser = $this->drupalCreateUser([
'administer entity_test content',
'view test entity',
]);
$this->drupalLogin($this->webUser);
// Enable an additional language.
ConfigurableLanguage::createFromLangcode('de')->save();
}
/**
* Check node revision related operations.
*/
public function testRevisions() {
// All revisable entity variations have to have the same results.
foreach (entity_test_entity_types(ENTITY_TEST_TYPES_REVISABLE) as $entity_type) {
$this->runRevisionsTests($entity_type);
}
}
/**
* Executes the revision tests for the given entity type.
*
* @param string $entity_type
* The entity type to run the tests with.
*/
protected function runRevisionsTests($entity_type) {
// Create initial entity.
$entity = $this->container->get('entity_type.manager')
->getStorage($entity_type)
->create([
'name' => 'foo',
'user_id' => $this->webUser->id(),
]);
$entity->field_test_text->value = 'bar';
$entity->save();
$names = [];
$texts = [];
$created = [];
$revision_ids = [];
// Create three revisions.
$revision_count = 3;
for ($i = 0; $i < $revision_count; $i++) {
$legacy_revision_id = $entity->revision_id->value;
$legacy_name = $entity->name->value;
$legacy_text = $entity->field_test_text->value;
$entity = $this->container->get('entity_type.manager')
->getStorage($entity_type)->load($entity->id->value);
$entity->setNewRevision(TRUE);
$names[] = $entity->name->value = $this->randomMachineName(32);
$texts[] = $entity->field_test_text->value = $this->randomMachineName(32);
$created[] = $entity->created->value = time() + $i + 1;
$entity->save();
$revision_ids[] = $entity->revision_id->value;
// Check that the fields and properties contain new content.
$this->assertTrue($entity->revision_id->value > $legacy_revision_id, format_string('%entity_type: Revision ID changed.', ['%entity_type' => $entity_type]));
$this->assertNotEqual($entity->name->value, $legacy_name, format_string('%entity_type: Name changed.', ['%entity_type' => $entity_type]));
$this->assertNotEqual($entity->field_test_text->value, $legacy_text, format_string('%entity_type: Text changed.', ['%entity_type' => $entity_type]));
}
$storage = $this->container->get('entity_type.manager')->getStorage($entity_type);
for ($i = 0; $i < $revision_count; $i++) {
// Load specific revision.
$entity_revision = $storage->loadRevision($revision_ids[$i]);
// Check if properties and fields contain the revision specific content.
$this->assertEqual($entity_revision->revision_id->value, $revision_ids[$i], format_string('%entity_type: Revision ID matches.', ['%entity_type' => $entity_type]));
$this->assertEqual($entity_revision->name->value, $names[$i], format_string('%entity_type: Name matches.', ['%entity_type' => $entity_type]));
$this->assertEqual($entity_revision->field_test_text->value, $texts[$i], format_string('%entity_type: Text matches.', ['%entity_type' => $entity_type]));
// Check non-revisioned values are loaded.
$this->assertTrue(isset($entity_revision->created->value), format_string('%entity_type: Non-revisioned field is loaded.', ['%entity_type' => $entity_type]));
$this->assertEqual($entity_revision->created->value, $created[2], format_string('%entity_type: Non-revisioned field value is the same between revisions.', ['%entity_type' => $entity_type]));
}
// Confirm the correct revision text appears in the edit form.
$entity = $this->container->get('entity_type.manager')
->getStorage($entity_type)
->load($entity->id->value);
$this->drupalGet($entity_type . '/manage/' . $entity->id->value . '/edit');
$this->assertFieldById('edit-name-0-value', $entity->name->value, format_string('%entity_type: Name matches in UI.', ['%entity_type' => $entity_type]));
$this->assertFieldById('edit-field-test-text-0-value', $entity->field_test_text->value, format_string('%entity_type: Text matches in UI.', ['%entity_type' => $entity_type]));
}
/**
* Tests that an entity revision is upcasted in the correct language.
*/
public function testEntityRevisionParamConverter() {
// Create a test entity with multiple revisions and translations for them.
$entity = EntityTestMulRev::create([
'name' => 'default revision - en',
'user_id' => $this->webUser,
'language' => 'en',
]);
$entity->addTranslation('de', ['name' => 'default revision - de']);
$entity->save();
$forward_revision = \Drupal::entityTypeManager()->getStorage('entity_test_mulrev')->loadUnchanged($entity->id());
$forward_revision->setNewRevision();
$forward_revision->isDefaultRevision(FALSE);
$forward_revision->name = 'forward revision - en';
$forward_revision->save();
$forward_revision_translation = $forward_revision->getTranslation('de');
$forward_revision_translation->name = 'forward revision - de';
$forward_revision_translation->save();
// Check that the entity revision is upcasted in the correct language.
$revision_url = 'entity_test_mulrev/' . $entity->id() . '/revision/' . $forward_revision->getRevisionId() . '/view';
$this->drupalGet($revision_url);
$this->assertText('forward revision - en');
$this->assertNoText('forward revision - de');
$this->drupalGet('de/' . $revision_url);
$this->assertText('forward revision - de');
$this->assertNoText('forward revision - en');
}
}

View file

@ -0,0 +1,149 @@
<?php
namespace Drupal\Tests\system\Functional\Entity;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\BrowserTestBase;
/**
* Tests EntityViewController functionality.
*
* @group Entity
*/
class EntityViewControllerTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['entity_test'];
/**
* Array of test entities.
*
* @var array
*/
protected $entities = [];
protected function setUp() {
parent::setUp();
// Create some dummy entity_test entities.
for ($i = 0; $i < 2; $i++) {
$entity_test = $this->createTestEntity('entity_test');
$entity_test->save();
$this->entities[] = $entity_test;
}
$this->drupalLogin($this->drupalCreateUser(['view test entity']));
}
/**
* Tests EntityViewController.
*/
public function testEntityViewController() {
$get_label_markup = function($label) {
return '<h1 class="page-title">
<div class="field field--name-name field--type-string field--label-hidden field__item">' . $label . '</div>
</h1>';
};
foreach ($this->entities as $entity) {
$this->drupalGet('entity_test/' . $entity->id());
$this->assertRaw($entity->label());
$this->assertRaw($get_label_markup($entity->label()));
$this->assertRaw('full');
$this->drupalGet('entity_test_converter/' . $entity->id());
$this->assertRaw($entity->label());
$this->assertRaw('full');
$this->drupalGet('entity_test_no_view_mode/' . $entity->id());
$this->assertRaw($entity->label());
$this->assertRaw('full');
}
// Test viewing a revisionable entity.
$entity_test_rev = $this->createTestEntity('entity_test_rev');
$entity_test_rev->save();
$entity_test_rev->name->value = 'rev 2';
$entity_test_rev->setNewRevision(TRUE);
$entity_test_rev->isDefaultRevision(TRUE);
$entity_test_rev->save();
$this->drupalGet('entity_test_rev/' . $entity_test_rev->id() . '/revision/' . $entity_test_rev->revision_id->value . '/view');
$this->assertRaw($entity_test_rev->label());
$this->assertRaw($get_label_markup($entity_test_rev->label()));
// As entity_test IDs must be integers, make sure requests for non-integer
// IDs return a page not found error.
$this->drupalGet('entity_test/invalid');
$this->assertResponse(404);
}
/**
* Tests field item attributes.
*/
public function testFieldItemAttributes() {
// Make sure the test field will be rendered.
entity_get_display('entity_test', 'entity_test', 'default')
->setComponent('field_test_text', ['type' => 'text_default'])
->save();
// Create an entity and save test value in field_test_text.
$test_value = $this->randomMachineName();
$entity = EntityTest::create();
$entity->field_test_text = $test_value;
$entity->save();
// Browse to the entity and verify that the attribute is rendered in the
// field item HTML markup.
$this->drupalGet('entity_test/' . $entity->id());
$xpath = $this->xpath('//div[@data-field-item-attr="foobar"]/p[text()=:value]', [':value' => $test_value]);
$this->assertTrue($xpath, 'The field item attribute has been found in the rendered output of the field.');
// Enable the RDF module to ensure that two modules can add attributes to
// the same field item.
\Drupal::service('module_installer')->install(['rdf']);
$this->resetAll();
// Set an RDF mapping for the field_test_text field. This RDF mapping will
// be turned into RDFa attributes in the field item output.
$mapping = rdf_get_mapping('entity_test', 'entity_test');
$mapping->setFieldMapping('field_test_text', [
'properties' => ['schema:text'],
])->save();
// Browse to the entity and verify that the attributes from both modules
// are rendered in the field item HTML markup.
$this->drupalGet('entity_test/' . $entity->id());
$xpath = $this->xpath('//div[@data-field-item-attr="foobar" and @property="schema:text"]/p[text()=:value]', [':value' => $test_value]);
$this->assertTrue($xpath, 'The field item attributes from both modules have been found in the rendered output of the field.');
}
/**
* Tests that a view builder can successfully override the view builder.
*/
public function testEntityViewControllerViewBuilder() {
$entity_test = $this->createTestEntity('entity_test_view_builder');
$entity_test->save();
$this->drupalGet('entity_test_view_builder/' . $entity_test->id());
$this->assertText($entity_test->label());
}
/**
* Creates an entity for testing.
*
* @param string $entity_type
* The entity type.
*
* @return \Drupal\Core\Entity\EntityInterface
* The created entity.
*/
protected function createTestEntity($entity_type) {
$data = [
'bundle' => $entity_type,
'name' => $this->randomMachineName(),
];
return $this->container->get('entity.manager')->getStorage($entity_type)->create($data);
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace Drupal\Tests\system\Functional\File;
use Drupal\Tests\BrowserTestBase;
/**
* Tests file system configuration operations.
*
* @group File
*/
class ConfigTest extends BrowserTestBase {
protected function setUp(){
parent::setUp();
$this->drupalLogin ($this->drupalCreateUser(['administer site configuration']));
}
/**
* Tests file configuration page.
*/
public function testFileConfigurationPage() {
$this->drupalGet('admin/config/media/file-system');
// Set the file paths to non-default values.
// The respective directories are created automatically
// upon form submission.
$file_path = $this->publicFilesDirectory;
$fields = [
'file_temporary_path' => $file_path . '/file_config_page_test/temporary',
'file_default_scheme' => 'private',
];
// Check that public and private can be selected as default scheme.
$this->assertText('Public local files served by the webserver.');
$this->assertText('Private local files served by Drupal.');
$this->drupalPostForm(NULL, $fields, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
foreach ($fields as $field => $value) {
$this->assertFieldByName($field, $value);
}
// Remove the private path, rebuild the container and verify that private
// can no longer be selected in the UI.
$settings['settings']['file_private_path'] = (object) [
'value' => '',
'required' => TRUE,
];
$this->writeSettings($settings);
$this->rebuildContainer();
$this->drupalGet('admin/config/media/file-system');
$this->assertText('Public local files served by the webserver.');
$this->assertNoText('Private local files served by Drupal.');
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Drupal\Tests\system\Functional\File;
use Drupal\Component\PhpStorage\FileStorage;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the log message added by file_save_htacess().
*
* @group File
*/
class FileSaveHtaccessLoggingTest extends BrowserTestBase {
protected static $modules = ['dblog'];
/**
* Tests file_save_htaccess().
*/
public function testHtaccessSave() {
// Prepare test directories.
$private = $this->publicFilesDirectory . '/test/private';
// Verify that file_save_htaccess() returns FALSE if .htaccess cannot be
// written and writes a correctly formatted message to the error log. Set
// $private to TRUE so all possible .htaccess lines are written.
$this->assertFalse(file_save_htaccess($private, TRUE));
$this->drupalLogin($this->rootUser);
$this->drupalGet('admin/reports/dblog');
$this->clickLink("Security warning: Couldn't write .htaccess file. Please…");
$lines = FileStorage::htaccessLines(TRUE);
foreach (array_filter(explode("\n", $lines)) as $line) {
$this->assertEscaped($line);
}
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace Drupal\Tests\system\Functional\FileTransfer;
use Drupal\Core\FileTransfer\FileTransferException;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Tests\BrowserTestBase;
/**
* Tests that the jail is respected and that protocols using recursive file move
* operations work.
*
* @group FileTransfer
*/
class FileTransferTest extends BrowserTestBase {
protected $hostname = 'localhost';
protected $username = 'drupal';
protected $password = 'password';
protected $port = '42';
protected function setUp() {
parent::setUp();
$this->testConnection = TestFileTransfer::factory(\Drupal::root(), ['hostname' => $this->hostname, 'username' => $this->username, 'password' => $this->password, 'port' => $this->port]);
}
public function _getFakeModuleFiles() {
$files = [
'fake.module',
'fake.info.yml',
'theme' => [
'fake.html.twig'
],
'inc' => [
'fake.inc'
]
];
return $files;
}
public function _buildFakeModule() {
$location = 'temporary://fake';
if (is_dir($location)) {
$ret = 0;
$output = [];
exec('rm -Rf ' . escapeshellarg($location), $output, $ret);
if ($ret != 0) {
throw new Exception('Error removing fake module directory.');
}
}
$files = $this->_getFakeModuleFiles();
$this->_writeDirectory($location, $files);
return $location;
}
public function _writeDirectory($base, $files = []) {
mkdir($base);
foreach ($files as $key => $file) {
if (is_array($file)) {
$this->_writeDirectory($base . DIRECTORY_SEPARATOR . $key, $file);
}
else {
//just write the filename into the file
file_put_contents($base . DIRECTORY_SEPARATOR . $file, $file);
}
}
}
public function testJail() {
$source = $this->_buildFakeModule();
// This convoluted piece of code is here because our testing framework does
// not support expecting exceptions.
$gotit = FALSE;
try {
$this->testConnection->copyDirectory($source, sys_get_temp_dir());
}
catch (FileTransferException $e) {
$gotit = TRUE;
}
$this->assertTrue($gotit, 'Was not able to copy a directory outside of the jailed area.');
$gotit = TRUE;
try {
$this->testConnection->copyDirectory($source, \Drupal::root() . '/' . PublicStream::basePath());
}
catch (FileTransferException $e) {
$gotit = FALSE;
}
$this->assertTrue($gotit, 'Was able to copy a directory inside of the jailed area');
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Drupal\Tests\system\Functional\FileTransfer;
/**
* Mock connection object for test case.
*/
class MockTestConnection {
protected $commandsRun = [];
public $connectionString;
public function run($cmd) {
$this->commandsRun[] = $cmd;
}
public function flushCommands() {
$out = $this->commandsRun;
$this->commandsRun = [];
return $out;
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace Drupal\Tests\system\Functional\FileTransfer;
use Drupal\Core\FileTransfer\FileTransfer;
use Drupal\Core\FileTransfer\FileTransferException;
/**
* Mock FileTransfer object for test case.
*/
class TestFileTransfer extends FileTransfer {
protected $host = NULL;
protected $username = NULL;
protected $password = NULL;
protected $port = NULL;
/**
* This is for testing the CopyRecursive logic.
*/
public $shouldIsDirectoryReturnTrue = FALSE;
public function __construct($jail, $username, $password, $hostname = 'localhost', $port = 9999) {
parent::__construct($jail, $username, $password, $hostname, $port);
}
public static function factory($jail, $settings) {
return new TestFileTransfer($jail, $settings['username'], $settings['password'], $settings['hostname'], $settings['port']);
}
public function connect() {
$this->connection = new MockTestConnection();
$this->connection->connectionString = 'test://' . urlencode($this->username) . ':' . urlencode($this->password) . "@$this->host:$this->port/";
}
public function copyFileJailed($source, $destination) {
$this->connection->run("copyFile $source $destination");
}
protected function removeDirectoryJailed($directory) {
$this->connection->run("rmdir $directory");
}
public function createDirectoryJailed($directory) {
$this->connection->run("mkdir $directory");
}
public function removeFileJailed($destination) {
if (!ftp_delete($this->connection, $item)) {
throw new FileTransferException('Unable to remove to file @file.', NULL, ['@file' => $item]);
}
}
public function isDirectory($path) {
return $this->shouldIsDirectoryReturnTrue;
}
public function isFile($path) {
return FALSE;
}
public function chmodJailed($path, $mode, $recursive) {
return;
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\system\Functional\Form;
use Drupal\Tests\BrowserTestBase;
/**
* Tests access control for form elements.
*
* @group Form
*/
class ElementsAccessTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
/**
* Ensures that child values are still processed when #access = FALSE.
*/
public function testAccessFalse() {
$this->drupalPostForm('form_test/vertical-tabs-access', NULL, t('Submit'));
$this->assertNoText(t('This checkbox inside a vertical tab does not have its default value.'));
$this->assertNoText(t('This textfield inside a vertical tab does not have its default value.'));
$this->assertNoText(t('This checkbox inside a fieldset does not have its default value.'));
$this->assertNoText(t('This checkbox inside a container does not have its default value.'));
$this->assertNoText(t('This checkbox inside a nested container does not have its default value.'));
$this->assertNoText(t('This checkbox inside a vertical tab whose fieldset access is allowed does not have its default value.'));
$this->assertText(t('The form submitted correctly.'));
}
}

View file

@ -0,0 +1,87 @@
<?php
namespace Drupal\Tests\system\Functional\Form;
use Drupal\system\Tests\System\SystemConfigFormTestBase;
use Drupal\form_test\FormTestObject;
/**
* Tests building a form from an object.
*
* @group Form
*/
class FormObjectTest extends SystemConfigFormTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
protected function setUp() {
parent::setUp();
$this->form = new FormTestObject($this->container->get('config.factory'));
$this->values = [
'bananas' => [
'#value' => $this->randomString(10),
'#config_name' => 'form_test.object',
'#config_key' => 'bananas',
],
];
}
/**
* Tests using an object as the form callback.
*
* @see \Drupal\form_test\EventSubscriber\FormTestEventSubscriber::onKernelRequest()
*/
public function testObjectFormCallback() {
$config_factory = $this->container->get('config.factory');
$this->drupalGet('form-test/object-builder');
$this->assertText('The FormTestObject::buildForm() method was used for this form.');
$elements = $this->xpath('//form[@id="form-test-form-test-object"]');
$this->assertTrue(!empty($elements), 'The correct form ID was used.');
$this->drupalPostForm(NULL, ['bananas' => 'green'], t('Save'));
$this->assertText('The FormTestObject::validateForm() method was used for this form.');
$this->assertText('The FormTestObject::submitForm() method was used for this form.');
$value = $config_factory->get('form_test.object')->get('bananas');
$this->assertIdentical('green', $value);
$this->drupalGet('form-test/object-arguments-builder/yellow');
$this->assertText('The FormTestArgumentsObject::buildForm() method was used for this form.');
$elements = $this->xpath('//form[@id="form-test-form-test-arguments-object"]');
$this->assertTrue(!empty($elements), 'The correct form ID was used.');
$this->drupalPostForm(NULL, NULL, t('Save'));
$this->assertText('The FormTestArgumentsObject::validateForm() method was used for this form.');
$this->assertText('The FormTestArgumentsObject::submitForm() method was used for this form.');
$value = $config_factory->get('form_test.object')->get('bananas');
$this->assertIdentical('yellow', $value);
$this->drupalGet('form-test/object-service-builder');
$this->assertText('The FormTestServiceObject::buildForm() method was used for this form.');
$elements = $this->xpath('//form[@id="form-test-form-test-service-object"]');
$this->assertTrue(!empty($elements), 'The correct form ID was used.');
$this->drupalPostForm(NULL, ['bananas' => 'brown'], t('Save'));
$this->assertText('The FormTestServiceObject::validateForm() method was used for this form.');
$this->assertText('The FormTestServiceObject::submitForm() method was used for this form.');
$value = $config_factory->get('form_test.object')->get('bananas');
$this->assertIdentical('brown', $value);
$this->drupalGet('form-test/object-controller-builder');
$this->assertText('The FormTestControllerObject::create() method was used for this form.');
$this->assertText('The FormTestControllerObject::buildForm() method was used for this form.');
$elements = $this->xpath('//form[@id="form-test-form-test-controller-object"]');
$this->assertTrue(!empty($elements), 'The correct form ID was used.');
$this->assertText('custom_value', 'Ensure parameters are injected from request attributes.');
$this->assertText('request_value', 'Ensure the request object is injected.');
$this->drupalPostForm(NULL, ['bananas' => 'black'], t('Save'));
$this->assertText('The FormTestControllerObject::validateForm() method was used for this form.');
$this->assertText('The FormTestControllerObject::submitForm() method was used for this form.');
$value = $config_factory->get('form_test.object')->get('bananas');
$this->assertIdentical('black', $value);
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\system\Functional\Form;
use Drupal\Tests\BrowserTestBase;
/**
* Tests \Drupal\system\Form\ModulesListForm.
*
* @group Form
*/
class ModulesListFormWebTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['system_test', 'help'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
\Drupal::state()->set('system_test.module_hidden', FALSE);
}
/**
* Tests the module list form.
*/
public function testModuleListForm() {
$this->drupalLogin(
$this->drupalCreateUser(
['administer modules', 'administer permissions']
)
);
$this->drupalGet('admin/modules');
$this->assertResponse('200');
// Check that system_test's configure link was rendered correctly.
$this->assertFieldByXPath("//a[contains(@href, '/system-test/configure/bar') and text()='Configure ']/span[contains(@class, 'visually-hidden') and text()='the System test module']");
// Check that system_test's permissions link was rendered correctly.
$this->assertFieldByXPath("//a[contains(@href, '/admin/people/permissions#module-system_test') and @title='Configure permissions']");
// Check that system_test's help link was rendered correctly.
$this->assertFieldByXPath("//a[contains(@href, '/admin/help/system_test') and @title='Help']");
// Ensure that the Testing module's machine name is printed. Testing module
// is used because its machine name is different than its human readable
// name.
$this->assertText('simpletest');
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace Drupal\Tests\system\Functional\Form;
use Drupal\Tests\BrowserTestBase;
/**
* Tests form redirection functionality.
*
* @group Form
*/
class RedirectTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test', 'block'];
/**
* Tests form redirection.
*/
public function testRedirect() {
$path = 'form-test/redirect';
$options = ['query' => ['foo' => 'bar']];
$options['absolute'] = TRUE;
// Test basic redirection.
$edit = [
'redirection' => TRUE,
'destination' => $this->randomMachineName(),
];
$this->drupalPostForm($path, $edit, t('Submit'));
$this->assertUrl($edit['destination'], [], 'Basic redirection works.');
// Test without redirection.
$edit = [
'redirection' => FALSE,
];
$this->drupalPostForm($path, $edit, t('Submit'));
$this->assertUrl($path, [], 'When redirect is set to FALSE, there should be no redirection.');
// Test redirection with query parameters.
$edit = [
'redirection' => TRUE,
'destination' => $this->randomMachineName(),
];
$this->drupalPostForm($path, $edit, t('Submit'), $options);
$this->assertUrl($edit['destination'], [], 'Redirection with query parameters works.');
// Test without redirection but with query parameters.
$edit = [
'redirection' => FALSE,
];
$this->drupalPostForm($path, $edit, t('Submit'), $options);
$this->assertUrl($path, $options, 'When redirect is set to FALSE, there should be no redirection, and the query parameters should be passed along.');
// Test redirection back to the original path.
$edit = [
'redirection' => TRUE,
'destination' => '',
];
$this->drupalPostForm($path, $edit, t('Submit'));
$this->assertUrl($path, [], 'When using an empty redirection string, there should be no redirection.');
// Test redirection back to the original path with query parameters.
$edit = [
'redirection' => TRUE,
'destination' => '',
];
$this->drupalPostForm($path, $edit, t('Submit'), $options);
$this->assertUrl($path, $options, 'When using an empty redirection string, there should be no redirection, and the query parameters should be passed along.');
}
/**
* Tests form redirection from 404/403 pages with the Block form.
*/
public function testRedirectFromErrorPages() {
// Make sure the block containing the redirect form is placed.
$this->drupalPlaceBlock('redirect_form_block');
// Create a user that does not have permission to administer blocks.
$user = $this->drupalCreateUser(['administer themes']);
$this->drupalLogin($user);
// Visit page 'foo' (404 page) and submit the form. Verify it ends up
// at the right URL.
$expected = \Drupal::url('form_test.route1', [], ['query' => ['test1' => 'test2'], 'absolute' => TRUE]);
$this->drupalGet('foo');
$this->assertResponse(404);
$this->drupalPostForm(NULL, [], t('Submit'));
$this->assertResponse(200);
$this->assertUrl($expected, [], 'Redirected to correct URL/query.');
// Visit the block admin page (403 page) and submit the form. Verify it
// ends up at the right URL.
$this->drupalGet('admin/structure/block');
$this->assertResponse(403);
$this->drupalPostForm(NULL, [], t('Submit'));
$this->assertResponse(200);
$this->assertUrl($expected, [], 'Redirected to correct URL/query.');
}
}

View file

@ -0,0 +1,82 @@
<?php
namespace Drupal\Tests\system\Functional\Lock;
use Drupal\Tests\BrowserTestBase;
/**
* Confirm locking works between two separate requests.
*
* @group Lock
*/
class LockFunctionalTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system_test'];
/**
* Confirms that we can acquire and release locks in two parallel requests.
*/
public function testLockAcquire() {
$lock = $this->container->get('lock');
$lock_acquired = 'TRUE: Lock successfully acquired in \Drupal\system_test\Controller\SystemTestController::lockAcquire()';
$lock_not_acquired = 'FALSE: Lock not acquired in \Drupal\system_test\Controller\SystemTestController::lockAcquire()';
$this->assertTrue($lock->acquire('system_test_lock_acquire'), 'Lock acquired by this request.', 'Lock');
$this->assertTrue($lock->acquire('system_test_lock_acquire'), 'Lock extended by this request.', 'Lock');
$lock->release('system_test_lock_acquire');
// Cause another request to acquire the lock.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_acquired, 'Lock acquired by the other request.', 'Lock');
// The other request has finished, thus it should have released its lock.
$this->assertTrue($lock->acquire('system_test_lock_acquire'), 'Lock acquired by this request.', 'Lock');
// This request holds the lock, so the other request cannot acquire it.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_not_acquired, 'Lock not acquired by the other request.', 'Lock');
$lock->release('system_test_lock_acquire');
// Try a very short timeout and lock breaking.
$this->assertTrue($lock->acquire('system_test_lock_acquire', 0.5), 'Lock acquired by this request.', 'Lock');
sleep(1);
// The other request should break our lock.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_acquired, 'Lock acquired by the other request, breaking our lock.', 'Lock');
// We cannot renew it, since the other thread took it.
$this->assertFalse($lock->acquire('system_test_lock_acquire'), 'Lock cannot be extended by this request.', 'Lock');
// Check the shut-down function.
$lock_acquired_exit = 'TRUE: Lock successfully acquired in \Drupal\system_test\Controller\SystemTestController::lockExit()';
$this->drupalGet('system-test/lock-exit');
$this->assertText($lock_acquired_exit, 'Lock acquired by the other request before exit.', 'Lock');
$this->assertTrue($lock->acquire('system_test_lock_exit'), 'Lock acquired by this request after the other request exits.', 'Lock');
}
/**
* Tests that the persistent lock is persisted between requests.
*/
public function testPersistentLock() {
$persistent_lock = $this->container->get('lock.persistent');
// Get a persistent lock.
$this->drupalGet('system-test/lock-persist/lock1');
$this->assertText('TRUE: Lock successfully acquired in SystemTestController::lockPersist()');
// Ensure that a shutdown function has not released the lock.
$this->assertFalse($persistent_lock->lockMayBeAvailable('lock1'));
$this->drupalGet('system-test/lock-persist/lock1');
$this->assertText('FALSE: Lock not acquired in SystemTestController::lockPersist()');
// Get another persistent lock.
$this->drupalGet('system-test/lock-persist/lock2');
$this->assertText('TRUE: Lock successfully acquired in SystemTestController::lockPersist()');
$this->assertFalse($persistent_lock->lockMayBeAvailable('lock2'));
// Release the first lock and try getting it again.
$persistent_lock->release('lock1');
$this->drupalGet('system-test/lock-persist/lock1');
$this->assertText('TRUE: Lock successfully acquired in SystemTestController::lockPersist()');
}
}

View file

@ -0,0 +1,387 @@
<?php
namespace Drupal\Tests\system\Functional\Mail;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Site\Settings;
use Drupal\Tests\BrowserTestBase;
/**
* Tests for \Drupal\Core\Mail\MailFormatHelper::htmlToText().
*
* @group Mail
*/
class HtmlToTextTest extends BrowserTestBase {
/**
* Converts a string to its PHP source equivalent for display in test messages.
*
* @param $text
* The text string to convert.
*
* @return
* An HTML representation of the text string that, when displayed in a
* browser, represents the PHP source code equivalent of $text.
*/
protected function stringToHtml($text) {
return '"' .
str_replace(
["\n", ' '],
['\n', '&nbsp;'],
Html::escape($text)
) . '"';
}
/**
* Helper function to test \Drupal\Core\Mail\MailFormatHelper::htmlToText().
*
* @param $html
* The source HTML string to be converted.
* @param $text
* The expected result of converting $html to text.
* @param $message
* A text message to display in the assertion message.
* @param $allowed_tags
* (optional) An array of allowed tags, or NULL to default to the full
* set of tags supported by
* \Drupal\Core\Mail\MailFormatHelper::htmlToText().
*/
protected function assertHtmlToText($html, $text, $message, $allowed_tags = NULL) {
preg_match_all('/<([a-z0-6]+)/', Unicode::strtolower($html), $matches);
$tested_tags = implode(', ', array_unique($matches[1]));
$message .= ' (' . $tested_tags . ')';
$result = MailFormatHelper::htmlToText($html, $allowed_tags);
$pass = $this->assertEqual($result, $text, Html::escape($message));
$verbose = 'html = <pre>' . $this->stringToHtml($html)
. '</pre><br />' . 'result = <pre>' . $this->stringToHtml($result)
. '</pre><br />' . 'expected = <pre>' . $this->stringToHtml($text)
. '</pre>';
$this->verbose($verbose);
if (!$pass) {
$this->pass("Previous test verbose info:<br />$verbose");
}
}
/**
* Test supported tags of \Drupal\Core\Mail\MailFormatHelper::htmlToText().
*/
public function testTags() {
global $base_path, $base_url;
$tests = [
// @todo Trailing linefeeds should be trimmed.
'<a href = "https://www.drupal.org">Drupal.org</a>' => "Drupal.org [1]\n\n[1] https://www.drupal.org\n",
// @todo Footer URLs should be absolute.
"<a href = \"$base_path\">Homepage</a>" => "Homepage [1]\n\n[1] $base_url/\n",
'<address>Drupal</address>' => "Drupal\n",
// @todo The <address> tag is currently not supported.
'<address>Drupal</address><address>Drupal</address>' => "DrupalDrupal\n",
'<b>Drupal</b>' => "*Drupal*\n",
// @todo There should be a space between the '>' and the text.
'<blockquote>Drupal</blockquote>' => ">Drupal\n",
'<blockquote>Drupal</blockquote><blockquote>Drupal</blockquote>' => ">Drupal\n>Drupal\n",
'<br />Drupal<br />Drupal<br /><br />Drupal' => "Drupal\nDrupal\nDrupal\n",
'<br/>Drupal<br/>Drupal<br/><br/>Drupal' => "Drupal\nDrupal\nDrupal\n",
// @todo There should be two line breaks before the paragraph.
'<br/>Drupal<br/>Drupal<br/><br/>Drupal<p>Drupal</p>' => "Drupal\nDrupal\nDrupal\nDrupal\n\n",
'<div>Drupal</div>' => "Drupal\n",
// @todo The <div> tag is currently not supported.
'<div>Drupal</div><div>Drupal</div>' => "DrupalDrupal\n",
'<em>Drupal</em>' => "/Drupal/\n",
'<h1>Drupal</h1>' => "======== DRUPAL ==============================================================\n\n",
'<h1>Drupal</h1><p>Drupal</p>' => "======== DRUPAL ==============================================================\n\nDrupal\n\n",
'<h2>Drupal</h2>' => "-------- DRUPAL --------------------------------------------------------------\n\n",
'<h2>Drupal</h2><p>Drupal</p>' => "-------- DRUPAL --------------------------------------------------------------\n\nDrupal\n\n",
'<h3>Drupal</h3>' => ".... Drupal\n\n",
'<h3>Drupal</h3><p>Drupal</p>' => ".... Drupal\n\nDrupal\n\n",
'<h4>Drupal</h4>' => ".. Drupal\n\n",
'<h4>Drupal</h4><p>Drupal</p>' => ".. Drupal\n\nDrupal\n\n",
'<h5>Drupal</h5>' => "Drupal\n\n",
'<h5>Drupal</h5><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
'<h6>Drupal</h6>' => "Drupal\n\n",
'<h6>Drupal</h6><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
'<hr />Drupal<hr />' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n",
'<hr/>Drupal<hr/>' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n",
'<hr/>Drupal<hr/><p>Drupal</p>' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n\n",
'<i>Drupal</i>' => "/Drupal/\n",
'<p>Drupal</p>' => "Drupal\n\n",
'<p>Drupal</p><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
'<strong>Drupal</strong>' => "*Drupal*\n",
// @todo Tables are currently not supported.
'<table><tr><td>Drupal</td><td>Drupal</td></tr><tr><td>Drupal</td><td>Drupal</td></tr></table>' => "DrupalDrupalDrupalDrupal\n",
'<table><tr><td>Drupal</td></tr></table><p>Drupal</p>' => "Drupal\nDrupal\n\n",
// @todo The <u> tag is currently not supported.
'<u>Drupal</u>' => "Drupal\n",
'<ul><li>Drupal</li></ul>' => " * Drupal\n\n",
'<ul><li>Drupal <em>Drupal</em> Drupal</li></ul>' => " * Drupal /Drupal/ Drupal\n\n",
// @todo Lines containing nothing but spaces should be trimmed.
'<ul><li>Drupal</li><li><ol><li>Drupal</li><li>Drupal</li></ol></li></ul>' => " * Drupal\n * 1) Drupal\n 2) Drupal\n \n\n",
'<ul><li>Drupal</li><li><ol><li>Drupal</li></ol></li><li>Drupal</li></ul>' => " * Drupal\n * 1) Drupal\n \n * Drupal\n\n",
'<ul><li>Drupal</li><li>Drupal</li></ul>' => " * Drupal\n * Drupal\n\n",
'<ul><li>Drupal</li></ul><p>Drupal</p>' => " * Drupal\n\nDrupal\n\n",
'<ol><li>Drupal</li></ol>' => " 1) Drupal\n\n",
'<ol><li>Drupal</li><li><ul><li>Drupal</li><li>Drupal</li></ul></li></ol>' => " 1) Drupal\n 2) * Drupal\n * Drupal\n \n\n",
'<ol><li>Drupal</li><li>Drupal</li></ol>' => " 1) Drupal\n 2) Drupal\n\n",
'<ol>Drupal</ol>' => "Drupal\n\n",
'<ol><li>Drupal</li></ol><p>Drupal</p>' => " 1) Drupal\n\nDrupal\n\n",
'<dl><dt>Drupal</dt></dl>' => "Drupal\n\n",
'<dl><dt>Drupal</dt><dd>Drupal</dd></dl>' => "Drupal\n Drupal\n\n",
'<dl><dt>Drupal</dt><dd>Drupal</dd><dt>Drupal</dt><dd>Drupal</dd></dl>' => "Drupal\n Drupal\nDrupal\n Drupal\n\n",
'<dl><dt>Drupal</dt><dd>Drupal</dd></dl><p>Drupal</p>' => "Drupal\n Drupal\n\nDrupal\n\n",
'<dl><dt>Drupal<dd>Drupal</dl>' => "Drupal\n Drupal\n\n",
'<dl><dt>Drupal</dt></dl><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
// @todo Again, lines containing only spaces should be trimmed.
'<ul><li>Drupal</li><li><dl><dt>Drupal</dt><dd>Drupal</dd><dt>Drupal</dt><dd>Drupal</dd></dl></li><li>Drupal</li></ul>' => " * Drupal\n * Drupal\n Drupal\n Drupal\n Drupal\n \n * Drupal\n\n",
// Tests malformed HTML tags.
'<br>Drupal<br>Drupal' => "Drupal\nDrupal\n",
'<hr>Drupal<hr>Drupal' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n",
'<ol><li>Drupal<li>Drupal</ol>' => " 1) Drupal\n 2) Drupal\n\n",
'<ul><li>Drupal <em>Drupal</em> Drupal</ul></ul>' => " * Drupal /Drupal/ Drupal\n\n",
'<ul><li>Drupal<li>Drupal</ol>' => " * Drupal\n * Drupal\n\n",
'<ul><li>Drupal<li>Drupal</ul>' => " * Drupal\n * Drupal\n\n",
'<ul>Drupal</ul>' => "Drupal\n\n",
'Drupal</ul></ol></dl><li>Drupal' => "Drupal\n * Drupal\n",
'<dl>Drupal</dl>' => "Drupal\n\n",
'<dl>Drupal</dl><p>Drupal</p>' => "Drupal\n\nDrupal\n\n",
'<dt>Drupal</dt>' => "Drupal\n",
// Tests some unsupported HTML tags.
'<html>Drupal</html>' => "Drupal\n",
// @todo Perhaps the contents of <script> tags should be dropped.
'<script type="text/javascript">Drupal</script>' => "Drupal\n",
// A couple of tests for Unicode characters.
'<q>I <em>will</em> be back…</q>' => "I /will/ be back…\n",
'FrançAIS is ÜBER-åwesome' => "FrançAIS is ÜBER-åwesome\n",
];
foreach ($tests as $html => $text) {
$this->assertHtmlToText($html, $text, 'Supported tags');
}
}
/**
* Tests allowing tags in \Drupal\Core\Mail\MailFormatHelper::htmlToText().
*/
public function testDrupalHtmlToTextArgs() {
// The second parameter of \Drupal\Core\Mail\MailFormatHelper::htmlToText()
// overrules the allowed tags.
$this->assertHtmlToText(
'Drupal <b>Drupal</b> Drupal',
"Drupal *Drupal* Drupal\n",
'Allowed <b> tag found',
['b']
);
$this->assertHtmlToText(
'Drupal <h1>Drupal</h1> Drupal',
"Drupal Drupal Drupal\n",
'Disallowed <h1> tag not found',
['b']
);
$this->assertHtmlToText(
'Drupal <p><em><b>Drupal</b></em><p> Drupal',
"Drupal Drupal Drupal\n",
'Disallowed <p>, <em>, and <b> tags not found',
['a', 'br', 'h1']
);
$this->assertHtmlToText(
'<html><body>Drupal</body></html>',
"Drupal\n",
'Unsupported <html> and <body> tags not found',
['html', 'body']
);
}
/**
* Test that whitespace is collapsed.
*/
public function testDrupalHtmltoTextCollapsesWhitespace() {
$input = "<p>Drupal Drupal\n\nDrupal<pre>Drupal Drupal\n\nDrupal</pre>Drupal Drupal\n\nDrupal</p>";
// @todo The whitespace should be collapsed.
$collapsed = "Drupal Drupal\n\nDrupalDrupal Drupal\n\nDrupalDrupal Drupal\n\nDrupal\n\n";
$this->assertHtmlToText(
$input,
$collapsed,
'Whitespace is collapsed',
['p']
);
}
/**
* Test that text separated by block-level tags in HTML get separated by
* (at least) a newline in the plaintext version.
*/
public function testDrupalHtmlToTextBlockTagToNewline() {
$input = '[text]'
. '<blockquote>[blockquote]</blockquote>'
. '<br />[br]'
. '<dl><dt>[dl-dt]</dt>'
. '<dt>[dt]</dt>'
. '<dd>[dd]</dd>'
. '<dd>[dd-dl]</dd></dl>'
. '<h1>[h1]</h1>'
. '<h2>[h2]</h2>'
. '<h3>[h3]</h3>'
. '<h4>[h4]</h4>'
. '<h5>[h5]</h5>'
. '<h6>[h6]</h6>'
. '<hr />[hr]'
. '<ol><li>[ol-li]</li>'
. '<li>[li]</li>'
. '<li>[li-ol]</li></ol>'
. '<p>[p]</p>'
. '<ul><li>[ul-li]</li>'
. '<li>[li-ul]</li></ul>'
. '[text]';
$output = MailFormatHelper::htmlToText($input);
$pass = $this->assertFalse(
preg_match('/\][^\n]*\[/s', $output),
'Block-level HTML tags should force newlines'
);
if (!$pass) {
$this->verbose($this->stringToHtml($output));
}
$output_upper = Unicode::strtoupper($output);
$upper_input = Unicode::strtoupper($input);
$upper_output = MailFormatHelper::htmlToText($upper_input);
$pass = $this->assertEqual(
$upper_output,
$output_upper,
'Tag recognition should be case-insensitive'
);
if (!$pass) {
$this->verbose(
$upper_output
. '<br />should be equal to <br />'
. $output_upper
);
}
}
/**
* Test that headers are properly separated from surrounding text.
*/
public function testHeaderSeparation() {
$html = 'Drupal<h1>Drupal</h1>Drupal';
// @todo There should be more space above the header than below it.
$text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n";
$this->assertHtmlToText($html, $text,
'Text before and after <h1> tag');
$html = '<p>Drupal</p><h1>Drupal</h1>Drupal';
// @todo There should be more space above the header than below it.
$text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n";
$this->assertHtmlToText($html, $text,
'Paragraph before and text after <h1> tag');
$html = 'Drupal<h1>Drupal</h1><p>Drupal</p>';
// @todo There should be more space above the header than below it.
$text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n\n";
$this->assertHtmlToText($html, $text,
'Text before and paragraph after <h1> tag');
$html = '<p>Drupal</p><h1>Drupal</h1><p>Drupal</p>';
$text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n\n";
$this->assertHtmlToText($html, $text,
'Paragraph before and after <h1> tag');
}
/**
* Test that footnote references are properly generated.
*/
public function testFootnoteReferences() {
global $base_path, $base_url;
$source = '<a href="http://www.example.com/node/1">Host and path</a>'
. '<br /><a href="http://www.example.com">Host, no path</a>'
. '<br /><a href="' . $base_path . 'node/1">Path, no host</a>'
. '<br /><a href="node/1">Relative path</a>';
// @todo Footnote URLs should be absolute.
$tt = "Host and path [1]"
. "\nHost, no path [2]"
// @todo The following two references should be combined.
. "\nPath, no host [3]"
. "\nRelative path [4]"
. "\n"
. "\n[1] http://www.example.com/node/1"
. "\n[2] http://www.example.com"
// @todo The following two references should be combined.
. "\n[3] $base_url/node/1"
. "\n[4] node/1\n";
$this->assertHtmlToText($source, $tt, 'Footnotes');
}
/**
* Test that combinations of paragraph breaks, line breaks, linefeeds,
* and spaces are properly handled.
*/
public function testDrupalHtmlToTextParagraphs() {
$tests = [];
$tests[] = [
'html' => "<p>line 1<br />\nline 2<br />line 3\n<br />line 4</p><p>paragraph</p>",
// @todo Trailing line breaks should be trimmed.
'text' => "line 1\nline 2\nline 3\nline 4\n\nparagraph\n\n",
];
$tests[] = [
'html' => "<p>line 1<br /> line 2</p> <p>line 4<br /> line 5</p> <p>0</p>",
// @todo Trailing line breaks should be trimmed.
'text' => "line 1\nline 2\n\nline 4\nline 5\n\n0\n\n",
];
foreach ($tests as $test) {
$this->assertHtmlToText($test['html'], $test['text'], 'Paragraph breaks');
}
}
/**
* Tests \Drupal\Core\Mail\MailFormatHelper::htmlToText() wrapping.
*
* RFC 3676 says, "The Text/Plain media type is the lowest common
* denominator of Internet email, with lines of no more than 998 characters."
*
* RFC 2046 says, "SMTP [RFC-821] allows a maximum of 998 octets before the
* next CRLF sequence."
*
* RFC 821 says, "The maximum total length of a text line including the
* <CRLF> is 1000 characters."
*/
public function testVeryLongLineWrap() {
$input = 'Drupal<br /><p>' . str_repeat('x', 2100) . '</p><br />Drupal';
$output = MailFormatHelper::htmlToText($input);
$eol = Settings::get('mail_line_endings', PHP_EOL);
$maximum_line_length = 0;
foreach (explode($eol, $output) as $line) {
// We must use strlen() rather than Unicode::strlen() in order to count
// octets rather than characters.
$maximum_line_length = max($maximum_line_length, strlen($line . $eol));
}
$verbose = 'Maximum line length found was ' . $maximum_line_length . ' octets.';
$this->assertTrue($maximum_line_length <= 1000, $verbose);
}
/**
* Tests that trailing whitespace is removed before newlines.
*
* @see \Drupal\Core\Mail\MailFormatHelper::wrapMail()
*/
public function testRemoveTrailingWhitespace() {
$text = "Hi there! \nHerp Derp";
$mail_lines = explode("\n", MailFormatHelper::wrapMail($text));
$this->assertNotEqual(" ", substr($mail_lines[0], -1), 'Trailing whitespace removed.');
}
/**
* Tests that trailing whitespace from Usenet style signatures is not removed.
*
* RFC 3676 says, "This is a special case; an (optionally quoted or quoted and
* stuffed) line consisting of DASH DASH SP is neither fixed nor flowed."
*
* @see \Drupal\Core\Mail\MailFormatHelper::wrapMail()
*/
public function testUsenetSignature() {
$text = "Hi there!\n-- \nHerp Derp";
$mail_lines = explode("\n", MailFormatHelper::wrapMail($text));
$this->assertEqual("-- ", $mail_lines[1], 'Trailing whitespace not removed for dash-dash-space signatures.');
$text = "Hi there!\n-- \nHerp Derp";
$mail_lines = explode("\n", MailFormatHelper::wrapMail($text));
$this->assertEqual("--", $mail_lines[1], 'Trailing whitespace removed for incorrect dash-dash-space signatures.');
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace Drupal\Tests\system\Functional\Mail;
use Drupal\Core\Mail\Plugin\Mail\TestMailCollector;
use Drupal\Tests\BrowserTestBase;
use Drupal\system_mail_failure_test\Plugin\Mail\TestPhpMailFailure;
/**
* Performs tests on the pluggable mailing framework.
*
* @group Mail
*/
class MailTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['simpletest', 'system_mail_failure_test'];
/**
* Assert that the pluggable mail system is functional.
*/
public function testPluggableFramework() {
// Switch mail backends.
$this->config('system.mail')->set('interface.default', 'test_php_mail_failure')->save();
// Get the default MailInterface class instance.
$mail_backend = \Drupal::service('plugin.manager.mail')->getInstance(['module' => 'default', 'key' => 'default']);
// Assert whether the default mail backend is an instance of the expected
// class.
$this->assertTrue($mail_backend instanceof TestPhpMailFailure, 'Default mail interface can be swapped.');
// Add a module-specific mail backend.
$this->config('system.mail')->set('interface.mymodule_testkey', 'test_mail_collector')->save();
// Get the added MailInterface class instance.
$mail_backend = \Drupal::service('plugin.manager.mail')->getInstance(['module' => 'mymodule', 'key' => 'testkey']);
// Assert whether the added mail backend is an instance of the expected
// class.
$this->assertTrue($mail_backend instanceof TestMailCollector, 'Additional mail interfaces can be added.');
}
/**
* Test that message sending may be canceled.
*
* @see simpletest_mail_alter()
*/
public function testCancelMessage() {
$language_interface = \Drupal::languageManager()->getCurrentLanguage();
// Use the state system collector mail backend.
$this->config('system.mail')->set('interface.default', 'test_mail_collector')->save();
// Reset the state variable that holds sent messages.
\Drupal::state()->set('system.test_mail_collector', []);
// Send a test message that simpletest_mail_alter should cancel.
\Drupal::service('plugin.manager.mail')->mail('simpletest', 'cancel_test', 'cancel@example.com', $language_interface->getId());
// Retrieve sent message.
$captured_emails = \Drupal::state()->get('system.test_mail_collector');
$sent_message = end($captured_emails);
// Assert that the message was not actually sent.
$this->assertFalse($sent_message, 'Message was canceled.');
}
/**
* Checks the From: and Reply-to: headers.
*/
public function testFromAndReplyToHeader() {
$language = \Drupal::languageManager()->getCurrentLanguage();
// Use the state system collector mail backend.
$this->config('system.mail')->set('interface.default', 'test_mail_collector')->save();
// Reset the state variable that holds sent messages.
\Drupal::state()->set('system.test_mail_collector', []);
// Send an email with a reply-to address specified.
$from_email = 'Drupal <simpletest@example.com>';
$reply_email = 'someone_else@example.com';
\Drupal::service('plugin.manager.mail')->mail('simpletest', 'from_test', 'from_test@example.com', $language, [], $reply_email);
// Test that the reply-to email is just the email and not the site name
// and default sender email.
$captured_emails = \Drupal::state()->get('system.test_mail_collector');
$sent_message = end($captured_emails);
$this->assertEqual($from_email, $sent_message['headers']['From'], 'Message is sent from the site email account.');
$this->assertEqual($reply_email, $sent_message['headers']['Reply-to'], 'Message reply-to headers are set.');
$this->assertFalse(isset($sent_message['headers']['Errors-To']), 'Errors-to header must not be set, it is deprecated.');
// Send an email and check that the From-header contains the site name.
\Drupal::service('plugin.manager.mail')->mail('simpletest', 'from_test', 'from_test@example.com', $language);
$captured_emails = \Drupal::state()->get('system.test_mail_collector');
$sent_message = end($captured_emails);
$this->assertEqual($from_email, $sent_message['headers']['From'], 'Message is sent from the site email account.');
$this->assertFalse(isset($sent_message['headers']['Reply-to']), 'Message reply-to is not set if not specified.');
$this->assertFalse(isset($sent_message['headers']['Errors-To']), 'Errors-to header must not be set, it is deprecated.');
}
}

View file

@ -0,0 +1,68 @@
<?php
namespace Drupal\Tests\system\Functional\Menu;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the route access checks on menu links.
*
* @group Menu
*/
class MenuAccessTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'menu_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Tests menu link for route with access check.
*
* @see \Drupal\menu_test\Access\AccessCheck::access()
*/
public function testMenuBlockLinksAccessCheck() {
$this->drupalPlaceBlock('system_menu_block:account');
// Test that there's link rendered on the route.
$this->drupalGet('menu_test_access_check_session');
$this->assertLink('Test custom route access check');
// Page still accessible but thre should not be menu link.
$this->drupalGet('menu_test_access_check_session');
$this->assertResponse(200);
$this->assertNoLink('Test custom route access check');
// Test that page is no more accessible.
$this->drupalGet('menu_test_access_check_session');
$this->assertResponse(403);
// Check for access to a restricted local task from a default local task.
$this->drupalGet('foo/asdf');
$this->assertResponse(200);
$this->assertLinkByHref('foo/asdf');
$this->assertLinkByHref('foo/asdf/b');
$this->assertNoLinkByHref('foo/asdf/c');
// Attempt to access a restricted local task.
$this->drupalGet('foo/asdf/c');
$this->assertResponse(403);
$elements = $this->xpath('//ul[@class=:class]/li/a[@href=:href]', [
':class' => 'tabs primary',
':href' => Url::fromRoute('menu_test.router_test1', ['bar' => 'asdf'])->toString(),
]);
$this->assertTrue(empty($elements), 'No tab linking to foo/asdf found');
$this->assertNoLinkByHref('foo/asdf/b');
$this->assertNoLinkByHref('foo/asdf/c');
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\system\Functional\Menu;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\Tests\BrowserTestBase;
/**
* Ensures that menu links don't cause XSS issues.
*
* @group Menu
*/
class MenuLinkSecurityTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_link_content', 'block', 'menu_test'];
/**
* Ensures that a menu link does not cause an XSS issue.
*/
public function testMenuLink() {
$menu_link_content = MenuLinkContent::create([
'title' => '<script>alert("Wild animals")</script>',
'menu_name' => 'tools',
'link' => ['uri' => 'route:<front>'],
]);
$menu_link_content->save();
$this->drupalPlaceBlock('system_menu_block:tools');
$this->drupalGet('<front>');
$this->assertNoRaw('<script>alert("Wild animals")</script>');
$this->assertNoRaw('<script>alert("Even more wild animals")</script>');
$this->assertEscaped('<script>alert("Wild animals")</script>');
$this->assertEscaped('<script>alert("Even more wild animals")</script>');
}
}

View file

@ -0,0 +1,87 @@
<?php
namespace Drupal\Tests\system\Functional\Module;
use Drupal\Tests\BrowserTestBase;
/**
* Tests class loading for modules.
*
* @group Module
*/
class ClassLoaderTest extends BrowserTestBase {
/**
* The expected result from calling the module-provided class' method.
*/
protected $expected = 'Drupal\\module_autoload_test\\SomeClass::testMethod() was invoked.';
/**
* Tests that module-provided classes can be loaded when a module is enabled.
*
* @see \Drupal\module_autoload_test\SomeClass
*/
public function testClassLoading() {
// Enable the module_test and module_autoload_test modules.
\Drupal::service('module_installer')->install(['module_test', 'module_autoload_test'], FALSE);
$this->resetAll();
// Check twice to test an unprimed and primed system_list() cache.
for ($i = 0; $i < 2; $i++) {
$this->drupalGet('module-test/class-loading');
$this->assertResponse(200);
$this->assertText($this->expected, 'Autoloader loads classes from an enabled module.');
}
}
/**
* Tests that module-provided classes can't be loaded if module not installed.
*
* @see \Drupal\module_autoload_test\SomeClass
*/
public function testClassLoadingNotInstalledModules() {
// Enable the module_test module.
\Drupal::service('module_installer')->install(['module_test'], FALSE);
$this->resetAll();
// Check twice to test an unprimed and primed system_list() cache.
for ($i = 0; $i < 2; $i++) {
$this->drupalGet('module-test/class-loading');
$this->assertResponse(200);
$this->assertNoText($this->expected, 'Autoloader does not load classes from a disabled module.');
}
}
/**
* Tests that module-provided classes can't be loaded from disabled modules.
*
* @see \Drupal\module_autoload_test\SomeClass
*/
public function testClassLoadingDisabledModules() {
// Enable the module_test and module_autoload_test modules.
\Drupal::service('module_installer')->install(['module_test', 'module_autoload_test'], FALSE);
$this->resetAll();
// Ensure that module_autoload_test is disabled.
$this->container->get('module_installer')->uninstall(['module_autoload_test'], FALSE);
$this->resetAll();
// Check twice to test an unprimed and primed system_list() cache.
for ($i = 0; $i < 2; $i++) {
$this->drupalGet('module-test/class-loading');
$this->assertResponse(200);
$this->assertNoText($this->expected, 'Autoloader does not load classes from a disabled module.');
}
}
/**
* Ensures the negative caches in the class loader don't result in crashes.
*/
public function testMultipleModules() {
$this->drupalLogin($this->rootUser);
$edit = [
"modules[module_install_class_loader_test1][enable]" => TRUE,
"modules[module_install_class_loader_test2][enable]" => TRUE,
];
$this->drupalPostForm('admin/modules', $edit, t('Install'));
$this->rebuildContainer();
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('module_install_class_loader_test2'), 'The module_install_class_loader_test2 module has been installed.');
}
}

View file

@ -0,0 +1,133 @@
<?php
namespace Drupal\Tests\system\Functional\Module;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the installation of modules.
*
* @group Module
*/
class ExperimentalModuleTest extends BrowserTestBase {
/**
* The admin user.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'administer modules']);
$this->drupalLogin($this->adminUser);
}
/**
* Tests installing experimental modules and dependencies in the UI.
*/
public function testExperimentalConfirmForm() {
// First, test installing a non-experimental module with no dependencies.
// There should be no confirmation form and no experimental module warning.
$edit = [];
$edit["modules[test_page_test][enable]"] = TRUE;
$this->drupalPostForm('admin/modules', $edit, t('Install'));
$this->assertText('Module Test page has been enabled.');
$this->assertNoText('Experimental modules are provided for testing purposes only.');
// Uninstall the module.
\Drupal::service('module_installer')->uninstall(['test_page_test']);
// Next, test installing an experimental module with no dependencies.
// There should be a confirmation form with an experimental warning, but no
// list of dependencies.
$edit = [];
$edit["modules[experimental_module_test][enable]"] = TRUE;
$this->drupalPostForm('admin/modules', $edit, 'Install');
// The module should not be enabled and there should be a warning and a
// list of the experimental modules with only this one.
$this->assertNoText('Experimental Test has been enabled.');
$this->assertText('Experimental modules are provided for testing purposes only.');
$this->assertText('The following modules are experimental: Experimental Test');
// There should be no message about enabling dependencies.
$this->assertNoText('You must enable');
// Enable the module and confirm that it worked.
$this->drupalPostForm(NULL, [], 'Continue');
$this->assertText('Experimental Test has been enabled.');
// Uninstall the module.
\Drupal::service('module_installer')->uninstall(['experimental_module_test']);
// Test enabling a module that is not itself experimental, but that depends
// on an experimental module.
$edit = [];
$edit["modules[experimental_module_dependency_test][enable]"] = TRUE;
$this->drupalPostForm('admin/modules', $edit, 'Install');
// The module should not be enabled and there should be a warning and a
// list of the experimental modules with only this one.
$this->assertNoText('2 modules have been enabled: Experimental Dependency Test, Experimental Test');
$this->assertText('Experimental modules are provided for testing purposes only.');
$this->assertText('The following modules are experimental: Experimental Test');
// Ensure the non-experimental module is not listed as experimental.
$this->assertNoText('The following modules are experimental: Experimental Test, Experimental Dependency Test');
$this->assertNoText('The following modules are experimental: Experimental Dependency Test');
// There should be a message about enabling dependencies.
$this->assertText('You must enable the Experimental Test module to install Experimental Dependency Test');
// Enable the module and confirm that it worked.
$this->drupalPostForm(NULL, [], 'Continue');
$this->assertText('2 modules have been enabled: Experimental Dependency Test, Experimental Test');
// Uninstall the modules.
\Drupal::service('module_installer')->uninstall(['experimental_module_test', 'experimental_module_dependency_test']);
// Finally, check both the module and its experimental dependency. There is
// still a warning about experimental modules, but no message about
// dependencies, since the user specifically enabled the dependency.
$edit = [];
$edit["modules[experimental_module_test][enable]"] = TRUE;
$edit["modules[experimental_module_dependency_test][enable]"] = TRUE;
$this->drupalPostForm('admin/modules', $edit, 'Install');
// The module should not be enabled and there should be a warning and a
// list of the experimental modules with only this one.
$this->assertNoText('2 modules have been enabled: Experimental Dependency Test, Experimental Test');
$this->assertText('Experimental modules are provided for testing purposes only.');
$this->assertText('The following modules are experimental: Experimental Test');
// Ensure the non-experimental module is not listed as experimental.
$this->assertNoText('The following modules are experimental: Experimental Dependency Test, Experimental Test');
$this->assertNoText('The following modules are experimental: Experimental Dependency Test');
// There should be no message about enabling dependencies.
$this->assertNoText('You must enable');
// Enable the module and confirm that it worked.
$this->drupalPostForm(NULL, [], 'Continue');
$this->assertText('2 modules have been enabled: Experimental Dependency Test, Experimental Test');
// Try to enable an experimental module that can not be due to
// hook_requirements().
\Drupal::state()->set('experimental_module_requirements_test_requirements', TRUE);
$edit = [];
$edit["modules[experimental_module_requirements_test][enable]"] = TRUE;
$this->drupalPostForm('admin/modules', $edit, 'Install');
$this->assertUrl('admin/modules', [], 'If the module can not be installed we are not taken to the confirm form.');
$this->assertText('The Experimental Test Requirements module can not be installed.');
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace Drupal\Tests\system\Functional\Module;
use Drupal\Core\Extension\ExtensionNameLengthException;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the installation of modules.
*
* @group Module
*/
class InstallTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['module_test'];
/**
* Verify that drupal_get_schema() can be used during module installation.
*/
public function testGetSchemaAtInstallTime() {
// @see module_test_install()
$value = db_query("SELECT data FROM {module_test}")->fetchField();
$this->assertIdentical($value, 'varchar');
}
/**
* Tests enabling User module once more.
*
* Regression: The installer might enable a module twice due to automatic
* dependency resolution. A bug caused the stored weight for User module to
* be an array.
*/
public function testEnableUserTwice() {
\Drupal::service('module_installer')->install(['user'], FALSE);
$this->assertIdentical($this->config('core.extension')->get('module.user'), 0);
}
/**
* Tests recorded schema versions of early installed modules in the installer.
*/
public function testRequiredModuleSchemaVersions() {
$version = drupal_get_installed_schema_version('system', TRUE);
$this->assertTrue($version > 0, 'System module version is > 0.');
$version = drupal_get_installed_schema_version('user', TRUE);
$this->assertTrue($version > 0, 'User module version is > 0.');
$post_update_key_value = \Drupal::keyValue('post_update');
$existing_updates = $post_update_key_value->get('existing_updates', []);
$this->assertTrue(in_array('module_test_post_update_test', $existing_updates));
}
/**
* Ensures that post update functions are removed on uninstall.
*/
public function testUninstallPostUpdateFunctions() {
\Drupal::service('module_installer')->uninstall(['module_test']);
$post_update_key_value = \Drupal::keyValue('post_update');
$existing_updates = $post_update_key_value->get('existing_updates', []);
$this->assertFalse(in_array('module_test_post_update_test', $existing_updates));
}
/**
* Tests that an exception is thrown when a module name is too long.
*/
public function testModuleNameLength() {
$module_name = 'invalid_module_name_over_the_maximum_allowed_character_length';
$message = format_string('Exception thrown when enabling module %name with a name length over the allowed maximum', ['%name' => $module_name]);
try {
$this->container->get('module_installer')->install([$module_name]);
$this->fail($message);
}
catch (ExtensionNameLengthException $e) {
$this->pass($message);
}
// Since for the UI, the submit callback uses FALSE, test that too.
$message = format_string('Exception thrown when enabling as if via the UI the module %name with a name length over the allowed maximum', ['%name' => $module_name]);
try {
$this->container->get('module_installer')->install([$module_name], FALSE);
$this->fail($message);
}
catch (ExtensionNameLengthException $e) {
$this->pass($message);
}
}
}

View file

@ -0,0 +1,194 @@
<?php
namespace Drupal\Tests\system\Functional\Module;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Database\Database;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Tests\BrowserTestBase;
/**
* Helper class for module test cases.
*/
abstract class ModuleTestBase extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system_test'];
protected $adminUser;
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'administer modules']);
$this->drupalLogin($this->adminUser);
}
/**
* Assert there are tables that begin with the specified base table name.
*
* @param $base_table
* Beginning of table name to look for.
* @param $count
* (optional) Whether or not to assert that there are tables that match the
* specified base table. Defaults to TRUE.
*/
public function assertTableCount($base_table, $count = TRUE) {
$tables = db_find_tables(Database::getConnection()->prefixTables('{' . $base_table . '}') . '%');
if ($count) {
return $this->assertTrue($tables, format_string('Tables matching "@base_table" found.', ['@base_table' => $base_table]));
}
return $this->assertFalse($tables, format_string('Tables matching "@base_table" not found.', ['@base_table' => $base_table]));
}
/**
* Assert that all tables defined in a module's hook_schema() exist.
*
* @param $module
* The name of the module.
*/
public function assertModuleTablesExist($module) {
$tables = array_keys(drupal_get_module_schema($module));
$tables_exist = TRUE;
foreach ($tables as $table) {
if (!db_table_exists($table)) {
$tables_exist = FALSE;
}
}
return $this->assertTrue($tables_exist, format_string('All database tables defined by the @module module exist.', ['@module' => $module]));
}
/**
* Assert that none of the tables defined in a module's hook_schema() exist.
*
* @param $module
* The name of the module.
*/
public function assertModuleTablesDoNotExist($module) {
$tables = array_keys(drupal_get_module_schema($module));
$tables_exist = FALSE;
foreach ($tables as $table) {
if (db_table_exists($table)) {
$tables_exist = TRUE;
}
}
return $this->assertFalse($tables_exist, format_string('None of the database tables defined by the @module module exist.', ['@module' => $module]));
}
/**
* Asserts that the default configuration of a module has been installed.
*
* @param string $module
* The name of the module.
*
* @return bool
* TRUE if configuration has been installed, FALSE otherwise.
*/
public function assertModuleConfig($module) {
$module_config_dir = drupal_get_path('module', $module) . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
if (!is_dir($module_config_dir)) {
return;
}
$module_file_storage = new FileStorage($module_config_dir);
// Verify that the module's default config directory is not empty and
// contains default configuration files (instead of something else).
$all_names = $module_file_storage->listAll();
if (empty($all_names)) {
// Module has an empty config directory. For example it might contain a
// schema directory.
return;
}
$this->assertTrue($all_names);
// Look up each default configuration object name in the active
// configuration, and if it exists, remove it from the stack.
// Only default config that belongs to $module is guaranteed to exist; any
// other default config depends on whether other modules are enabled. Thus,
// list all default config once more, but filtered by $module.
$names = $module_file_storage->listAll($module . '.');
foreach ($names as $key => $name) {
if ($this->config($name)->get()) {
unset($names[$key]);
}
}
// Verify that all configuration has been installed (which means that $names
// is empty).
return $this->assertFalse($names, format_string('All default configuration of @module module found.', ['@module' => $module]));
}
/**
* Asserts that no configuration exists for a given module.
*
* @param string $module
* The name of the module.
*
* @return bool
* TRUE if no configuration was found, FALSE otherwise.
*/
public function assertNoModuleConfig($module) {
$names = \Drupal::configFactory()->listAll($module . '.');
return $this->assertFalse($names, format_string('No configuration found for @module module.', ['@module' => $module]));
}
/**
* Assert the list of modules are enabled or disabled.
*
* @param $modules
* Module list to check.
* @param $enabled
* Expected module state.
*/
public function assertModules(array $modules, $enabled) {
$this->rebuildContainer();
foreach ($modules as $module) {
if ($enabled) {
$message = 'Module "@module" is enabled.';
}
else {
$message = 'Module "@module" is not enabled.';
}
$this->assertEqual($this->container->get('module_handler')->moduleExists($module), $enabled, format_string($message, ['@module' => $module]));
}
}
/**
* Verify a log entry was entered for a module's status change.
*
* @param $type
* The category to which this message belongs.
* @param $message
* The message to store in the log. Keep $message translatable
* by not concatenating dynamic values into it! Variables in the
* message should be added by using placeholder strings alongside
* the variables argument to declare the value of the placeholders.
* See t() for documentation on how $message and $variables interact.
* @param $variables
* Array of variables to replace in the message on display or
* NULL if message is already translated or not possible to
* translate.
* @param $severity
* The severity of the message, as per RFC 3164.
* @param $link
* A link to associate with the message.
*/
public function assertLogMessage($type, $message, $variables = [], $severity = RfcLogLevel::NOTICE, $link = '') {
$count = db_select('watchdog', 'w')
->condition('type', $type)
->condition('message', $message)
->condition('variables', serialize($variables))
->condition('severity', $severity)
->condition('link', $link)
->countQuery()
->execute()
->fetchField();
$this->assertTrue($count > 0, format_string('watchdog table contains @count rows for @message', ['@count' => $count, '@message' => format_string($message, $variables)]));
}
}

View file

@ -0,0 +1,158 @@
<?php
namespace Drupal\Tests\system\Functional\Module;
use Drupal\Core\Cache\Cache;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityMalformedException;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the uninstallation of modules.
*
* @group Module
*/
class UninstallTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['module_test', 'user', 'views', 'node'];
/**
* Tests the hook_modules_uninstalled() of the user module.
*/
public function testUserPermsUninstalled() {
// Uninstalls the module_test module, so hook_modules_uninstalled()
// is executed.
$this->container->get('module_installer')->uninstall(['module_test']);
// Are the perms defined by module_test removed?
$this->assertFalse(user_roles(FALSE, 'module_test perm'), 'Permissions were all removed.');
}
/**
* Tests the Uninstall page and Uninstall confirmation page.
*/
public function testUninstallPage() {
$account = $this->drupalCreateUser(['administer modules']);
$this->drupalLogin($account);
// Create a node type.
$node_type = NodeType::create(['type' => 'uninstall_blocker', 'name' => 'Uninstall blocker']);
// Create a dependency that can be fixed.
$node_type->setThirdPartySetting('module_test', 'key', 'value');
$node_type->save();
// Add a node to prevent node from being uninstalled.
$node = Node::create([
'type' => 'uninstall_blocker',
'title' => $this->randomString(),
]);
$node->save();
$this->drupalGet('admin/modules/uninstall');
$this->assertTitle(t('Uninstall') . ' | Drupal');
// Be sure labels are rendered properly.
// @see regression https://www.drupal.org/node/2512106
$this->assertRaw('<label for="edit-uninstall-node" class="module-name table-filter-text-source">Node</label>');
$this->assertText(\Drupal::translation()->translate('The following reason prevents Node from being uninstalled:'));
$this->assertText(\Drupal::translation()->translate('There is content for the entity type: Content'));
// Delete the node to allow node to be uninstalled.
$node->delete();
// Uninstall module_test.
$edit = [];
$edit['uninstall[module_test]'] = TRUE;
$this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall'));
$this->assertNoText(\Drupal::translation()->translate('Configuration deletions'), 'No configuration deletions listed on the module install confirmation page.');
$this->assertText(\Drupal::translation()->translate('Configuration updates'), 'Configuration updates listed on the module install confirmation page.');
$this->assertText($node_type->label());
$this->drupalPostForm(NULL, NULL, t('Uninstall'));
$this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
// Uninstall node testing that the configuration that will be deleted is
// listed.
$node_dependencies = \Drupal::service('config.manager')->findConfigEntityDependentsAsEntities('module', ['node']);
$edit = [];
$edit['uninstall[node]'] = TRUE;
$this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall'));
$this->assertText(\Drupal::translation()->translate('Configuration deletions'), 'Configuration deletions listed on the module install confirmation page.');
$this->assertNoText(\Drupal::translation()->translate('Configuration updates'), 'No configuration updates listed on the module install confirmation page.');
$entity_types = [];
foreach ($node_dependencies as $entity) {
$label = $entity->label() ?: $entity->id();
$this->assertText($label);
$entity_types[] = $entity->getEntityTypeId();
}
$entity_types = array_unique($entity_types);
foreach ($entity_types as $entity_type_id) {
$entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
// Add h3's since the entity type label is often repeated in the entity
// labels.
$this->assertRaw('<h3>' . $entity_type->getLabel() . '</h3>');
}
// Set a unique cache entry to be able to test whether all caches are
// cleared during the uninstall.
\Drupal::cache()->set('uninstall_test', 'test_uninstall_page', Cache::PERMANENT);
$cached = \Drupal::cache()->get('uninstall_test');
$this->assertEqual($cached->data, 'test_uninstall_page', SafeMarkup::format('Cache entry found: @bin', ['@bin' => $cached->data]));
$this->drupalPostForm(NULL, NULL, t('Uninstall'));
$this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
$this->assertNoRaw('&lt;label', 'The page does not have double escaped HTML tags.');
// Make sure our unique cache entry is gone.
$cached = \Drupal::cache()->get('uninstall_test');
$this->assertFalse($cached, 'Cache entry not found');
// Make sure we get an error message when we try to confirm uninstallation
// of an empty list of modules.
$this->drupalGet('admin/modules/uninstall/confirm');
$this->assertText(t('The selected modules could not be uninstalled, either due to a website problem or due to the uninstall confirmation form timing out. Please try again.'), 'Module uninstall confirmation form displays error message');
// Make sure confirmation page is accessible only during uninstall process.
$this->drupalGet('admin/modules/uninstall/confirm');
$this->assertUrl('admin/modules/uninstall');
$this->assertTitle(t('Uninstall') . ' | Drupal');
// Make sure the correct error is shown when no modules are selected.
$edit = [];
$this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall'));
$this->assertText(t('No modules selected.'), 'No module is selected to uninstall');
}
/**
* Tests that a module which fails to install can still be uninstalled.
*/
public function testFailedInstallStatus() {
$account = $this->drupalCreateUser(['administer modules']);
$this->drupalLogin($account);
$message = 'Exception thrown when installing module_installer_config_test with an invalid configuration file.';
try {
$this->container->get('module_installer')->install(['module_installer_config_test']);
$this->fail($message);
}
catch (EntityMalformedException $e) {
$this->pass($message);
}
// Even though the module failed to install properly, its configuration
// status is "enabled" and should still be available to uninstall.
$this->drupalGet('admin/modules/uninstall');
$this->assertText('Module installer config test');
$edit['uninstall[module_installer_config_test]'] = TRUE;
$this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall'));
$this->drupalPostForm(NULL, NULL, t('Uninstall'));
$this->assertText(t('The selected modules have been uninstalled.'));
$this->assertNoText('Module installer config test');
}
}

View file

@ -0,0 +1,82 @@
<?php
namespace Drupal\Tests\system\Functional\ParamConverter;
use Drupal\Tests\BrowserTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests upcasting of url arguments to entities.
*
* @group ParamConverter
*/
class UpcastingTest extends BrowserTestBase {
public static $modules = ['paramconverter_test', 'node', 'language'];
/**
* Confirms that all parameters are converted as expected.
*
* All of these requests end up being processed by a controller with the
* signature: f($user, $node, $foo) returning either values or labels
* like "user: Dries, node: First post, foo: bar"
*
* The tests shuffle the parameters around an checks if the right thing is
* happening.
*/
public function testUpcasting() {
$node = $this->drupalCreateNode(['title' => $this->randomMachineName(8)]);
$user = $this->drupalCreateUser(['access content']);
$foo = 'bar';
// paramconverter_test/test_user_node_foo/{user}/{node}/{foo}
$this->drupalGet("paramconverter_test/test_user_node_foo/" . $user->id() . '/' . $node->id() . "/$foo");
$this->assertRaw("user: {$user->label()}, node: {$node->label()}, foo: $foo", 'user and node upcast by entity name');
// paramconverter_test/test_node_user_user/{node}/{foo}/{user}
// options.parameters.foo.type = entity:user
$this->drupalGet("paramconverter_test/test_node_user_user/" . $node->id() . "/" . $user->id() . "/" . $user->id());
$this->assertRaw("user: {$user->label()}, node: {$node->label()}, foo: {$user->label()}", 'foo converted to user as well');
// paramconverter_test/test_node_node_foo/{user}/{node}/{foo}
// options.parameters.user.type = entity:node
$this->drupalGet("paramconverter_test/test_node_node_foo/" . $node->id() . "/" . $node->id() . "/$foo");
$this->assertRaw("user: {$node->label()}, node: {$node->label()}, foo: $foo", 'user is upcast to node (rather than to user)');
}
/**
* Confirms we can upcast to controller arguments of the same type.
*/
public function testSameTypes() {
$node = $this->drupalCreateNode(['title' => $this->randomMachineName(8)]);
$parent = $this->drupalCreateNode(['title' => $this->randomMachineName(8)]);
// paramconverter_test/node/{node}/set/parent/{parent}
// options.parameters.parent.type = entity:node
$this->drupalGet("paramconverter_test/node/" . $node->id() . "/set/parent/" . $parent->id());
$this->assertRaw("Setting '" . $parent->getTitle() . "' as parent of '" . $node->getTitle() . "'.");
}
/**
* Confirms entity is shown in user's language by default.
*/
public function testEntityLanguage() {
$language = ConfigurableLanguage::createFromLangcode('de');
$language->save();
\Drupal::configFactory()->getEditable('language.negotiation')
->set('url.prefixes', ['de' => 'de'])
->save();
// The container must be recreated after adding a new language.
$this->rebuildContainer();
$node = $this->drupalCreateNode(['title' => 'English label']);
$translation = $node->addTranslation('de');
$translation->setTitle('Deutscher Titel')->save();
$this->drupalGet("/paramconverter_test/node/" . $node->id() . "/test_language");
$this->assertRaw("English label");
$this->drupalGet("paramconverter_test/node/" . $node->id() . "/test_language", ['language' => $language]);
$this->assertRaw("Deutscher Titel");
}
}

View file

@ -0,0 +1,118 @@
<?php
namespace Drupal\Tests\system\Functional\Path;
use Drupal\Core\Database\Database;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
use Drupal\taxonomy\Entity\Term;
/**
* Tests altering the inbound path and the outbound path.
*
* @group Path
*/
class UrlAlterFunctionalTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['path', 'forum', 'url_alter_test'];
/**
* Test that URL altering works and that it occurs in the correct order.
*/
public function testUrlAlter() {
// Ensure that the url_alias table exists after Drupal installation.
$this->assertTrue(Database::getConnection()->schema()->tableExists('url_alias'), 'The url_alias table exists after Drupal installation.');
// User names can have quotes and plus signs so we should ensure that URL
// altering works with this.
$account = $this->drupalCreateUser(['administer url aliases'], "a'foo+bar");
$this->drupalLogin($account);
$uid = $account->id();
$name = $account->getUsername();
// Test a single altered path.
$this->drupalGet("user/$name");
$this->assertResponse('200', 'The user/username path gets resolved correctly');
$this->assertUrlOutboundAlter("/user/$uid", "/user/$name");
// Test that a path always uses its alias.
$path = ['source' => "/user/$uid/test1", 'alias' => '/alias/test1'];
$this->container->get('path.alias_storage')->save($path['source'], $path['alias']);
$this->rebuildContainer();
$this->assertUrlInboundAlter('/alias/test1', "/user/$uid/test1");
$this->assertUrlOutboundAlter("/user/$uid/test1", '/alias/test1');
// Test adding an alias via the UI.
$edit = ['source' => "/user/$uid/edit", 'alias' => '/alias/test2'];
$this->drupalPostForm('admin/config/search/path/add', $edit, t('Save'));
$this->assertText(t('The alias has been saved.'));
$this->drupalGet('alias/test2');
$this->assertResponse('200', 'The path alias gets resolved correctly');
$this->assertUrlOutboundAlter("/user/$uid/edit", '/alias/test2');
// Test a non-existent user is not altered.
$uid++;
$this->assertUrlOutboundAlter("/user/$uid", "/user/$uid");
// Test that 'forum' is altered to 'community' correctly, both at the root
// level and for a specific existing forum.
$this->drupalGet('community');
$this->assertText('General discussion', 'The community path gets resolved correctly');
$this->assertUrlOutboundAlter('/forum', '/community');
$forum_vid = $this->config('forum.settings')->get('vocabulary');
$term_name = $this->randomMachineName();
$term = Term::create([
'name' => $term_name,
'vid' => $forum_vid,
]);
$term->save();
$this->drupalGet("community/" . $term->id());
$this->assertText($term_name, 'The community/{tid} path gets resolved correctly');
$this->assertUrlOutboundAlter("/forum/" . $term->id(), "/community/" . $term->id());
// Test outbound query string altering.
$url = Url::fromRoute('user.login');
$this->assertIdentical(\Drupal::request()->getBaseUrl() . '/user/login?foo=bar', $url->toString());
}
/**
* Assert that an outbound path is altered to an expected value.
*
* @param $original
* A string with the original path that is run through generateFrommPath().
* @param $final
* A string with the expected result after generateFrommPath().
*
* @return
* TRUE if $original was correctly altered to $final, FALSE otherwise.
*/
protected function assertUrlOutboundAlter($original, $final) {
// Test outbound altering.
$result = $this->container->get('path_processor_manager')->processOutbound($original);
return $this->assertIdentical($result, $final, format_string('Altered outbound URL %original, expected %final, and got %result.', ['%original' => $original, '%final' => $final, '%result' => $result]));
}
/**
* Assert that a inbound path is altered to an expected value.
*
* @param $original
* The original path before it has been altered by inbound URL processing.
* @param $final
* A string with the expected result.
*
* @return
* TRUE if $original was correctly altered to $final, FALSE otherwise.
*/
protected function assertUrlInboundAlter($original, $final) {
// Test inbound altering.
$result = $this->container->get('path.alias_manager')->getPathByAlias($original);
return $this->assertIdentical($result, $final, format_string('Altered inbound URL %original, expected %final, and got %result.', ['%original' => $original, '%final' => $final, '%result' => $result]));
}
}

View file

@ -0,0 +1,119 @@
<?php
namespace Drupal\Tests\system\Functional\Render;
use Drupal\Tests\BrowserTestBase;
/**
* Performs tests for the effects of the ajax_page_state query parameter.
*
* @group Render
*/
class AjaxPageStateTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['node', 'views'];
/**
* User account with all available permissions
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $adminUser;
protected function setUp() {
parent::setUp();
// Create an administrator with all permissions.
$this->adminUser = $this->drupalCreateUser(array_keys(\Drupal::service('user.permissions')
->getPermissions()));
// Log in so there are more libraries to test with otherwise only html5shiv
// is the only one in the source we can easily test for.
$this->drupalLogin($this->adminUser);
}
/**
* Default functionality without the param ajax_page_state[libraries].
*
* The libraries html5shiv and drupalSettings are loaded default from core
* and available in code as scripts. Do this as the base test.
*/
public function testLibrariesAvailable() {
$this->drupalGet('node', []);
$this->assertRaw(
'/core/assets/vendor/html5shiv/html5shiv.min.js',
'The html5shiv library from core should be loaded.'
);
$this->assertRaw(
'/core/misc/drupalSettingsLoader.js',
'The drupalSettings library from core should be loaded.'
);
}
/**
* Give ajax_page_state[libraries]=core/html5shiv to exclude the library.
*
* When called with ajax_page_state[libraries]=core/html5shiv the library
* should be excluded as it is already loaded. This should not affect other
* libraries so test if drupalSettings is still available.
*/
public function testHtml5ShivIsNotLoaded() {
$this->drupalGet('node',
[
"query" =>
[
'ajax_page_state' => [
'libraries' => 'core/html5shiv'
]
]
]
);
$this->assertNoRaw(
'/core/assets/vendor/html5shiv/html5shiv.min.js',
'The html5shiv library from core should be excluded from loading'
);
$this->assertRaw(
'/core/misc/drupalSettingsLoader.js',
'The drupalSettings library from core should be loaded.'
);
}
/**
* Test if multiple libraries can be excluded.
*
* The ajax_page_state[libraries] should be able to support multiple libraries
* comma separated.
*/
public function testMultipleLibrariesAreNotLoaded() {
$this->drupalGet('node',
['query' => ['ajax_page_state' => ['libraries' => 'core/html5shiv,core/drupalSettings']]]
);
$this->assertResponse(200);
$this->assertNoRaw(
'/core/assets/vendor/html5shiv/html5shiv.min.js',
'The html5shiv library from core should be excluded from loading.'
);
$this->assertNoRaw(
'/core/misc/drupalSettingsLoader.js',
'The drupalSettings library from core should be excluded from loading.'
);
$this->drupalGet('node');
$this->assertRaw(
'/core/assets/vendor/html5shiv/html5shiv.min.js',
'The html5shiv library from core should be included in loading.'
);
$this->assertRaw(
'/core/misc/drupalSettingsLoader.js',
'The drupalSettings library from core should be included in loading.'
);
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\system\Functional\Render;
use Drupal\Tests\BrowserTestBase;
/**
* Tests selecting a display variant.
*
* @group Render
*/
class DisplayVariantTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['display_variant_test'];
/**
* Tests selecting the variant and passing configuration.
*/
public function testPageDisplayVariantSelectionEvent() {
// Tests that our display variant was selected, and that its configuration
// was passed correctly. If the configuration wasn't passed, we'd get an
// error page here.
$this->drupalGet('<front>');
$this->assertRaw('A very important, required value.');
$this->assertRaw('Explicitly passed in context.');
$this->assertCacheTag('custom_cache_tag');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\system\Functional\Routing;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
/**
* A mock matcher that can be configured with any matching logic for testing.
*/
class MockMatcher implements RequestMatcherInterface {
/**
* The matcher being tested.
*/
protected $matcher;
/**
* Constructs a MockMatcher object.
*
* @param \Closure $matcher
* An anonymous function that will be used for the matchRequest() method.
*/
public function __construct(\Closure $matcher) {
$this->matcher = $matcher;
}
/**
* {@inheritdoc}
*/
public function matchRequest(Request $request) {
$matcher = $this->matcher;
return $matcher($request);
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\system\Functional\Routing;
use Drupal\Tests\BrowserTestBase;
/**
* Function Tests for the routing permission system.
*
* @group Routing
*/
class RouterPermissionTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['router_test'];
/**
* Tests permission requirements on routes.
*/
public function testPermissionAccess() {
$path = 'router_test/test7';
$this->drupalGet($path);
$this->assertResponse(403, "Access denied for a route where we don't have a permission");
$this->drupalGet('router_test/test8');
$this->assertResponse(403, 'Access denied by default if no access specified');
$user = $this->drupalCreateUser(['access test7']);
$this->drupalLogin($user);
$this->drupalGet('router_test/test7');
$this->assertResponse(200);
$this->assertNoRaw('Access denied');
$this->assertRaw('test7text', 'The correct string was returned because the route was successful.');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\system\Functional\ServiceProvider;
use Drupal\Tests\BrowserTestBase;
/**
* Tests service provider registration to the DIC.
*
* @group ServiceProvider
*/
class ServiceProviderWebTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['file', 'service_provider_test'];
/**
* Tests that module service providers get registered to the DIC.
*
* Also tests that services provided by module service providers get
* registered to the DIC.
*/
public function testServiceProviderRegistrationIntegration() {
$this->assertTrue(\Drupal::hasService('service_provider_test_class'), 'The service_provider_test_class service has been registered to the DIC');
// The event subscriber method in the test class calls drupal_set_message()
// with a message saying it has fired. This will fire on every page request
// so it should show up on the front page.
$this->drupalGet('');
$this->assertText(t('The service_provider_test event subscriber fired!'), 'The service_provider_test event subscriber fired');
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
/**
* Confirm that the fingerprinting meta tag appears as expected.
*
* @group system
*/
class AdminMetaTagTest extends BrowserTestBase {
/**
* Verify that the meta tag HTML is generated correctly.
*/
public function testMetaTag() {
list($version, ) = explode('.', \Drupal::VERSION);
$string = '<meta name="Generator" content="Drupal ' . $version . ' (https://www.drupal.org)" />';
$this->drupalGet('node');
$this->assertRaw($string, 'Fingerprinting meta tag generated correctly.', 'System');
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the locked functionality of date formats.
*
* @group system
*/
class DateFormatsLockedTest extends BrowserTestBase {
/**
* Tests attempts at listing, editing, and deleting locked date formats.
*/
public function testDateLocking() {
$this->drupalLogin($this->rootUser);
// Locked date formats are not linked on the listing page, locked date
// formats are clearly marked as such; unlocked formats are not marked as
// "locked".
$this->drupalGet('admin/config/regional/date-time');
$this->assertLinkByHref('admin/config/regional/date-time/formats/manage/short');
$this->assertNoLinkByHref('admin/config/regional/date-time/formats/manage/html_date');
$this->assertText('Fallback date format');
$this->assertNoText('short (locked)');
// Locked date formats are not editable.
$this->drupalGet('admin/config/regional/date-time/formats/manage/short');
$this->assertResponse(200);
$this->drupalGet('admin/config/regional/date-time/formats/manage/html_date');
$this->assertResponse(403);
// Locked date formats are not deletable.
$this->drupalGet('admin/config/regional/date-time/formats/manage/short/delete');
$this->assertResponse(200);
$this->drupalGet('admin/config/regional/date-time/formats/manage/html_date/delete');
$this->assertResponse(403);
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Component\Utility\Unicode;
use Drupal\Tests\BrowserTestBase;
/**
* Tests validity of date format machine names.
*
* @group system
*/
class DateFormatsMachineNameTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create a new administrator user for the test.
$admin = $this->drupalCreateUser(['administer site configuration']);
$this->drupalLogin($admin);
}
/**
* Tests that date formats cannot be created with invalid machine names.
*/
public function testDateFormatsMachineNameAllowedValues() {
// Try to create a date format with a not allowed character to test the date
// format specific machine name replace pattern.
$edit = [
'label' => 'Something Not Allowed',
'id' => 'something.bad',
'date_format_pattern' => 'Y-m-d',
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertText(t('The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".'), 'It is not possible to create a date format with the machine name that has any character other than lowercase letters, digits or underscore.');
// Try to create a date format with the reserved machine name "custom".
$edit = [
'label' => 'Custom',
'id' => 'custom',
'date_format_pattern' => 'Y-m-d',
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertText(t('The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".'), 'It is not possible to create a date format with the machine name "custom".');
// Try to create a date format with a machine name, "fallback", that
// already exists.
$edit = [
'label' => 'Fallback',
'id' => 'fallback',
'date_format_pattern' => 'j/m/Y',
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertText(t('The machine-readable name is already in use. It must be unique.'), 'It is not possible to create a date format with the machine name "fallback". It is a built-in format that already exists.');
// Create a date format with a machine name distinct from the previous two.
$id = Unicode::strtolower($this->randomMachineName(16));
$edit = [
'label' => $this->randomMachineName(16),
'id' => $id,
'date_format_pattern' => 'd/m/Y',
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertText(t('Custom date format added.'), 'It is possible to create a date format with a new machine name.');
// Try to create a date format with same machine name as the previous one.
$edit = [
'label' => $this->randomMachineName(16),
'id' => $id,
'date_format_pattern' => 'd-m-Y',
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertText(t('The machine-readable name is already in use. It must be unique.'), 'It is not possible to create a new date format with an existing machine name.');
}
}

View file

@ -0,0 +1,228 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
/**
* Configure date and time settings. Test date formatting and time zone
* handling, including daylight saving time.
*
* @group system
*/
class DateTimeTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'node', 'language', 'field', 'field_ui', 'datetime', 'options'];
protected function setUp() {
parent::setUp();
// Create admin user and log in admin user.
$this->drupalLogin ($this->drupalCreateUser([
'administer site configuration',
'administer content types',
'administer nodes',
'administer node fields',
'administer node form display',
'administer node display',
]));
$this->drupalPlaceBlock('local_actions_block');
}
/**
* Test time zones and DST handling.
*/
public function testTimeZoneHandling() {
// Setup date/time settings for Honolulu time.
$config = $this->config('system.date')
->set('timezone.default', 'Pacific/Honolulu')
->set('timezone.user.configurable', 0)
->save();
DateFormat::load('medium')
->setPattern('Y-m-d H:i:s O')
->save();
// Create some nodes with different authored-on dates.
$date1 = '2007-01-31 21:00:00 -1000';
$date2 = '2007-07-31 21:00:00 -1000';
$this->drupalCreateContentType(['type' => 'article']);
$node1 = $this->drupalCreateNode(['created' => strtotime($date1), 'type' => 'article']);
$node2 = $this->drupalCreateNode(['created' => strtotime($date2), 'type' => 'article']);
// Confirm date format and time zone.
$this->drupalGet('node/' . $node1->id());
$this->assertText('2007-01-31 21:00:00 -1000', 'Date should be identical, with GMT offset of -10 hours.');
$this->drupalGet('node/' . $node2->id());
$this->assertText('2007-07-31 21:00:00 -1000', 'Date should be identical, with GMT offset of -10 hours.');
// Set time zone to Los Angeles time.
$config->set('timezone.default', 'America/Los_Angeles')->save();
\Drupal::entityManager()->getViewBuilder('node')->resetCache([$node1, $node2]);
// Confirm date format and time zone.
$this->drupalGet('node/' . $node1->id());
$this->assertText('2007-01-31 23:00:00 -0800', 'Date should be two hours ahead, with GMT offset of -8 hours.');
$this->drupalGet('node/' . $node2->id());
$this->assertText('2007-08-01 00:00:00 -0700', 'Date should be three hours ahead, with GMT offset of -7 hours.');
}
/**
* Test date format configuration.
*/
public function testDateFormatConfiguration() {
// Confirm 'no custom date formats available' message appears.
$this->drupalGet('admin/config/regional/date-time');
// Add custom date format.
$this->clickLink(t('Add format'));
$date_format_id = strtolower($this->randomMachineName(8));
$name = ucwords($date_format_id);
$date_format = 'd.m.Y - H:i';
$edit = [
'id' => $date_format_id,
'label' => $name,
'date_format_pattern' => $date_format,
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertUrl(\Drupal::url('entity.date_format.collection', [], ['absolute' => TRUE]), [], 'Correct page redirection.');
$this->assertText(t('Custom date format added.'), 'Date format added confirmation message appears.');
$this->assertText($name, 'Custom date format appears in the date format list.');
$this->assertText(t('Delete'), 'Delete link for custom date format appears.');
// Edit the custom date format and re-save without editing the format.
$this->drupalGet('admin/config/regional/date-time');
$this->clickLink(t('Edit'));
$this->drupalPostForm(NULL, NULL, t('Save format'));
$this->assertUrl('admin/config/regional/date-time', ['absolute' => TRUE], 'Correct page redirection.');
$this->assertText(t('Custom date format updated.'), 'Custom date format successfully updated.');
// Edit custom date format.
$this->drupalGet('admin/config/regional/date-time');
$this->clickLink(t('Edit'));
$edit = [
'date_format_pattern' => 'Y m',
];
$this->drupalPostForm($this->getUrl(), $edit, t('Save format'));
$this->assertUrl(\Drupal::url('entity.date_format.collection', [], ['absolute' => TRUE]), [], 'Correct page redirection.');
$this->assertText(t('Custom date format updated.'), 'Custom date format successfully updated.');
// Delete custom date format.
$this->clickLink(t('Delete'));
$this->drupalPostForm('admin/config/regional/date-time/formats/manage/' . $date_format_id . '/delete', [], t('Delete'));
$this->assertUrl(\Drupal::url('entity.date_format.collection', [], ['absolute' => TRUE]), [], 'Correct page redirection.');
$this->assertRaw(t('The date format %format has been deleted.', ['%format' => $name]), 'Custom date format removed.');
// Make sure the date does not exist in config.
$date_format = DateFormat::load($date_format_id);
$this->assertFalse($date_format);
// Add a new date format with an existing format.
$date_format_id = strtolower($this->randomMachineName(8));
$name = ucwords($date_format_id);
$date_format = 'Y';
$edit = [
'id' => $date_format_id,
'label' => $name,
'date_format_pattern' => $date_format,
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertUrl(\Drupal::url('entity.date_format.collection', [], ['absolute' => TRUE]), [], 'Correct page redirection.');
$this->assertText(t('Custom date format added.'), 'Date format added confirmation message appears.');
$this->assertText($name, 'Custom date format appears in the date format list.');
$this->assertText(t('Delete'), 'Delete link for custom date format appears.');
$date_format = DateFormat::create([
'id' => 'xss_short',
'label' => 'XSS format',
'pattern' => '\<\s\c\r\i\p\t\>\a\l\e\r\t\(\'\X\S\S\'\)\;\<\/\s\c\r\i\p\t\>',
]);
$date_format->save();
$this->drupalGet(Url::fromRoute('entity.date_format.collection'));
$this->assertEscaped("<script>alert('XSS');</script>", 'The date format was properly escaped');
// Add a new date format with HTML in it.
$date_format_id = strtolower($this->randomMachineName(8));
$name = ucwords($date_format_id);
$date_format = '& \<\e\m\>Y\<\/\e\m\>';
$edit = [
'id' => $date_format_id,
'label' => $name,
'date_format_pattern' => $date_format,
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertUrl(\Drupal::url('entity.date_format.collection', [], ['absolute' => TRUE]), [], 'Correct page redirection.');
$this->assertText(t('Custom date format added.'), 'Date format added confirmation message appears.');
$this->assertText($name, 'Custom date format appears in the date format list.');
$this->assertEscaped('<em>' . date("Y") . '</em>');
}
/**
* Test handling case with invalid data in selectors (like February, 31st).
*/
public function testEnteringDateTimeViaSelectors() {
$this->drupalCreateContentType(['type' => 'page_with_date', 'name' => 'Page with date']);
$this->drupalGet('admin/structure/types/manage/page_with_date');
$this->assertResponse(200, 'Content type created.');
$this->drupalGet('admin/structure/types/manage/page_with_date/fields/add-field');
$edit = [
'new_storage_type' => 'datetime',
'label' => 'dt',
'field_name' => 'dt',
];
$this->drupalPostForm('admin/structure/types/manage/page_with_date/fields/add-field', $edit, t('Save and continue'));
$this->assertText(t('These settings apply to the'), 'New datetime field created, now configuring');
$this->drupalGet('admin/structure/types/manage/page_with_date/fields/node.page_with_date.field_dt/storage');
$edit = [
'settings[datetime_type]' => 'datetime',
'cardinality' => 'number',
'cardinality_number' => '1',
];
$this->drupalPostForm('admin/structure/types/manage/page_with_date/fields/node.page_with_date.field_dt/storage', $edit, t('Save field settings'));
$this->drupalGet('admin/structure/types/manage/page_with_date/fields');
$this->assertText('field_dt', 'New field is in place');
$this->drupalGet('admin/structure/types/manage/page_with_date/form-display');
$edit = [
'fields[field_dt][type]' => 'datetime_datelist',
'fields[field_dt][region]' => 'content',
];
$this->drupalPostForm('admin/structure/types/manage/page_with_date/form-display', $edit, t('Save'));
$this->drupalLogout();
// Now log in as a regular editor.
$this->drupalLogin($this->drupalCreateUser(['create page_with_date content']));
$this->drupalGet('node/add/page_with_date');
$edit = [
'title[0][value]' => 'sample doc',
'field_dt[0][value][year]' => '2016',
'field_dt[0][value][month]' => '2',
'field_dt[0][value][day]' => '31',
'field_dt[0][value][hour]' => '1',
'field_dt[0][value][minute]' => '30',
];
$this->drupalPostForm('node/add/page_with_date', $edit, t('Save'));
$this->assertText(t('Selected combination of day and month is not valid.'), 'Inorrect date failed validation');
$edit['field_dt[0][value][day]'] = '29';
$this->drupalPostForm('node/add/page_with_date', $edit, t('Save'));
$this->assertNoText(t('Selected combination of day and month is not valid.'), 'Correct date passed validation.');
$this->drupalGet('node/1');
$this->assertText(t('Mon, 02/29/2016 - 01:30'), 'Node successfully created with valid date.');
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the handling of requests containing 'index.php'.
*
* @group system
*/
class IndexPhpTest extends BrowserTestBase {
protected function setUp() {
parent::setUp();
}
/**
* Test index.php handling.
*/
public function testIndexPhpHandling() {
$index_php = $GLOBALS['base_url'] . '/index.php';
$this->drupalGet($index_php, ['external' => TRUE]);
$this->assertResponse(200, 'Make sure index.php returns a valid page.');
$this->drupalGet($index_php . '/user', ['external' => TRUE]);
$this->assertResponse(200, 'Make sure index.php/user returns a valid page.');
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
/**
* Test SimplePageVariant main content rendering fallback page display variant.
*
* @group system
*/
class MainContentFallbackTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'system_test'];
protected $adminUser;
protected $webUser;
protected function setUp() {
parent::setUp();
// Create and log in admin user.
$this->adminUser = $this->drupalCreateUser([
'access administration pages',
'administer site configuration',
'administer modules',
]);
$this->drupalLogin($this->adminUser);
// Create a web user.
$this->webUser = $this->drupalCreateUser(['access user profiles']);
}
/**
* Test availability of main content: Drupal falls back to SimplePageVariant.
*/
public function testMainContentFallback() {
$edit = [];
// Uninstall the block module.
$edit['uninstall[block]'] = 'block';
$this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall'));
$this->drupalPostForm(NULL, NULL, t('Uninstall'));
$this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
$this->rebuildContainer();
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('block'), 'Block module uninstall.');
// When Block module is not installed and BlockPageVariant is not available,
// Drupal should fall back to SimplePageVariant. Both for the admin and the
// front-end theme.
$this->drupalGet('admin/config/system/site-information');
$this->assertField('site_name', 'Fallback to SimplePageVariant works for admin theme.');
$this->drupalGet('system-test/main-content-fallback');
$this->assertText(t('Content to test main content fallback'), 'Fallback to SimplePageVariant works for front-end theme.');
// Request a user* page and see if it is displayed.
$this->drupalLogin($this->webUser);
$this->drupalGet('user/' . $this->webUser->id() . '/edit');
$this->assertField('mail', 'User interface still available.');
// Enable the block module again.
$this->drupalLogin($this->adminUser);
$edit = [];
$edit['modules[block][enable]'] = 'block';
$this->drupalPostForm('admin/modules', $edit, t('Install'));
$this->assertText(t('Module Block has been enabled.'), 'Modules status has been updated.');
$this->rebuildContainer();
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('block'), 'Block module re-enabled.');
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
/**
* Tests HTTP file fetching and error handling.
*
* @group system
*/
class RetrieveFileTest extends BrowserTestBase {
/**
* Invokes system_retrieve_file() in several scenarios.
*/
public function testFileRetrieving() {
// Test 404 handling by trying to fetch a randomly named file.
drupal_mkdir($sourcedir = 'public://' . $this->randomMachineName());
$filename = 'Файл для тестирования ' . $this->randomMachineName();
$url = file_create_url($sourcedir . '/' . $filename);
$retrieved_file = system_retrieve_file($url);
$this->assertFalse($retrieved_file, 'Non-existent file not fetched.');
// Actually create that file, download it via HTTP and test the returned path.
file_put_contents($sourcedir . '/' . $filename, 'testing');
$retrieved_file = system_retrieve_file($url);
// URLs could not contains characters outside the ASCII set so $filename
// has to be encoded.
$encoded_filename = rawurlencode($filename);
$this->assertEqual($retrieved_file, 'public://' . $encoded_filename, 'Sane path for downloaded file returned (public:// scheme).');
$this->assertTrue(is_file($retrieved_file), 'Downloaded file does exist (public:// scheme).');
$this->assertEqual(filesize($retrieved_file), 7, 'File size of downloaded file is correct (public:// scheme).');
file_unmanaged_delete($retrieved_file);
// Test downloading file to a different location.
drupal_mkdir($targetdir = 'temporary://' . $this->randomMachineName());
$retrieved_file = system_retrieve_file($url, $targetdir);
$this->assertEqual($retrieved_file, "$targetdir/$encoded_filename", 'Sane path for downloaded file returned (temporary:// scheme).');
$this->assertTrue(is_file($retrieved_file), 'Downloaded file does exist (temporary:// scheme).');
$this->assertEqual(filesize($retrieved_file), 7, 'File size of downloaded file is correct (temporary:// scheme).');
file_unmanaged_delete($retrieved_file);
file_unmanaged_delete_recursive($sourcedir);
file_unmanaged_delete_recursive($targetdir);
}
}

View file

@ -0,0 +1,111 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Tests\BrowserTestBase;
/**
* Tests Drupal permissions hardening of /sites subdirectories.
*
* @group system
*/
class SitesDirectoryHardeningTest extends BrowserTestBase {
use StringTranslationTrait;
/**
* Tests the default behavior to restrict directory permissions is enforced.
*
* Checks both the the current sites directory and settings.php.
*/
public function testSitesDirectoryHardening() {
$site_path = $this->kernel->getSitePath();
$settings_file = $this->settingsFile($site_path);
// First, we check based on what the initial install has set.
$this->assertTrue(drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir'), new FormattableMarkup('Verified permissions for @file.', ['@file' => $site_path]));
// We intentionally don't check for settings.local.php as that file is
// not created by Drupal.
$this->assertTrue(drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE), new FormattableMarkup('Verified permissions for @file.', ['@file' => $settings_file]));
$this->makeWritable($site_path);
$this->checkSystemRequirements();
$this->assertTrue(drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir'), new FormattableMarkup('Verified permissions for @file after manual permissions change.', ['@file' => $site_path]));
$this->assertTrue(drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE), new FormattableMarkup('Verified permissions for @file after manual permissions change.', ['@file' => $settings_file]));
}
/**
* Tests writable files remain writable when directory hardening is disabled.
*/
public function testSitesDirectoryHardeningConfig() {
$site_path = $this->kernel->getSitePath();
$settings_file = $this->settingsFile($site_path);
// Disable permissions enforcement.
$settings = Settings::getAll();
$settings['skip_permissions_hardening'] = TRUE;
new Settings($settings);
$this->assertTrue(Settings::get('skip_permissions_hardening'), 'Able to set hardening to true');
$this->makeWritable($site_path);
// Manually trigger the requirements check.
$requirements = $this->checkSystemRequirements();
$this->assertEqual(REQUIREMENT_WARNING, $requirements['configuration_files']['severity'], 'Warning severity is properly set.');
$this->assertEqual($this->t('Protection disabled'), (string) $requirements['configuration_files']['description']['#context']['configuration_error_list']['#items'][0], 'Description is properly set.');
$this->assertTrue(is_writable($site_path), 'Site directory remains writable when automatically fixing permissions is disabled.');
$this->assertTrue(is_writable($settings_file), 'settings.php remains writable when automatically fixing permissions is disabled.');
// Re-enable permissions enforcement.
$settings = Settings::getAll();
$settings['skip_permissions_hardening'] = FALSE;
new Settings($settings);
// Manually trigger the requirements check.
$this->checkSystemRequirements();
$this->assertFalse(is_writable($site_path), 'Site directory is protected when automatically fixing permissions is enabled.');
$this->assertFalse(is_writable($settings_file), 'settings.php is protected when automatically fixing permissions is enabled.');
}
/**
* Checks system runtime requirements.
*
* @return array
* An array of system requirements.
*/
protected function checkSystemRequirements() {
module_load_install('system');
return system_requirements('runtime');
}
/**
* Makes the given path and settings file writable.
*
* @param string $site_path
* The sites directory path, such as 'sites/default'.
*/
protected function makeWritable($site_path) {
chmod($site_path, 0755);
chmod($this->settingsFile($site_path), 0644);
}
/**
* Returns the path to settings.php
*
* @param string $site_path
* The sites subdirectory path.
*
* @return string
* The path to settings.php.
*/
protected function settingsFile($site_path) {
$settings_file = $site_path . '/settings.php';
return $settings_file;
}
}

View file

@ -0,0 +1,94 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
use Drupal\system\SystemRequirements;
/**
* Tests output on the status overview page.
*
* @group system
*/
class StatusTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['update_test_postupdate'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Unset the sync directory in settings.php to trigger $config_directories
// error.
$settings['config_directories'] = [
CONFIG_SYNC_DIRECTORY => (object) [
'value' => '',
'required' => TRUE,
],
];
$this->writeSettings($settings);
$admin_user = $this->drupalCreateUser([
'administer site configuration',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests that the status page returns.
*/
public function testStatusPage() {
// Go to Administration.
$this->drupalGet('admin/reports/status');
$this->assertResponse(200, 'The status page is reachable.');
$phpversion = phpversion();
$this->assertText($phpversion, 'Php version is shown on the page.');
// Checks if the suggestion to update to php 5.5.21 or 5.6.5 for disabling
// multiple statements is present when necessary.
if (\Drupal::database()->driver() === 'mysql' && !SystemRequirements::phpVersionWithPdoDisallowMultipleStatements($phpversion)) {
$this->assertText(t('PHP (multiple statement disabling)'));
}
else {
$this->assertNoText(t('PHP (multiple statement disabling)'));
}
if (function_exists('phpinfo')) {
$this->assertLinkByHref(Url::fromRoute('system.php')->toString());
}
else {
$this->assertNoLinkByHref(Url::fromRoute('system.php')->toString());
}
// If a module is fully installed no pending updates exists.
$this->assertNoText(t('Out of date'));
// The global $config_directories is not properly formed.
$this->assertRaw(t('Your %file file must define the $config_directories variable as an array containing the names of directories in which configuration files can be found. It must contain a %sync_key key.', ['%file' => $this->siteDirectory . '/settings.php', '%sync_key' => CONFIG_SYNC_DIRECTORY]));
// Set the schema version of update_test_postupdate to a lower version, so
// update_test_postupdate_update_8001() needs to be executed.
drupal_set_installed_schema_version('update_test_postupdate', 8000);
$this->drupalGet('admin/reports/status');
$this->assertText(t('Out of date'));
// Now cleanup the executed post update functions.
drupal_set_installed_schema_version('update_test_postupdate', 8001);
/** @var \Drupal\Core\Update\UpdateRegistry $post_update_registry */
$post_update_registry = \Drupal::service('update.post_update_registry');
$post_update_registry->filterOutInvokedUpdatesByModule('update_test_postupdate');
$this->drupalGet('admin/reports/status');
$this->assertText(t('Out of date'));
$this->drupalGet('admin/reports/status/php');
$this->assertResponse(200, 'The phpinfo page is reachable.');
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the authorize.php script and related API.
*
* @group system
*/
class SystemAuthorizeTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system_test'];
protected function setUp() {
parent::setUp();
// Create an administrator user.
$this->drupalLogin ($this->drupalCreateUser(['administer software updates']));
}
/**
* Helper function to initialize authorize.php and load it via drupalGet().
*
* Initializing authorize.php needs to happen in the child Drupal
* installation, not the parent. So, we visit a menu callback provided by
* system_test.module which calls system_authorized_init() to initialize the
* $_SESSION inside the test site, not the framework site. This callback
* redirects to authorize.php when it's done initializing.
*
* @see system_authorized_init()
*/
public function drupalGetAuthorizePHP($page_title = 'system-test-auth') {
$this->drupalGet('system-test/authorize-init/' . $page_title);
}
/**
* Tests the FileTransfer hooks
*/
public function testFileTransferHooks() {
$page_title = $this->randomMachineName(16);
$this->drupalGetAuthorizePHP($page_title);
$this->assertTitle(strtr('@title | Drupal', ['@title' => $page_title]), 'authorize.php page title is correct.');
$this->assertNoText('It appears you have reached this page in error.');
$this->assertText('To continue, provide your server connection details');
// Make sure we see the new connection method added by system_test.
$this->assertRaw('System Test FileTransfer');
// Make sure the settings form callback works.
$this->assertText('System Test Username');
// Test that \Drupal\Core\Render\BareHtmlPageRenderer adds assets as
// expected to the first page of the authorize.php script.
$this->assertRaw('core/misc/states.js');
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
/**
* Scan token-like patterns in a dummy text to check token scanning.
*
* @group system
*/
class TokenScanTest extends BrowserTestBase {
/**
* Scans dummy text, then tests the output.
*/
public function testTokenScan() {
// Define text with valid and not valid, fake and existing token-like
// strings.
$text = 'First a [valid:simple], but dummy token, and a dummy [valid:token with: spaces].';
$text .= 'Then a [not valid:token].';
$text .= 'Then an [:empty token type].';
$text .= 'Then an [empty token:].';
$text .= 'Then a totally empty token: [:].';
$text .= 'Last an existing token: [node:author:name].';
$token_wannabes = \Drupal::token()->scan($text);
$this->assertTrue(isset($token_wannabes['valid']['simple']), 'A simple valid token has been matched.');
$this->assertTrue(isset($token_wannabes['valid']['token with: spaces']), 'A valid token with space characters in the token name has been matched.');
$this->assertFalse(isset($token_wannabes['not valid']), 'An invalid token with spaces in the token type has not been matched.');
$this->assertFalse(isset($token_wannabes['empty token']), 'An empty token has not been matched.');
$this->assertFalse(isset($token_wannabes['']['empty token type']), 'An empty token type has not been matched.');
$this->assertFalse(isset($token_wannabes['']['']), 'An empty token and type has not been matched.');
$this->assertTrue(isset($token_wannabes['node']), 'An existing valid token has been matched.');
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
/**
* Tests output on the status overview page.
*
* @group system
*/
class TrustedHostsTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$admin_user = $this->drupalCreateUser([
'administer site configuration',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests that the status page shows an error when the trusted host setting
* is missing from settings.php
*/
public function testStatusPageWithoutConfiguration() {
$this->drupalGet('admin/reports/status');
$this->assertResponse(200, 'The status page is reachable.');
$this->assertRaw(t('Trusted Host Settings'));
$this->assertRaw(t('The trusted_host_patterns setting is not configured in settings.php.'));
}
/**
* Tests that the status page shows the trusted patterns from settings.php.
*/
public function testStatusPageWithConfiguration() {
$settings['settings']['trusted_host_patterns'] = (object) [
'value' => ['^' . preg_quote(\Drupal::request()->getHost()) . '$'],
'required' => TRUE,
];
$this->writeSettings($settings);
$this->drupalGet('admin/reports/status');
$this->assertResponse(200, 'The status page is reachable.');
$this->assertRaw(t('Trusted Host Settings'));
$this->assertRaw(t('The trusted_host_patterns setting is set to allow'));
}
/**
* Tests that fake requests have the proper host configured.
*
* @see \Drupal\Core\Http\TrustedHostsRequestFactory
*/
public function testFakeRequests() {
$this->container->get('module_installer')->install(['trusted_hosts_test']);
$this->container->get('router.builder')->rebuild();
$host = $this->container->get('request_stack')->getCurrentRequest()->getHost();
$settings['settings']['trusted_host_patterns'] = (object) [
'value' => ['^' . preg_quote($host) . '$'],
'required' => TRUE,
];
$this->writeSettings($settings);
$this->drupalGet('trusted-hosts-test/fake-request');
$this->assertText('Host: ' . $host);
}
/**
* Tests that shortcut module works together with host verification.
*/
public function testShortcut() {
$this->container->get('module_installer')->install(['block', 'shortcut']);
$this->rebuildContainer();
$this->container->get('router.builder')->rebuild();
/** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
$entity_manager = $this->container->get('entity.manager');
$shortcut_storage = $entity_manager->getStorage('shortcut');
$shortcut = $shortcut_storage->create([
'title' => $this->randomString(),
'link' => 'internal:/admin/reports/status',
'shortcut_set' => 'default',
]);
$shortcut_storage->save($shortcut);
// Grant the current user access to see the shortcuts.
$role_storage = $entity_manager->getStorage('user_role');
$roles = $this->loggedInUser->getRoles(TRUE);
/** @var \Drupal\user\RoleInterface $role */
$role = $role_storage->load(reset($roles));
$role->grantPermission('access shortcuts')->save();
$this->drupalPlaceBlock('shortcuts');
$this->drupalGet('');
$this->assertLink($shortcut->label());
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the multi theme engine support.
*
* @group Theme
*/
class EngineNyanCatTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['theme_test'];
protected function setUp() {
parent::setUp();
\Drupal::service('theme_handler')->install(['test_theme_nyan_cat_engine']);
}
/**
* Ensures a theme's template is overridable based on the 'template' filename.
*/
public function testTemplateOverride() {
$this->config('system.theme')
->set('default', 'test_theme_nyan_cat_engine')
->save();
$this->drupalGet('theme-test/template-test');
$this->assertText('Success: Template overridden with Nyan Cat theme. All of them', 'Template overridden by Nyan Cat file.');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
/**
* Tests autocompletion not loading registry.
*
* @group Theme
*/
class FastTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['theme_test'];
protected function setUp() {
parent::setUp();
$this->account = $this->drupalCreateUser(['access user profiles']);
}
/**
* Tests access to user autocompletion and verify the correct results.
*/
public function testUserAutocomplete() {
$this->drupalLogin($this->account);
$this->drupalGet('user/autocomplete', ['query' => ['q' => $this->account->getUsername()]]);
$this->assertRaw($this->account->getUsername());
$this->assertNoText('registry initialized', 'The registry was not initialized');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
/**
* Tests that the theme system can be correctly initialized early in the page
* request.
*
* @group Theme
*/
class ThemeEarlyInitializationTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['theme_test'];
/**
* Test that the theme system can generate output in a request listener.
*/
public function testRequestListener() {
$this->drupalGet('theme-test/request-listener');
// Verify that themed output generated in the request listener appears.
$this->assertRaw('Themed output generated in a KernelEvents::REQUEST listener');
// Verify that the default theme's CSS still appears even though the theme
// system was initialized early.
$this->assertRaw('classy/css/components/action-links.css');
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
/**
* Tests processing of theme .info.yml properties.
*
* @group Theme
*/
class ThemeInfoTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['theme_test'];
/**
* The theme handler used in this test for enabling themes.
*
* @var \Drupal\Core\Extension\ThemeHandler
*/
protected $themeHandler;
/**
* The theme manager used in this test.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* The state service used in this test.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->themeHandler = $this->container->get('theme_handler');
$this->themeManager = $this->container->get('theme.manager');
$this->state = $this->container->get('state');
}
/**
* Tests stylesheets-remove.
*/
public function testStylesheets() {
$this->themeHandler->install(['test_basetheme', 'test_subtheme']);
$this->config('system.theme')
->set('default', 'test_subtheme')
->save();
$base = drupal_get_path('theme', 'test_basetheme');
$sub = drupal_get_path('theme', 'test_subtheme') . '/css';
// All removals are expected to be based on a file's path and name and
// should work nevertheless.
$this->drupalGet('theme-test/info/stylesheets');
$this->assertIdentical(1, count($this->xpath('//link[contains(@href, :href)]', [':href' => "$base/base-add.css"])), "$base/base-add.css found");
$this->assertIdentical(0, count($this->xpath('//link[contains(@href, :href)]', [':href' => "base-remove.css"])), "base-remove.css not found");
$this->assertIdentical(1, count($this->xpath('//link[contains(@href, :href)]', [':href' => "$sub/sub-add.css"])), "$sub/sub-add.css found");
$this->assertIdentical(0, count($this->xpath('//link[contains(@href, :href)]', [':href' => "sub-remove.css"])), "sub-remove.css not found");
$this->assertIdentical(0, count($this->xpath('//link[contains(@href, :href)]', [':href' => "base-add.sub-remove.css"])), "base-add.sub-remove.css not found");
// Verify that CSS files with the same name are loaded from both the base theme and subtheme.
$this->assertIdentical(1, count($this->xpath('//link[contains(@href, :href)]', [':href' => "$base/samename.css"])), "$base/samename.css found");
$this->assertIdentical(1, count($this->xpath('//link[contains(@href, :href)]', [':href' => "$sub/samename.css"])), "$sub/samename.css found");
}
/**
* Tests that changes to the info file are picked up.
*/
public function testChanges() {
$this->themeHandler->install(['test_theme']);
$this->config('system.theme')->set('default', 'test_theme')->save();
$this->themeManager->resetActiveTheme();
$active_theme = $this->themeManager->getActiveTheme();
// Make sure we are not testing the wrong theme.
$this->assertEqual('test_theme', $active_theme->getName());
$this->assertEqual(['classy/base', 'core/normalize', 'test_theme/global-styling'], $active_theme->getLibraries());
// @see theme_test_system_info_alter()
$this->state->set('theme_test.modify_info_files', TRUE);
drupal_flush_all_caches();
$active_theme = $this->themeManager->getActiveTheme();
$this->assertEqual(['classy/base', 'core/normalize', 'test_theme/global-styling', 'core/backbone'], $active_theme->getLibraries());
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the generation of 'theme_token' key in Drupal settings.
*
* @group Theme
*/
class ThemeTokenTest extends BrowserTestBase {
/**
* We want to visit the 'admin/structure/block' page.
*
* @var array
*/
static public $modules = ['block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$account = $this->drupalCreateUser(['administer blocks', 'view the administration theme']);
$this->drupalLogin($account);
}
/**
* Tests if the 'theme_token' key of 'ajaxPageState' is computed.
*/
public function testThemeToken() {
// Visit the block administrative page with default theme. We use that page
// because 'misc/ajax.js' is loaded there and we can test the token
// generation.
$this->drupalGet('admin/structure/block');
$settings = $this->getDrupalSettings();
$this->assertNull($settings['ajaxPageState']['theme_token']);
// Install 'seven' and configure it as administrative theme.
$this->container->get('theme_installer')->install(['seven']);
$this->config('system.theme')->set('admin', 'seven')->save();
// Revisit the page. This time the page is displayed using the 'seven' theme
// and that is different from the default theme ('classy').
$this->drupalGet('admin/structure/block');
$settings = $this->getDrupalSettings();
$this->assertNotNull($settings['ajaxPageState']['theme_token']);
// The CSRF token is a 43 length string.
$this->assertTrue(is_string($settings['ajaxPageState']['theme_token']));
$this->assertEqual(strlen($settings['ajaxPageState']['theme_token']), 43);
}
}

View file

@ -0,0 +1,89 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
/**
* Tests Twig extensions.
*
* @group Theme
*/
class TwigExtensionTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['theme_test', 'twig_extension_test'];
protected function setUp() {
parent::setUp();
\Drupal::service('theme_handler')->install(['test_theme']);
}
/**
* Tests that the provided Twig extension loads the service appropriately.
*/
public function testTwigExtensionLoaded() {
$twigService = \Drupal::service('twig');
$ext = $twigService->getExtension('twig_extension_test.test_extension');
$this->assertEqual(get_class($ext), 'Drupal\twig_extension_test\TwigExtension\TestExtension', 'TestExtension loaded successfully.');
}
/**
* Tests that the Twig extension's filter produces expected output.
*/
public function testTwigExtensionFilter() {
$this->config('system.theme')
->set('default', 'test_theme')
->save();
$this->drupalGet('twig-extension-test/filter');
$this->assertText('Every plant is not a mineral.', 'Success: String filtered.');
// Test safe_join filter.
$this->assertRaw('&lt;em&gt;will be escaped&lt;/em&gt;<br/><em>will be markup</em><br/><strong>will be rendered</strong>');
}
/**
* Tests that the Twig extension's function produces expected output.
*/
public function testTwigExtensionFunction() {
$this->config('system.theme')
->set('default', 'test_theme')
->save();
$this->drupalGet('twig-extension-test/function');
$this->assertText('THE QUICK BROWN BOX JUMPS OVER THE LAZY DOG 123.', 'Success: Text converted to uppercase.');
$this->assertText('the quick brown box jumps over the lazy dog 123.', 'Success: Text converted to lowercase.');
$this->assertNoText('The Quick Brown Fox Jumps Over The Lazy Dog 123.', 'Success: No text left behind.');
}
/**
* Tests output of integer and double 0 values of TwigExtension::escapeFilter().
*
* @see https://www.drupal.org/node/2417733
*/
public function testsRenderEscapedZeroValue() {
/** @var \Drupal\Core\Template\TwigExtension $extension */
$extension = \Drupal::service('twig.extension');
/** @var \Drupal\Core\Template\TwigEnvironment $twig */
$twig = \Drupal::service('twig');
$this->assertIdentical($extension->escapeFilter($twig, 0), 0, 'TwigExtension::escapeFilter() returns zero correctly when provided as an integer.');
$this->assertIdentical($extension->escapeFilter($twig, 0.0), 0, 'TwigExtension::escapeFilter() returns zero correctly when provided as a double.');
}
/**
* Tests output of integer and double 0 values of TwigExtension->renderVar().
*
* @see https://www.drupal.org/node/2417733
*/
public function testsRenderZeroValue() {
/** @var \Drupal\Core\Template\TwigExtension $extension */
$extension = \Drupal::service('twig.extension');
$this->assertIdentical($extension->renderVar(0), 0, 'TwigExtension::renderVar() renders zero correctly when provided as an integer.');
$this->assertIdentical($extension->renderVar(0.0), 0, 'TwigExtension::renderVar() renders zero correctly when provided as a double.');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
/**
* Tests adding Twig loaders.
*
* @group Theme
*/
class TwigLoaderTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['twig_loader_test'];
/**
* Tests adding an additional twig loader to the loader chain.
*/
public function testTwigLoaderAddition() {
$environment = \Drupal::service('twig');
$template = $environment->loadTemplate('kittens');
$this->assertEqual($template->render([]), 'kittens', 'Passing "kittens" to the custom Twig loader returns "kittens".');
$template = $environment->loadTemplate('meow');
$this->assertEqual($template->render([]), 'cats', 'Passing something other than "kittens" to the custom Twig loader returns "cats".');
}
}

View file

@ -0,0 +1,84 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
/**
* Tests Twig registry loader.
*
* @group Theme
*/
class TwigRegistryLoaderTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['twig_theme_test', 'block'];
/**
* @var \Drupal\Core\Template\TwigEnvironment
*/
protected $twig;
protected function setUp() {
parent::setUp();
\Drupal::service('theme_handler')->install(['test_theme_twig_registry_loader', 'test_theme_twig_registry_loader_theme', 'test_theme_twig_registry_loader_subtheme']);
$this->twig = \Drupal::service('twig');
}
/**
* Checks to see if a value is a Twig template.
*/
public function assertTwigTemplate($value, $message = '', $group = 'Other') {
$this->assertTrue($value instanceof \Twig_Template, $message, $group);
}
/**
* Tests template discovery using the Drupal theme registry.
*/
public function testTemplateDiscovery() {
$this->assertTwigTemplate($this->twig->resolveTemplate('block.html.twig'), 'Found block.html.twig in block module.');
}
/**
* Tests template extension and includes using the Drupal theme registry.
*/
public function testTwigNamespaces() {
// Test the module-provided extend and insert templates.
$this->drupalGet('twig-theme-test/registry-loader');
$this->assertText('This line is from twig_theme_test/templates/twig-registry-loader-test-extend.html.twig');
$this->assertText('This line is from twig_theme_test/templates/twig-registry-loader-test-include.html.twig');
// Enable a theme that overrides the extend and insert templates to ensure
// they are picked up by the registry loader.
$this->config('system.theme')
->set('default', 'test_theme_twig_registry_loader')
->save();
$this->drupalGet('twig-theme-test/registry-loader');
$this->assertText('This line is from test_theme_twig_registry_loader/templates/twig-registry-loader-test-extend.html.twig');
$this->assertText('This line is from test_theme_twig_registry_loader/templates/twig-registry-loader-test-include.html.twig');
// Enable overriding theme that overrides the extend and insert templates
// from the base theme.
$this->config('system.theme')
->set('default', 'test_theme_twig_registry_loader_theme')
->save();
$this->drupalGet('twig-theme-test/registry-loader');
$this->assertText('This line is from test_theme_twig_registry_loader_theme/templates/twig-registry-loader-test-extend.html.twig');
$this->assertText('This line is from test_theme_twig_registry_loader_theme/templates/twig-registry-loader-test-include.html.twig');
// Enable a subtheme for the theme that doesn't have any overrides to make
// sure that templates are being loaded from the first parent which has the
// templates.
$this->config('system.theme')
->set('default', 'test_theme_twig_registry_loader_subtheme')
->save();
$this->drupalGet('twig-theme-test/registry-loader');
$this->assertText('This line is from test_theme_twig_registry_loader_theme/templates/twig-registry-loader-test-extend.html.twig');
$this->assertText('This line is from test_theme_twig_registry_loader_theme/templates/twig-registry-loader-test-include.html.twig');
}
}

View file

@ -0,0 +1,131 @@
<?php
namespace Drupal\Tests\system\Functional\Theme;
use Drupal\Tests\BrowserTestBase;
use Drupal\Core\PhpStorage\PhpStorageFactory;
/**
* Tests overriding Twig engine settings via settings.php.
*
* @group Theme
*/
class TwigSettingsTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['theme_test'];
/**
* Ensures Twig template auto reload setting can be overridden.
*/
public function testTwigAutoReloadOverride() {
// Enable auto reload and rebuild the service container.
$parameters = $this->container->getParameter('twig.config');
$parameters['auto_reload'] = TRUE;
$this->setContainerParameter('twig.config', $parameters);
$this->rebuildContainer();
// Check isAutoReload() via the Twig service container.
$this->assertTrue($this->container->get('twig')->isAutoReload(), 'Automatic reloading of Twig templates enabled.');
// Disable auto reload and check the service container again.
$parameters = $this->container->getParameter('twig.config');
$parameters['auto_reload'] = FALSE;
$this->setContainerParameter('twig.config', $parameters);
$this->rebuildContainer();
$this->assertFalse($this->container->get('twig')->isAutoReload(), 'Automatic reloading of Twig templates disabled.');
}
/**
* Ensures Twig engine debug setting can be overridden.
*/
public function testTwigDebugOverride() {
// Enable debug and rebuild the service container.
$parameters = $this->container->getParameter('twig.config');
$parameters['debug'] = TRUE;
$this->setContainerParameter('twig.config', $parameters);
$this->rebuildContainer();
// Check isDebug() via the Twig service container.
$this->assertTrue($this->container->get('twig')->isDebug(), 'Twig debug enabled.');
$this->assertTrue($this->container->get('twig')->isAutoReload(), 'Twig automatic reloading is enabled when debug is enabled.');
// Override auto reload when debug is enabled.
$parameters = $this->container->getParameter('twig.config');
$parameters['auto_reload'] = FALSE;
$this->setContainerParameter('twig.config', $parameters);
$this->rebuildContainer();
$this->assertFalse($this->container->get('twig')->isAutoReload(), 'Twig automatic reloading can be disabled when debug is enabled.');
// Disable debug and check the service container again.
$parameters = $this->container->getParameter('twig.config');
$parameters['debug'] = FALSE;
$this->setContainerParameter('twig.config', $parameters);
$this->rebuildContainer();
$this->assertFalse($this->container->get('twig')->isDebug(), 'Twig debug disabled.');
}
/**
* Ensures Twig template cache setting can be overridden.
*/
public function testTwigCacheOverride() {
$extension = twig_extension();
$theme_handler = $this->container->get('theme_handler');
$theme_handler->install(['test_theme']);
$this->config('system.theme')->set('default', 'test_theme')->save();
// The registry still works on theme globals, so set them here.
\Drupal::theme()->setActiveTheme(\Drupal::service('theme.initialization')->getActiveThemeByName('test_theme'));
// Reset the theme registry, so that the new theme is used.
$this->container->set('theme.registry', NULL);
// Load array of Twig templates.
// reset() is necessary to invalidate caches tagged with 'theme_registry'.
$registry = $this->container->get('theme.registry');
$registry->reset();
$templates = $registry->getRuntime();
// Get the template filename and the cache filename for
// theme_test.template_test.html.twig.
$info = $templates->get('theme_test_template_test');
$template_filename = $info['path'] . '/' . $info['template'] . $extension;
$cache_filename = $this->container->get('twig')->getCacheFilename($template_filename);
// Navigate to the page and make sure the template gets cached.
$this->drupalGet('theme-test/template-test');
$this->assertTrue(PhpStorageFactory::get('twig')->exists($cache_filename), 'Cached Twig template found.');
// Disable the Twig cache and rebuild the service container.
$parameters = $this->container->getParameter('twig.config');
$parameters['cache'] = FALSE;
$this->setContainerParameter('twig.config', $parameters);
$this->rebuildContainer();
// This should return false after rebuilding the service container.
$this->assertFalse($this->container->get('twig')->getCache(), 'Twig environment has caching disabled.');
}
/**
* Tests twig inline templates with auto_reload.
*/
public function testTwigInlineWithAutoReload() {
$parameters = $this->container->getParameter('twig.config');
$parameters['auto_reload'] = TRUE;
$parameters['debug'] = TRUE;
$this->setContainerParameter('twig.config', $parameters);
$this->rebuildContainer();
$this->drupalGet('theme-test/inline-template-test');
$this->assertResponse(200);
$this->drupalGet('theme-test/inline-template-test');
$this->assertResponse(200);
}
}

View file

@ -17,7 +17,7 @@ class ActionTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('system', 'field', 'user', 'action_test');
public static $modules = ['system', 'field', 'user', 'action_test'];
/**
* The action manager.
@ -34,7 +34,7 @@ class ActionTest extends KernelTestBase {
$this->actionManager = $this->container->get('plugin.manager.action');
$this->installEntitySchema('user');
$this->installSchema('system', array('sequences'));
$this->installSchema('system', ['sequences']);
}
/**
@ -59,7 +59,7 @@ class ActionTest extends KernelTestBase {
// Create a new unsaved user.
$name = $this->randomMachineName();
$user_storage = $this->container->get('entity.manager')->getStorage('user');
$account = $user_storage->create(array('name' => $name, 'bundle' => 'user'));
$account = $user_storage->create(['name' => $name, 'bundle' => 'user']);
$loaded_accounts = $user_storage->loadMultiple();
$this->assertEqual(count($loaded_accounts), 0);
@ -76,25 +76,25 @@ class ActionTest extends KernelTestBase {
*/
public function testDependencies() {
// Create a new action that depends on a user role.
$action = Action::create(array(
$action = Action::create([
'id' => 'user_add_role_action.' . RoleInterface::ANONYMOUS_ID,
'type' => 'user',
'label' => t('Add the anonymous role to the selected users'),
'configuration' => array(
'configuration' => [
'rid' => RoleInterface::ANONYMOUS_ID,
),
],
'plugin' => 'user_add_role_action',
));
]);
$action->save();
$expected = array(
'config' => array(
$expected = [
'config' => [
'user.role.' . RoleInterface::ANONYMOUS_ID,
),
'module' => array(
],
'module' => [
'user',
),
);
],
];
$this->assertIdentical($expected, $action->calculateDependencies()->getDependencies());
}

View file

@ -31,7 +31,7 @@ class SystemMenuBlockTest extends KernelTestBase {
*
* @var array
*/
public static $modules = array(
public static $modules = [
'system',
'block',
'menu_test',
@ -39,7 +39,7 @@ class SystemMenuBlockTest extends KernelTestBase {
'field',
'user',
'link',
);
];
/**
* The block under test.
@ -97,16 +97,16 @@ class SystemMenuBlockTest extends KernelTestBase {
$this->blockManager = $this->container->get('plugin.manager.block');
$routes = new RouteCollection();
$requirements = array('_access' => 'TRUE');
$options = array('_access_checks' => array('access_check.default'));
$routes->add('example1', new Route('/example1', array(), $requirements, $options));
$routes->add('example2', new Route('/example2', array(), $requirements, $options));
$routes->add('example3', new Route('/example3', array(), $requirements, $options));
$routes->add('example4', new Route('/example4', array(), $requirements, $options));
$routes->add('example5', new Route('/example5', array(), $requirements, $options));
$routes->add('example6', new Route('/example6', array(), $requirements, $options));
$routes->add('example7', new Route('/example7', array(), $requirements, $options));
$routes->add('example8', new Route('/example8', array(), $requirements, $options));
$requirements = ['_access' => 'TRUE'];
$options = ['_access_checks' => ['access_check.default']];
$routes->add('example1', new Route('/example1', [], $requirements, $options));
$routes->add('example2', new Route('/example2', [], $requirements, $options));
$routes->add('example3', new Route('/example3', [], $requirements, $options));
$routes->add('example4', new Route('/example4', [], $requirements, $options));
$routes->add('example5', new Route('/example5', [], $requirements, $options));
$routes->add('example6', new Route('/example6', [], $requirements, $options));
$routes->add('example7', new Route('/example7', [], $requirements, $options));
$routes->add('example8', new Route('/example8', [], $requirements, $options));
$mock_route_provider = new MockRouteProvider($routes);
$this->container->set('router.route_provider', $mock_route_provider);
@ -115,11 +115,11 @@ class SystemMenuBlockTest extends KernelTestBase {
$menu_name = 'mock';
$label = $this->randomMachineName(16);
$this->menu = Menu::create(array(
$this->menu = Menu::create([
'id' => $menu_name,
'label' => $label,
'description' => 'Description text',
));
]);
$this->menu->save();
// This creates a tree with the following structure:
@ -132,16 +132,16 @@ class SystemMenuBlockTest extends KernelTestBase {
// - 6
// - 8
// With link 6 being the only external link.
$links = array(
1 => MenuLinkMock::create(array('id' => 'test.example1', 'route_name' => 'example1', 'title' => 'foo', 'parent' => '', 'weight' => 0)),
2 => MenuLinkMock::create(array('id' => 'test.example2', 'route_name' => 'example2', 'title' => 'bar', 'parent' => '', 'route_parameters' => array('foo' => 'bar'), 'weight' => 1)),
3 => MenuLinkMock::create(array('id' => 'test.example3', 'route_name' => 'example3', 'title' => 'baz', 'parent' => 'test.example2', 'weight' => 2)),
4 => MenuLinkMock::create(array('id' => 'test.example4', 'route_name' => 'example4', 'title' => 'qux', 'parent' => 'test.example3', 'weight' => 3)),
5 => MenuLinkMock::create(array('id' => 'test.example5', 'route_name' => 'example5', 'title' => 'foofoo', 'parent' => '', 'expanded' => TRUE, 'weight' => 4)),
6 => MenuLinkMock::create(array('id' => 'test.example6', 'route_name' => '', 'url' => 'https://www.drupal.org/', 'title' => 'barbar', 'parent' => '', 'weight' => 5)),
7 => MenuLinkMock::create(array('id' => 'test.example7', 'route_name' => 'example7', 'title' => 'bazbaz', 'parent' => 'test.example5', 'weight' => 6)),
8 => MenuLinkMock::create(array('id' => 'test.example8', 'route_name' => 'example8', 'title' => 'quxqux', 'parent' => '', 'weight' => 7)),
);
$links = [
1 => MenuLinkMock::create(['id' => 'test.example1', 'route_name' => 'example1', 'title' => 'foo', 'parent' => '', 'weight' => 0]),
2 => MenuLinkMock::create(['id' => 'test.example2', 'route_name' => 'example2', 'title' => 'bar', 'parent' => '', 'route_parameters' => ['foo' => 'bar'], 'weight' => 1]),
3 => MenuLinkMock::create(['id' => 'test.example3', 'route_name' => 'example3', 'title' => 'baz', 'parent' => 'test.example2', 'weight' => 2]),
4 => MenuLinkMock::create(['id' => 'test.example4', 'route_name' => 'example4', 'title' => 'qux', 'parent' => 'test.example3', 'weight' => 3]),
5 => MenuLinkMock::create(['id' => 'test.example5', 'route_name' => 'example5', 'title' => 'foofoo', 'parent' => '', 'expanded' => TRUE, 'weight' => 4]),
6 => MenuLinkMock::create(['id' => 'test.example6', 'route_name' => '', 'url' => 'https://www.drupal.org/', 'title' => 'barbar', 'parent' => '', 'weight' => 5]),
7 => MenuLinkMock::create(['id' => 'test.example7', 'route_name' => 'example7', 'title' => 'bazbaz', 'parent' => 'test.example5', 'weight' => 6]),
8 => MenuLinkMock::create(['id' => 'test.example8', 'route_name' => 'example8', 'title' => 'quxqux', 'parent' => '', 'weight' => 7]),
];
foreach ($links as $instance) {
$this->menuLinkManager->addDefinition($instance->getPluginId(), $instance->getPluginDefinition());
}
@ -152,25 +152,25 @@ class SystemMenuBlockTest extends KernelTestBase {
*/
public function testSystemMenuBlockConfigDependencies() {
$block = Block::create(array(
$block = Block::create([
'plugin' => 'system_menu_block:' . $this->menu->id(),
'region' => 'footer',
'id' => 'machinename',
'theme' => 'stark',
));
]);
$dependencies = $block->calculateDependencies()->getDependencies();
$expected = array(
'config' => array(
$expected = [
'config' => [
'system.menu.' . $this->menu->id()
),
'module' => array(
],
'module' => [
'system'
),
'theme' => array(
],
'theme' => [
'stark'
),
);
],
];
$this->assertIdentical($expected, $dependencies);
}
@ -180,13 +180,13 @@ class SystemMenuBlockTest extends KernelTestBase {
public function testConfigLevelDepth() {
// Helper function to generate a configured block instance.
$place_block = function ($level, $depth) {
return $this->blockManager->createInstance('system_menu_block:' . $this->menu->id(), array(
return $this->blockManager->createInstance('system_menu_block:' . $this->menu->id(), [
'region' => 'footer',
'id' => 'machinename',
'theme' => 'stark',
'level' => $level,
'depth' => $depth,
));
]);
};
// All the different block instances we're going to test.

View file

@ -14,7 +14,7 @@ class PageRenderTest extends KernelTestBase {
/**
* Tests hook_page_attachments() exceptions.
*/
function testHookPageAttachmentsExceptions() {
public function testHookPageAttachmentsExceptions() {
$this->enableModules(['common_test', 'system']);
\Drupal::service('router.builder')->rebuild();
@ -24,7 +24,7 @@ class PageRenderTest extends KernelTestBase {
/**
* Tests hook_page_attachments_alter() exceptions.
*/
function testHookPageAlter() {
public function testHookPageAlter() {
$this->enableModules(['common_test', 'system']);
\Drupal::service('router.builder')->rebuild();
@ -39,7 +39,7 @@ class PageRenderTest extends KernelTestBase {
* @param string $hook
* The page render hook to assert expected exceptions for.
*/
function assertPageRenderHookExceptions($module, $hook) {
public function assertPageRenderHookExceptions($module, $hook) {
$html_renderer = \Drupal::getContainer()->get('main_content_renderer.html');
// Assert a valid hook implementation doesn't trigger an exception.

View file

@ -14,17 +14,17 @@ class SystemListingTest extends KernelTestBase {
/**
* Tests that files in different directories take precedence as expected.
*/
function testDirectoryPrecedence() {
public function testDirectoryPrecedence() {
// Define the module files we will search for, and the directory precedence
// we expect.
$expected_directories = array(
$expected_directories = [
// When both copies of the module are compatible with Drupal core, the
// copy in the profile directory takes precedence.
'drupal_system_listing_compatible_test' => array(
'drupal_system_listing_compatible_test' => [
'core/profiles/testing/modules',
'core/modules/system/tests/modules',
),
);
],
];
// This test relies on two versions of the same module existing in
// different places in the filesystem. Without that, the test has no
@ -32,22 +32,22 @@ class SystemListingTest extends KernelTestBase {
foreach ($expected_directories as $module => $directories) {
foreach ($directories as $directory) {
$filename = "$directory/$module/$module.info.yml";
$this->assertTrue(file_exists(\Drupal::root() . '/' . $filename), format_string('@filename exists.', array('@filename' => $filename)));
$this->assertTrue(file_exists(\Drupal::root() . '/' . $filename), format_string('@filename exists.', ['@filename' => $filename]));
}
}
// Now scan the directories and check that the files take precedence as
// expected.
$listing = new ExtensionDiscovery(\Drupal::root());
$listing->setProfileDirectories(array('core/profiles/testing'));
$listing->setProfileDirectories(['core/profiles/testing']);
$files = $listing->scan('module');
foreach ($expected_directories as $module => $directories) {
$expected_directory = array_shift($directories);
$expected_uri = "$expected_directory/$module/$module.info.yml";
$this->assertEqual($files[$module]->getPathname(), $expected_uri, format_string('Module @actual was found at @expected.', array(
$this->assertEqual($files[$module]->getPathname(), $expected_uri, format_string('Module @actual was found at @expected.', [
'@actual' => $files[$module]->getPathname(),
'@expected' => $expected_uri,
)));
]));
}
}
@ -56,7 +56,7 @@ class SystemListingTest extends KernelTestBase {
*/
public function testFileScanIgnoreDirectory() {
$listing = new ExtensionDiscovery(\Drupal::root(), FALSE);
$listing->setProfileDirectories(array('core/profiles/testing'));
$listing->setProfileDirectories(['core/profiles/testing']);
$files = $listing->scan('module');
$this->assertArrayHasKey('drupal_system_listing_compatible_test', $files);
@ -68,7 +68,7 @@ class SystemListingTest extends KernelTestBase {
$this->setSetting('file_scan_ignore_directories', ['drupal_system_listing_compatible_test']);
$listing = new ExtensionDiscovery(\Drupal::root(), FALSE);
$listing->setProfileDirectories(array('core/profiles/testing'));
$listing->setProfileDirectories(['core/profiles/testing']);
$files = $listing->scan('module');
$this->assertArrayNotHasKey('drupal_system_listing_compatible_test', $files);
}

View file

@ -38,13 +38,13 @@ class ModuleHandlerTest extends KernelTestBase {
/**
* The basic functionality of retrieving enabled modules.
*/
function testModuleList() {
public function testModuleList() {
$module_list = ['system'];
$this->assertModuleList($module_list, 'Initial');
// Try to install a new module.
$this->moduleInstaller()->install(array('ban'));
$this->moduleInstaller()->install(['ban']);
$module_list[] = 'ban';
sort($module_list);
$this->assertModuleList($module_list, 'After adding a module');
@ -58,10 +58,10 @@ class ModuleHandlerTest extends KernelTestBase {
$this->assertModuleList($module_list, 'After changing weights');
// Test the fixed list feature.
$fixed_list = array(
$fixed_list = [
'system' => 'core/modules/system/system.module',
'menu' => 'core/modules/menu/menu.module',
);
];
$this->moduleHandler()->setModuleList($fixed_list);
$new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list));
$this->assertModuleList($new_module_list, t('When using a fixed list'));
@ -77,7 +77,7 @@ class ModuleHandlerTest extends KernelTestBase {
protected function assertModuleList(array $expected_values, $condition) {
$expected_values = array_values(array_unique($expected_values));
$enabled_modules = array_keys($this->container->get('module_handler')->getModuleList());
$this->assertEqual($expected_values, $enabled_modules, format_string('@condition: extension handler returns correct results', array('@condition' => $condition)));
$this->assertEqual($expected_values, $enabled_modules, format_string('@condition: extension handler returns correct results', ['@condition' => $condition]));
}
/**
@ -92,8 +92,8 @@ class ModuleHandlerTest extends KernelTestBase {
* @see module_test_system_info_alter()
* @see https://www.drupal.org/files/issues/dep.gv__0.png
*/
function testDependencyResolution() {
$this->enableModules(array('module_test'));
public function testDependencyResolution() {
$this->enableModules(['module_test']);
$this->assertTrue($this->moduleHandler()->moduleExists('module_test'), 'Test module is enabled.');
// Ensure that modules are not enabled.
@ -108,7 +108,7 @@ class ModuleHandlerTest extends KernelTestBase {
drupal_static_reset('system_rebuild_module_data');
try {
$result = $this->moduleInstaller()->install(array('color'));
$result = $this->moduleInstaller()->install(['color']);
$this->fail(t('ModuleInstaller::install() throws an exception if dependencies are missing.'));
}
catch (MissingDependencyException $e) {
@ -122,7 +122,7 @@ class ModuleHandlerTest extends KernelTestBase {
\Drupal::state()->set('module_test.dependency', 'dependency');
drupal_static_reset('system_rebuild_module_data');
$result = $this->moduleInstaller()->install(array('color'));
$result = $this->moduleInstaller()->install(['color']);
$this->assertTrue($result, 'ModuleInstaller::install() returns the correct value.');
// Verify that the fake dependency chain was installed.
@ -132,20 +132,20 @@ class ModuleHandlerTest extends KernelTestBase {
$this->assertTrue($this->moduleHandler()->moduleExists('color'), 'Module installation with dependencies succeeded.');
// Verify that the modules were enabled in the correct order.
$module_order = \Drupal::state()->get('module_test.install_order') ?: array();
$this->assertEqual($module_order, array('help', 'config', 'color'));
$module_order = \Drupal::state()->get('module_test.install_order') ?: [];
$this->assertEqual($module_order, ['help', 'config', 'color']);
// Uninstall all three modules explicitly, but in the incorrect order,
// and make sure that ModuleInstaller::uninstall() uninstalled them in the
// correct sequence.
$result = $this->moduleInstaller()->uninstall(array('config', 'help', 'color'));
$result = $this->moduleInstaller()->uninstall(['config', 'help', 'color']);
$this->assertTrue($result, 'ModuleInstaller::uninstall() returned TRUE.');
foreach (array('color', 'config', 'help') as $module) {
foreach (['color', 'config', 'help'] as $module) {
$this->assertEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, "$module module was uninstalled.");
}
$uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: array();
$this->assertEqual($uninstalled_modules, array('color', 'config', 'help'), 'Modules were uninstalled in the correct order.');
$uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: [];
$this->assertEqual($uninstalled_modules, ['color', 'config', 'help'], 'Modules were uninstalled in the correct order.');
// Enable Color module again, which should enable both the Config module and
// Help module. But, this time do it with Config module declaring a
@ -154,7 +154,7 @@ class ModuleHandlerTest extends KernelTestBase {
\Drupal::state()->set('module_test.dependency', 'version dependency');
drupal_static_reset('system_rebuild_module_data');
$result = $this->moduleInstaller()->install(array('color'));
$result = $this->moduleInstaller()->install(['color']);
$this->assertTrue($result, 'ModuleInstaller::install() returns the correct value.');
// Verify that the fake dependency chain was installed.
@ -164,14 +164,14 @@ class ModuleHandlerTest extends KernelTestBase {
$this->assertTrue($this->moduleHandler()->moduleExists('color'), 'Module installation with version dependencies succeeded.');
// Finally, verify that the modules were enabled in the correct order.
$enable_order = \Drupal::state()->get('module_test.install_order') ?: array();
$this->assertIdentical($enable_order, array('help', 'config', 'color'));
$enable_order = \Drupal::state()->get('module_test.install_order') ?: [];
$this->assertIdentical($enable_order, ['help', 'config', 'color']);
}
/**
* Tests uninstalling a module that is a "dependency" of a profile.
*/
function testUninstallProfileDependency() {
public function testUninstallProfileDependency() {
$profile = 'minimal';
$dependency = 'dblog';
$this->setSetting('install_profile', $profile);
@ -180,23 +180,23 @@ class ModuleHandlerTest extends KernelTestBase {
// yet have any cached way to retrieve its location.
// @todo Remove as part of https://www.drupal.org/node/2186491
drupal_get_filename('profile', $profile, 'core/profiles/' . $profile . '/' . $profile . '.info.yml');
$this->enableModules(array('module_test', $profile));
$this->enableModules(['module_test', $profile]);
drupal_static_reset('system_rebuild_module_data');
$data = system_rebuild_module_data();
$this->assertTrue(isset($data[$profile]->requires[$dependency]));
$this->moduleInstaller()->install(array($dependency));
$this->moduleInstaller()->install([$dependency]);
$this->assertTrue($this->moduleHandler()->moduleExists($dependency));
// Uninstall the profile module "dependency".
$result = $this->moduleInstaller()->uninstall(array($dependency));
$result = $this->moduleInstaller()->uninstall([$dependency]);
$this->assertTrue($result, 'ModuleInstaller::uninstall() returns TRUE.');
$this->assertFalse($this->moduleHandler()->moduleExists($dependency));
$this->assertEqual(drupal_get_installed_schema_version($dependency), SCHEMA_UNINSTALLED, "$dependency module was uninstalled.");
// Verify that the installation profile itself was not uninstalled.
$uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: array();
$uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: [];
$this->assertTrue(in_array($dependency, $uninstalled_modules), "$dependency module is in the list of uninstalled modules.");
$this->assertFalse(in_array($profile, $uninstalled_modules), 'The installation profile is not in the list of uninstalled modules.');
}
@ -204,8 +204,8 @@ class ModuleHandlerTest extends KernelTestBase {
/**
* Tests uninstalling a module that has content.
*/
function testUninstallContentDependency() {
$this->enableModules(array('module_test', 'entity_test', 'text', 'user', 'help'));
public function testUninstallContentDependency() {
$this->enableModules(['module_test', 'entity_test', 'text', 'user', 'help']);
$this->assertTrue($this->moduleHandler()->moduleExists('entity_test'), 'Test module is enabled.');
$this->assertTrue($this->moduleHandler()->moduleExists('module_test'), 'Test module is enabled.');
@ -224,13 +224,13 @@ class ModuleHandlerTest extends KernelTestBase {
drupal_static_reset('system_rebuild_module_data');
// Create an entity so that the modules can not be disabled.
$entity = EntityTest::create(array('name' => $this->randomString()));
$entity = EntityTest::create(['name' => $this->randomString()]);
$entity->save();
// Uninstalling entity_test is not possible when there is content.
try {
$message = 'ModuleInstaller::uninstall() throws ModuleUninstallValidatorException upon uninstalling a module which does not pass validation.';
$this->moduleInstaller()->uninstall(array('entity_test'));
$this->moduleInstaller()->uninstall(['entity_test']);
$this->fail($message);
}
catch (ModuleUninstallValidatorException $e) {
@ -240,7 +240,7 @@ class ModuleHandlerTest extends KernelTestBase {
// Uninstalling help needs entity_test to be un-installable.
try {
$message = 'ModuleInstaller::uninstall() throws ModuleUninstallValidatorException upon uninstalling a module which does not pass validation.';
$this->moduleInstaller()->uninstall(array('help'));
$this->moduleInstaller()->uninstall(['help']);
$this->fail($message);
}
catch (ModuleUninstallValidatorException $e) {
@ -250,7 +250,7 @@ class ModuleHandlerTest extends KernelTestBase {
// Deleting the entity.
$entity->delete();
$result = $this->moduleInstaller()->uninstall(array('help'));
$result = $this->moduleInstaller()->uninstall(['help']);
$this->assertTrue($result, 'ModuleInstaller::uninstall() returns TRUE.');
$this->assertEqual(drupal_get_installed_schema_version('entity_test'), SCHEMA_UNINSTALLED, "entity_test module was uninstalled.");
}
@ -258,7 +258,7 @@ class ModuleHandlerTest extends KernelTestBase {
/**
* Tests whether the correct module metadata is returned.
*/
function testModuleMetaData() {
public function testModuleMetaData() {
// Generate the list of available modules.
$modules = system_rebuild_module_data();
// Check that the mtime field exists for the system module.
@ -289,7 +289,7 @@ class ModuleHandlerTest extends KernelTestBase {
/**
* Tests whether the correct theme metadata is returned.
*/
function testThemeMetaData() {
public function testThemeMetaData() {
// Generate the list of available themes.
$themes = \Drupal::service('theme_handler')->rebuildThemeData();
// Check that the mtime field exists for the bartik theme.

View file

@ -31,6 +31,16 @@ class FormElementLabelTest extends KernelTestBase {
$this->render($render_array);
$elements = $this->xpath($css_selector_converter->toXPath('.kitten'));
$this->assertCount(1, $elements);
// Add label attributes to a form element.
$render_array = [
'#type' => 'textfield',
'#label_attributes' => ['class' => ['meow']],
'#title' => 'Kitten sounds',
];
$this->render($render_array);
$elements = $this->xpath($css_selector_converter->toXPath('label.meow'));
$this->assertCount(1, $elements);
}
}

View file

@ -37,7 +37,7 @@ class MigrateDateFormatTest extends MigrateDrupal6TestBase {
// Test that we can re-import using the EntityDateFormat destination.
Database::getConnection('default', 'migrate')
->update('variable')
->fields(array('value' => serialize('\S\H\O\R\T d/m/Y - H:i')))
->fields(['value' => serialize('\S\H\O\R\T d/m/Y - H:i')])
->condition('name', 'date_format_short')
->execute();

View file

@ -36,14 +36,14 @@ EOT;
// Test that we can re-import using the ConfigEntityBase destination.
Database::getConnection('default', 'migrate')
->update('menu_custom')
->fields(array('title' => 'Home Navigation'))
->fields(['title' => 'Home Navigation'])
->condition('menu_name', 'navigation')
->execute();
$migration = $this->getMigration('d6_menu');
\Drupal::database()
->truncate($migration->getIdMap()->mapTableName())
->execute();
->truncate($migration->getIdMap()->mapTableName())
->execute();
$this->executeMigration($migration);
$navigation_menu = Menu::load('navigation');

View file

@ -0,0 +1,162 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Migrates various configuration objects owned by the System module.
*
* @group migrate_drupal_6
*/
class MigrateSystemConfigurationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['action', 'file', 'system'];
protected $expectedConfig = [
'system.cron' => [
'threshold' => [
'requirements_warning' => 172800,
'requirements_error' => 1209600,
],
// logging is not handled by the migration.
'logging' => 1,
],
'system.date' => [
// country is not handled by the migration.
'country' => [
'default' => '',
],
'first_day' => 4,
// timezone is not handled by the migration.
'timezone' => [
'default' => 'Europe/Paris',
'user' => [
'configurable' => FALSE,
// warn is not handled by the migration.
'warn' => FALSE,
// default is not handled by the migration.
'default' => 0,
],
],
],
'system.file' => [
'allow_insecure_uploads' => TRUE,
// default_scheme is not handled by the migration.
'default_scheme' => 'public',
'path' => [
'temporary' => 'files/temp',
],
// temporary_maximum_age is not handled by the migration.
'temporary_maximum_age' => 21600,
],
'system.image.gd' => [
'jpeg_quality' => 75,
],
'system.image' => [
'toolkit' => 'gd',
],
'system.logging' => [
'error_level' => 'some',
],
'system.maintenance' => [
'message' => 'Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.',
// langcode is not handled by the migration.
'langcode' => 'en',
],
'system.performance' => [
'cache' => [
'page' => [
'max_age' => 0,
],
],
'css' => [
'preprocess' => FALSE,
// gzip is not handled by the migration.
'gzip' => TRUE,
],
// fast_404 is not handled by the migration.
'fast_404' => [
'enabled' => TRUE,
'paths' => '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i',
'exclude_paths' => '/\/(?:styles|imagecache)\//',
'html' => '<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>',
],
'js' => [
'preprocess' => FALSE,
// gzip is not handled by the migration.
'gzip' => TRUE,
],
// stale_file_threshold is not handled by the migration.
'stale_file_threshold' => 2592000,
'response' => [
'gzip' => TRUE,
],
],
'system.rss' => [
// channel is not handled by the migration.
'channel' => [
'description' => '',
],
'items' => [
'limit' => 10,
'view_mode' => 'title',
],
// langcode is not handled by the migration.
'langcode' => 'en',
],
'system.site' => [
// uuid is not handled by the migration.
'uuid' => '',
'name' => 'site_name',
'mail' => 'site_mail@example.com',
'slogan' => 'Migrate rocks',
'page' => [
'403' => '/user',
'404' => '/page-not-found',
'front' => '/node',
],
'admin_compact_mode' => FALSE,
'weight_select_max' => 100,
// langcode and default_langcode are not handled by the migration.
'langcode' => 'en',
'default_langcode' => 'en',
],
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$migrations = [
'd6_system_cron',
'd6_system_date',
'd6_system_file',
'system_image_gd',
'system_image',
'system_logging',
'system_maintenance',
'd6_system_performance',
'system_rss',
'system_site',
];
$this->executeMigrations($migrations);
}
/**
* Tests that all expected configuration gets migrated.
*/
public function testConfigurationMigration() {
foreach ($this->expectedConfig as $config_id => $values) {
$actual = \Drupal::config($config_id)->get();
unset($actual['_core']);
$this->assertSame($actual, $values, $config_id . ' matches expected values.');
}
}
}

View file

@ -1,31 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade cron variable to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemCronTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_system_cron');
}
/**
* Tests migration of system (cron) variables to system.cron.yml.
*/
public function testSystemCron() {
$config = $this->config('system.cron');
$this->assertIdentical(172800, $config->get('threshold.requirements_warning'));
$this->assertIdentical(1209600, $config->get('threshold.requirements_error'));
}
}

View file

@ -1,32 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade date time variables to system.date config
*
* @group migrate_drupal_6
*/
class MigrateSystemDateTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_system_date');
}
/**
* Tests migration of user variables to system_date.yml.
*/
public function testSystemDate() {
$config = $this->config('system.date');
$this->assertIdentical(4, $config->get('first_day'));
$this->assertIdentical(FALSE, $config->get('timezone.user.configurable'));
$this->assertIdentical("Europe/Paris", $config->get('timezone.default'));
}
}

View file

@ -1,31 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemFileTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_system_file');
}
/**
* Tests migration of system (file) variables to system.file.yml.
*/
public function testSystemFile() {
$config = \Drupal::configFactory()->getEditable('system.file');
$this->assertIdentical('files/temp', $config->get('path.temporary'));
$this->assertIdentical(TRUE, $config->get('allow_insecure_uploads'));
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade image gd variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemImageGdTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('system_image_gd');
}
/**
* Tests migration of system (image GD) variables to system.image.gd.yml.
*/
public function testSystemImageGd() {
$config = $this->config('system.image.gd');
$this->assertIdentical(75, $config->get('jpeg_quality'));
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade image variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemImageTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('system_image');
}
/**
* Tests migration of system (image) variables to system.image.yml.
*/
public function testSystemImage() {
$config = $this->config('system.image');
$this->assertIdentical('gd', $config->get('toolkit'));
}
}

View file

@ -1,34 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade error_level variable to system.logging.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemLoggingTest extends MigrateDrupal6TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('system_logging');
}
/**
* Tests migration of system error_level variables to system.logging.yml.
*/
public function testSystemLogging() {
$config = $this->config('system.logging');
$this->assertIdentical('some', $config->get('error_level'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'system.logging', $config->get());
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade maintenance variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemMaintenanceTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('system_maintenance');
}
/**
* Tests migration of system (maintenance) variables to system.maintenance.yml.
*/
public function testSystemMaintenance() {
$config = $this->config('system.maintenance');
$this->assertIdentical('Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.', $config->get('message'));
}
}

View file

@ -1,32 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade performance variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemPerformanceTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_system_performance');
}
/**
* Tests migration of system (Performance) variables to system.performance.yml.
*/
public function testSystemPerformance() {
$config = $this->config('system.performance');
$this->assertIdentical(FALSE, $config->get('css.preprocess'));
$this->assertIdentical(FALSE, $config->get('js.preprocess'));
$this->assertIdentical(0, $config->get('cache.page.max_age'));
}
}

View file

@ -1,31 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade rss variable to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemRssTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('system_rss');
}
/**
* Tests migration of system (rss) variables to system.rss.yml.
*/
public function testSystemRss() {
$config = $this->config('system.rss');
$this->assertIdentical(10, $config->get('items.limit'));
$this->assertIdentical('title', $config->get('items.view_mode'));
}
}

View file

@ -1,36 +0,0 @@
<?php
namespace Drupal\Tests\system\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade site variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemSiteTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('system_site');
}
/**
* Tests migration of system (site) variables to system.site.yml.
*/
public function testSystemSite() {
$config = $this->config('system.site');
$this->assertIdentical('site_name', $config->get('name'));
$this->assertIdentical('site_mail@example.com', $config->get('mail'));
$this->assertIdentical('Migrate rocks', $config->get('slogan'));
$this->assertIdentical('/user', $config->get('page.403'));
$this->assertIdentical('/page-not-found', $config->get('page.404'));
$this->assertIdentical('/node', $config->get('page.front'));
$this->assertIdentical(FALSE, $config->get('admin_compact_mode'));
}
}

View file

@ -51,14 +51,14 @@ class MigrateMenuTest extends MigrateDrupal7TestBase {
// Test that we can re-import using the ConfigEntityBase destination.
Database::getConnection('default', 'migrate')
->update('menu_custom')
->fields(array('title' => 'Home Navigation'))
->fields(['title' => 'Home Navigation'])
->condition('menu_name', 'navigation')
->execute();
$migration = $this->getMigration('d7_menu');
\Drupal::database()
->truncate($migration->getIdMap()->mapTableName())
->execute();
->truncate($migration->getIdMap()->mapTableName())
->execute();
$this->executeMigration($migration);
$navigation_menu = Menu::load('tools');

View file

@ -24,6 +24,7 @@ class MigrateSystemConfigurationTest extends MigrateDrupal7TestBase {
'requirements_warning' => 172800,
'requirements_error' => 1209600,
],
'logging' => 1,
],
'system.date' => [
'country' => [

View file

@ -57,19 +57,19 @@ class PhpStorageFactoryTest extends KernelTestBase {
$this->assertTrue($php instanceof MockPhpStorage, 'A MockPhpStorage instance was returned from overridden settings.');
// Test that the name is used for the bin when it is NULL.
$this->setSettings('test', array('bin' => NULL));
$this->setSettings('test', ['bin' => NULL]);
$php = PhpStorageFactory::get('test');
$this->assertTrue($php instanceof MockPhpStorage, 'An MockPhpStorage instance was returned from overridden settings.');
$this->assertSame('test', $php->getConfigurationValue('bin'), 'Name value was used for bin.');
// Test that a default directory is set if it's empty.
$this->setSettings('test', array('directory' => NULL));
$this->setSettings('test', ['directory' => NULL]);
$php = PhpStorageFactory::get('test');
$this->assertTrue($php instanceof MockPhpStorage, 'An MockPhpStorage instance was returned from overridden settings.');
$this->assertSame(PublicStream::basePath() . '/php', $php->getConfigurationValue('directory'), 'Default file directory was used.');
// Test that a default storage class is set if it's empty.
$this->setSettings('test', array('class' => NULL));
$this->setSettings('test', ['class' => NULL]);
$php = PhpStorageFactory::get('test');
$this->assertTrue($php instanceof MTimeProtectedFileStorage, 'An MTimeProtectedFileStorage instance was returned from overridden settings with no class.');
@ -79,7 +79,7 @@ class PhpStorageFactoryTest extends KernelTestBase {
$this->assertNotEquals('mock hash salt', $php->getConfigurationValue('secret'), 'The default secret is not used if a secret is set in the overridden settings.');
// Test that a default secret is set if it's empty.
$this->setSettings('test', array('secret' => NULL));
$this->setSettings('test', ['secret' => NULL]);
$php = PhpStorageFactory::get('test');
$this->assertSame('mock hash salt', $php->getConfigurationValue('secret'), 'The default secret is used if one is not set in the overridden settings.');
}
@ -92,13 +92,13 @@ class PhpStorageFactoryTest extends KernelTestBase {
* @param array $configuration
* An array of configuration to set. Will be merged with default values.
*/
protected function setSettings($name = 'default', array $configuration = array()) {
$settings['php_storage'][$name] = $configuration + array(
protected function setSettings($name = 'default', array $configuration = []) {
$settings['php_storage'][$name] = $configuration + [
'class' => 'Drupal\system\PhpStorage\MockPhpStorage',
'directory' => 'tmp://',
'secret' => $this->randomString(),
'bin' => 'test',
);
];
$settings['hash_salt'] = 'mock hash salt';
new Settings($settings);
}

View file

@ -14,7 +14,7 @@ class ClassyTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('system', 'twig_theme_test');
public static $modules = ['system', 'twig_theme_test'];
/**
* {@inheritdoc}
@ -36,12 +36,12 @@ class ClassyTest extends KernelTestBase {
/**
* Test the classy theme.
*/
function testClassyTheme() {
public function testClassyTheme() {
drupal_set_message('An error occurred', 'error');
drupal_set_message('But then something nice happened');
$messages = array(
$messages = [
'#type' => 'status_messages',
);
];
$this->render($messages);
$this->assertNoText('custom-test-messages-class', 'The custom class attribute value added in the status messages preprocess function is not displayed as page content.');
}

View file

@ -8,6 +8,7 @@
namespace Drupal\Tests\system\Kernel\Scripts;
use Drupal\Core\Command\DbCommandBase;
use Drupal\Core\Database\ConnectionNotDefinedException;
use Drupal\Core\Database\Database;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\Console\Input\InputInterface;
@ -42,8 +43,6 @@ class DbCommandBaseTest extends KernelTestBase {
/**
* Invalid database names will throw a useful exception.
*
* @expectedException \Drupal\Core\Database\ConnectionNotDefinedException
*/
public function testSpecifyDatabaseDoesNotExist() {
$command = new DbCommandBaseTester();
@ -51,6 +50,7 @@ class DbCommandBaseTest extends KernelTestBase {
$command_tester->execute([
'--database' => 'dne'
]);
$this->setExpectedException(ConnectionNotDefinedException::class);
$command->getDatabaseConnection($command_tester->getInput());
}

View file

@ -51,10 +51,10 @@ class DbImportCommandTest extends KernelTestBase {
* @requires extension pdo_sqlite
*/
public function testDbImportCommand() {
$connection_info = array(
$connection_info = [
'driver' => 'sqlite',
'database' => ':memory:',
);
];
Database::addConnectionInfo($this->databasePrefix, 'default', $connection_info);
$command = new DbImportCommand();

View file

@ -55,7 +55,7 @@ class CronQueueTest extends KernelTestBase {
$queue = $this->container->get('queue')->get('cron_queue_test_exception');
// Enqueue an item for processing.
$queue->createItem(array($this->randomMachineName() => $this->randomMachineName()));
$queue->createItem([$this->randomMachineName() => $this->randomMachineName()]);
// Run cron; the worker for this queue should throw an exception and handle
// it.

View file

@ -11,7 +11,7 @@ use Drupal\KernelTests\KernelTestBase;
*/
class InfoAlterTest extends KernelTestBase {
public static $modules = array('system');
public static $modules = ['system'];
/**
* Tests that theme .info.yml data is rebuild after enabling a module.
@ -20,13 +20,13 @@ class InfoAlterTest extends KernelTestBase {
* hook_system_info_alter() is enabled. Also tests if core *_list() functions
* return freshly altered info.
*/
function testSystemInfoAlter() {
public function testSystemInfoAlter() {
\Drupal::state()->set('module_required_test.hook_system_info_alter', TRUE);
$info = system_rebuild_module_data();
$this->assertFalse(isset($info['node']->info['required']), 'Before the module_required_test is installed the node module is not required.');
// Enable the test module.
\Drupal::service('module_installer')->install(array('module_required_test'), FALSE);
\Drupal::service('module_installer')->install(['module_required_test'], FALSE);
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('module_required_test'), 'Test required module is enabled.');
$info = system_rebuild_module_data();

View file

@ -29,25 +29,25 @@ class TokenReplaceKernelTest extends TokenReplaceKernelTestBase {
*/
public function testSystemTokenRecognition() {
// Generate prefixes and suffixes for the token context.
$tests = array(
array('prefix' => 'this is the ', 'suffix' => ' site'),
array('prefix' => 'this is the', 'suffix' => 'site'),
array('prefix' => '[', 'suffix' => ']'),
array('prefix' => '', 'suffix' => ']]]'),
array('prefix' => '[[[', 'suffix' => ''),
array('prefix' => ':[:', 'suffix' => '--]'),
array('prefix' => '-[-', 'suffix' => ':]:'),
array('prefix' => '[:', 'suffix' => ']'),
array('prefix' => '[site:', 'suffix' => ':name]'),
array('prefix' => '[site:', 'suffix' => ']'),
);
$tests = [
['prefix' => 'this is the ', 'suffix' => ' site'],
['prefix' => 'this is the', 'suffix' => 'site'],
['prefix' => '[', 'suffix' => ']'],
['prefix' => '', 'suffix' => ']]]'],
['prefix' => '[[[', 'suffix' => ''],
['prefix' => ':[:', 'suffix' => '--]'],
['prefix' => '-[-', 'suffix' => ':]:'],
['prefix' => '[:', 'suffix' => ']'],
['prefix' => '[site:', 'suffix' => ':name]'],
['prefix' => '[site:', 'suffix' => ']'],
];
// Check if the token is recognized in each of the contexts.
foreach ($tests as $test) {
$input = $test['prefix'] . '[site:name]' . $test['suffix'];
$expected = $test['prefix'] . 'Drupal' . $test['suffix'];
$output = $this->tokenService->replace($input, array(), array('langcode' => $this->interfaceLanguage->getId()));
$this->assertTrue($output == $expected, format_string('Token recognized in string %string', array('%string' => $input)));
$output = $this->tokenService->replace($input, [], ['langcode' => $this->interfaceLanguage->getId()]);
$this->assertTrue($output == $expected, format_string('Token recognized in string %string', ['%string' => $input]));
}
// Test token replacement when the string contains no tokens.
@ -67,12 +67,12 @@ class TokenReplaceKernelTest extends TokenReplaceKernelTestBase {
// Replace with the clear parameter, only the valid token should remain.
$target = Html::escape($this->config('system.site')->get('name'));
$result = $this->tokenService->replace($source, array(), array('langcode' => $this->interfaceLanguage->getId(), 'clear' => TRUE));
$result = $this->tokenService->replace($source, [], ['langcode' => $this->interfaceLanguage->getId(), 'clear' => TRUE]);
$this->assertEqual($target, $result, 'Valid tokens replaced while invalid tokens ignored.');
$target .= '[user:name]';
$target .= '[bogus:token]';
$result = $this->tokenService->replace($source, array(), array('langcode' => $this->interfaceLanguage->getId()));
$result = $this->tokenService->replace($source, [], ['langcode' => $this->interfaceLanguage->getId()]);
$this->assertEqual($target, $result, 'Valid tokens replaced while invalid tokens ignored.');
}
@ -80,10 +80,10 @@ class TokenReplaceKernelTest extends TokenReplaceKernelTestBase {
* Tests the generation of all system site information tokens.
*/
public function testSystemSiteTokenReplacement() {
$url_options = array(
$url_options = [
'absolute' => TRUE,
'language' => $this->interfaceLanguage,
);
];
$slogan = '<blink>Slogan</blink>';
$safe_slogan = Xss::filterAdmin($slogan);
@ -98,12 +98,12 @@ class TokenReplaceKernelTest extends TokenReplaceKernelTestBase {
// Generate and test tokens.
$tests = array();
$tests = [];
$tests['[site:name]'] = Html::escape($config->get('name'));
$tests['[site:slogan]'] = $safe_slogan;
$tests['[site:mail]'] = $config->get('mail');
$tests['[site:url]'] = \Drupal::url('<front>', [], $url_options);
$tests['[site:url-brief]'] = preg_replace(array('!^https?://!', '!/$!'), '', \Drupal::url('<front>', [], $url_options));
$tests['[site:url-brief]'] = preg_replace(['!^https?://!', '!/$!'], '', \Drupal::url('<front>', [], $url_options));
$tests['[site:login-url]'] = \Drupal::url('user.page', [], $url_options);
$base_bubbleable_metadata = new BubbleableMetadata();
@ -122,7 +122,7 @@ class TokenReplaceKernelTest extends TokenReplaceKernelTestBase {
foreach ($tests as $input => $expected) {
$bubbleable_metadata = new BubbleableMetadata();
$output = $this->tokenService->replace($input, array(), array('langcode' => $this->interfaceLanguage->getId()), $bubbleable_metadata);
$output = $this->tokenService->replace($input, [], ['langcode' => $this->interfaceLanguage->getId()], $bubbleable_metadata);
$this->assertEqual($output, $expected, new FormattableMarkup('System site information token %token replaced.', ['%token' => $input]));
$this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
}
@ -136,21 +136,21 @@ class TokenReplaceKernelTest extends TokenReplaceKernelTestBase {
$date = REQUEST_TIME - 3600;
// Generate and test tokens.
$tests = array();
$tests = [];
$date_formatter = \Drupal::service('date.formatter');
$tests['[date:short]'] = $date_formatter->format($date, 'short', '', NULL, $this->interfaceLanguage->getId());
$tests['[date:medium]'] = $date_formatter->format($date, 'medium', '', NULL, $this->interfaceLanguage->getId());
$tests['[date:long]'] = $date_formatter->format($date, 'long', '', NULL, $this->interfaceLanguage->getId());
$tests['[date:custom:m/j/Y]'] = $date_formatter->format($date, 'custom', 'm/j/Y', NULL, $this->interfaceLanguage->getId());
$tests['[date:since]'] = $date_formatter->formatTimeDiffSince($date, array('langcode' => $this->interfaceLanguage->getId()));
$tests['[date:since]'] = $date_formatter->formatTimeDiffSince($date, ['langcode' => $this->interfaceLanguage->getId()]);
$tests['[date:raw]'] = Xss::filter($date);
// Test to make sure that we generated something for each token.
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
foreach ($tests as $input => $expected) {
$output = $this->tokenService->replace($input, array('date' => $date), array('langcode' => $this->interfaceLanguage->getId()));
$this->assertEqual($output, $expected, format_string('Date token %token replaced.', array('%token' => $input)));
$output = $this->tokenService->replace($input, ['date' => $date], ['langcode' => $this->interfaceLanguage->getId()]);
$this->assertEqual($output, $expected, format_string('Date token %token replaced.', ['%token' => $input]));
}
}

View file

@ -28,12 +28,12 @@ abstract class TokenReplaceKernelTestBase extends EntityKernelTestBase {
*
* @var array
*/
public static $modules = array('system');
public static $modules = ['system'];
protected function setUp() {
parent::setUp();
// Install default system configuration.
$this->installConfig(array('system'));
$this->installConfig(['system']);
\Drupal::service('router.builder')->rebuild();
$this->interfaceLanguage = \Drupal::languageManager()->getCurrentLanguage();

View file

@ -94,7 +94,7 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
$this->requestMatcher = $this->getMock('\Symfony\Component\Routing\Matcher\RequestMatcherInterface');
$config_factory = $this->getConfigFactoryStub(array('system.site' => array('front' => 'test_frontpage')));
$config_factory = $this->getConfigFactoryStub(['system.site' => ['front' => 'test_frontpage']]);
$this->pathProcessor = $this->getMock('\Drupal\Core\PathProcessor\InboundPathProcessorInterface');
$this->context = $this->getMock('\Drupal\Core\Routing\RequestContext');
@ -182,11 +182,11 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
->method('matchRequest')
->will($this->returnCallback(function(Request $request) use ($route_1) {
if ($request->getPathInfo() == '/example') {
return array(
return [
RouteObjectInterface::ROUTE_NAME => 'example',
RouteObjectInterface::ROUTE_OBJECT => $route_1,
'_raw_variables' => new ParameterBag(array()),
);
'_raw_variables' => new ParameterBag([]),
];
}
}));
@ -218,18 +218,18 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
->method('matchRequest')
->will($this->returnCallback(function(Request $request) use ($route_1, $route_2) {
if ($request->getPathInfo() == '/example/bar') {
return array(
return [
RouteObjectInterface::ROUTE_NAME => 'example_bar',
RouteObjectInterface::ROUTE_OBJECT => $route_1,
'_raw_variables' => new ParameterBag(array()),
);
'_raw_variables' => new ParameterBag([]),
];
}
elseif ($request->getPathInfo() == '/example') {
return array(
return [
RouteObjectInterface::ROUTE_NAME => 'example',
RouteObjectInterface::ROUTE_OBJECT => $route_2,
'_raw_variables' => new ParameterBag(array()),
);
'_raw_variables' => new ParameterBag([]),
];
}
}));
@ -286,11 +286,11 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
* @see \Drupal\Tests\system\Unit\Breadcrumbs\PathBasedBreadcrumbBuilderTest::testBuildWithException()
*/
public function providerTestBuildWithException() {
return array(
array('Drupal\Core\ParamConverter\ParamNotConvertedException', ''),
array('Symfony\Component\Routing\Exception\MethodNotAllowedException', array()),
array('Symfony\Component\Routing\Exception\ResourceNotFoundException', ''),
);
return [
['Drupal\Core\ParamConverter\ParamNotConvertedException', ''],
['Symfony\Component\Routing\Exception\MethodNotAllowedException', []],
['Symfony\Component\Routing\Exception\ResourceNotFoundException', ''],
];
}
/**
@ -310,7 +310,7 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
$this->requestMatcher->expects($this->any())
->method('matchRequest')
->will($this->returnValue(array()));
->will($this->returnValue([]));
$breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
@ -348,11 +348,11 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
->method('matchRequest')
->will($this->returnCallback(function(Request $request) use ($route_1) {
if ($request->getPathInfo() == '/user/1') {
return array(
return [
RouteObjectInterface::ROUTE_NAME => 'user_page',
RouteObjectInterface::ROUTE_OBJECT => $route_1,
'_raw_variables' => new ParameterBag(array()),
);
'_raw_variables' => new ParameterBag([]),
];
}
}));

View file

@ -45,11 +45,11 @@ class InstallTranslationFilePatternTest extends UnitTestCase {
* @return array
*/
public function providerValidTranslationFiles() {
return array(
array('hu', 'drupal-8.0.0-alpha1.hu.po'),
array('ta', 'drupal-8.10.10-beta12.ta.po'),
array('hi', 'drupal-8.0.0.hi.po'),
);
return [
['hu', 'drupal-8.0.0-alpha1.hu.po'],
['ta', 'drupal-8.10.10-beta12.ta.po'],
['hi', 'drupal-8.0.0.hi.po'],
];
}
/**
@ -64,13 +64,13 @@ class InstallTranslationFilePatternTest extends UnitTestCase {
* @return array
*/
public function providerInvalidTranslationFiles() {
return array(
array('hu', 'drupal-alpha1-*-hu.po'),
array('ta', 'drupal-beta12.ta'),
array('hi', 'drupal-hi.po'),
array('de', 'drupal-dummy-de.po'),
array('hu', 'drupal-10.0.1.alpha1-hu.po'),
);
return [
['hu', 'drupal-alpha1-*-hu.po'],
['ta', 'drupal-beta12.ta'],
['hi', 'drupal-hi.po'],
['de', 'drupal-dummy-de.po'],
['hu', 'drupal-10.0.1.alpha1-hu.po'],
];
}
}

View file

@ -25,20 +25,20 @@ class SystemLocalTasksTest extends LocalTaskIntegrationTestBase {
protected function setUp() {
parent::setUp();
$this->directoryList = array(
$this->directoryList = [
'system' => 'core/modules/system',
);
];
$this->themeHandler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
$theme = new Extension($this->root, 'theme', '/core/themes/bartik', 'bartik.info.yml');
$theme->status = 1;
$theme->info = array('name' => 'bartik');
$theme->info = ['name' => 'bartik'];
$this->themeHandler->expects($this->any())
->method('listInfo')
->will($this->returnValue(array(
->will($this->returnValue([
'bartik' => $theme,
)));
]));
$this->themeHandler->expects($this->any())
->method('hasUi')
->with('bartik')
@ -59,13 +59,13 @@ class SystemLocalTasksTest extends LocalTaskIntegrationTestBase {
* Provides a list of routes to test.
*/
public function getSystemAdminRoutes() {
return array(
array('system.admin_content', array(array('system.admin_content'))),
array('system.theme_settings_theme', array(
array('system.themes_page', 'system.theme_settings'),
array('system.theme_settings_global', 'system.theme_settings_theme:bartik'),
)),
);
return [
['system.admin_content', [['system.admin_content']]],
['system.theme_settings_theme', [
['system.themes_page', 'system.theme_settings'],
['system.theme_settings_global', 'system.theme_settings_theme:bartik'],
]],
];
}
}

View file

@ -53,21 +53,21 @@ class MachineNameControllerTest extends UnitTestCase {
* - The expected content of the JSONresponse.
*/
public function providerTestMachineNameController() {
$valid_data = array(
array(array('text' => 'Bob', 'langcode' => 'en'), '"Bob"'),
array(array('text' => 'Bob', 'langcode' => 'en', 'lowercase' => TRUE), '"bob"'),
array(array('text' => 'Bob', 'langcode' => 'en', 'replace' => 'Alice', 'replace_pattern' => 'Bob'), '"Alice"'),
array(array('text' => 'Bob', 'langcode' => 'en', 'replace' => 'Alice', 'replace_pattern' => 'Tom'), '"Bob"'),
array(array('text' => 'Äwesome', 'langcode' => 'en', 'lowercase' => TRUE), '"awesome"'),
array(array('text' => 'Äwesome', 'langcode' => 'de', 'lowercase' => TRUE), '"aewesome"'),
$valid_data = [
[['text' => 'Bob', 'langcode' => 'en'], '"Bob"'],
[['text' => 'Bob', 'langcode' => 'en', 'lowercase' => TRUE], '"bob"'],
[['text' => 'Bob', 'langcode' => 'en', 'replace' => 'Alice', 'replace_pattern' => 'Bob'], '"Alice"'],
[['text' => 'Bob', 'langcode' => 'en', 'replace' => 'Alice', 'replace_pattern' => 'Tom'], '"Bob"'],
[['text' => 'Äwesome', 'langcode' => 'en', 'lowercase' => TRUE], '"awesome"'],
[['text' => 'Äwesome', 'langcode' => 'de', 'lowercase' => TRUE], '"aewesome"'],
// Tests special characters replacement in the input text.
array(array('text' => 'B?!"@\/-ob@e', 'langcode' => 'en', 'lowercase' => TRUE, 'replace' => '_', 'replace_pattern' => '[^a-z0-9_.]+'), '"b_ob_e"'),
[['text' => 'B?!"@\/-ob@e', 'langcode' => 'en', 'lowercase' => TRUE, 'replace' => '_', 'replace_pattern' => '[^a-z0-9_.]+'], '"b_ob_e"'],
// Tests @ character in the replace_pattern regex.
array(array('text' => 'Bob@e\0', 'langcode' => 'en', 'lowercase' => TRUE, 'replace' => '_', 'replace_pattern' => '[^a-z0-9_.@]+'), '"bob@e_0"'),
[['text' => 'Bob@e\0', 'langcode' => 'en', 'lowercase' => TRUE, 'replace' => '_', 'replace_pattern' => '[^a-z0-9_.@]+'], '"bob@e_0"'],
// Tests null byte in the replace_pattern regex.
array(array('text' => 'Bob', 'langcode' => 'en', 'lowercase' => TRUE, 'replace' => 'fail()', 'replace_pattern' => ".*@e\0"), '"bob"'),
array(array('text' => 'Bob@e', 'langcode' => 'en', 'lowercase' => TRUE, 'replace' => 'fail()', 'replace_pattern' => ".*@e\0"), '"fail()"'),
);
[['text' => 'Bob', 'langcode' => 'en', 'lowercase' => TRUE, 'replace' => 'fail()', 'replace_pattern' => ".*@e\0"], '"bob"'],
[['text' => 'Bob@e', 'langcode' => 'en', 'lowercase' => TRUE, 'replace' => 'fail()', 'replace_pattern' => ".*@e\0"], '"fail()"'],
];
$valid_data = array_map(function ($data) {
if (isset($data[0]['replace_pattern'])) {