updated core

This commit is contained in:
Bachir Soussi Chiadmi
2018-01-24 13:36:32 +01:00
parent cd7dea45a9
commit f9374cf96d
1997 changed files with 5175 additions and 127715 deletions
@@ -24,7 +24,7 @@ class TimezoneController {
* Daylight saving time indicator. If abbr does not exist then the time
* zone is searched solely by offset and isdst.
*
* @return JsonResponse
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The timezone name in JsonResponse object.
*/
public function getTimezone($abbreviation = '', $offset = -1, $is_daylight_saving_time = NULL) {
@@ -68,7 +68,7 @@ class SystemConfigSubscriber implements EventSubscriberInterface {
* This event listener checks that the system.site:uuid's in the source and
* target match.
*
* @param ConfigImporterEvent $event
* @param \Drupal\Core\Config\ConfigImporterEvent $event
* The config import event.
*/
public function onConfigImporterValidateSiteUUID(ConfigImporterEvent $event) {
@@ -1,101 +0,0 @@
<?php
namespace Drupal\system\Tests\Action;
use Drupal\simpletest\KernelTestBase;
use Drupal\Core\Action\ActionInterface;
use Drupal\system\Entity\Action;
use Drupal\user\RoleInterface;
/**
* Tests action plugins.
*
* @group Action
*/
class ActionUnitTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('system', 'field', 'user', 'action_test');
/**
* The action manager.
*
* @var \Drupal\Core\Action\ActionManager
*/
protected $actionManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->actionManager = $this->container->get('plugin.manager.action');
$this->installEntitySchema('user');
$this->installSchema('system', array('sequences'));
}
/**
* Tests the functionality of test actions.
*/
public function testOperations() {
// Test that actions can be discovered.
$definitions = $this->actionManager->getDefinitions();
$this->assertTrue(count($definitions) > 1, 'Action definitions are found.');
$this->assertTrue(!empty($definitions['action_test_no_type']), 'The test action is among the definitions found.');
$definition = $this->actionManager->getDefinition('action_test_no_type');
$this->assertTrue(!empty($definition), 'The test action definition is found.');
$definitions = $this->actionManager->getDefinitionsByType('user');
$this->assertTrue(empty($definitions['action_test_no_type']), 'An action with no type is not found.');
// Create an instance of the 'save entity' action.
$action = $this->actionManager->createInstance('action_test_save_entity');
$this->assertTrue($action instanceof ActionInterface, 'The action implements the correct interface.');
// 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'));
$loaded_accounts = $user_storage->loadMultiple();
$this->assertEqual(count($loaded_accounts), 0);
// Execute the 'save entity' action.
$action->execute($account);
$loaded_accounts = $user_storage->loadMultiple();
$this->assertEqual(count($loaded_accounts), 1);
$account = reset($loaded_accounts);
$this->assertEqual($name, $account->label());
}
/**
* Tests the dependency calculation of actions.
*/
public function testDependencies() {
// Create a new action that depends on a user role.
$action = Action::create(array(
'id' => 'user_add_role_action.' . RoleInterface::ANONYMOUS_ID,
'type' => 'user',
'label' => t('Add the anonymous role to the selected users'),
'configuration' => array(
'rid' => RoleInterface::ANONYMOUS_ID,
),
'plugin' => 'user_add_role_action',
));
$action->save();
$expected = array(
'config' => array(
'user.role.' . RoleInterface::ANONYMOUS_ID,
),
'module' => array(
'user',
),
);
$this->assertIdentical($expected, $action->calculateDependencies()->getDependencies());
}
}
@@ -1,102 +0,0 @@
<?php
namespace Drupal\system\Tests\Ajax;
/**
* Performs tests on AJAX forms in cached pages.
*
* @group Ajax
*/
class AjaxFormPageCacheTest extends AjaxTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$config = $this->config('system.performance');
$config->set('cache.page.max_age', 300);
$config->save();
}
/**
* Return the build id of the current form.
*/
protected function getFormBuildId() {
$build_id_fields = $this->xpath('//input[@name="form_build_id"]');
$this->assertEqual(count($build_id_fields), 1, 'One form build id field on the page');
return (string) $build_id_fields[0]['value'];
}
/**
* Create a simple form, then submit the form via AJAX to change to it.
*/
public function testSimpleAJAXFormValue() {
$this->drupalGet('ajax_forms_test_get_form');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$build_id_initial = $this->getFormBuildId();
$edit = ['select' => 'green'];
$commands = $this->drupalPostAjaxForm(NULL, $edit, 'select');
$build_id_first_ajax = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
$expected = [
'command' => 'update_build_id',
'old' => $build_id_initial,
'new' => $build_id_first_ajax,
];
$this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');
$edit = ['select' => 'red'];
$commands = $this->drupalPostAjaxForm(NULL, $edit, 'select');
$build_id_second_ajax = $this->getFormBuildId();
$this->assertNotEqual($build_id_first_ajax, $build_id_second_ajax, 'Build id changes on subsequent AJAX submissions');
$expected = [
'command' => 'update_build_id',
'old' => $build_id_first_ajax,
'new' => $build_id_second_ajax,
];
$this->assertCommand($commands, $expected, 'Build id change command issued on subsequent AJAX submissions');
// Repeat the test sequence but this time with a page loaded from the cache.
$this->drupalGet('ajax_forms_test_get_form');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$build_id_from_cache_initial = $this->getFormBuildId();
$this->assertEqual($build_id_initial, $build_id_from_cache_initial, 'Build id is the same as on the first request');
$edit = ['select' => 'green'];
$commands = $this->drupalPostAjaxForm(NULL, $edit, 'select');
$build_id_from_cache_first_ajax = $this->getFormBuildId();
$this->assertNotEqual($build_id_from_cache_initial, $build_id_from_cache_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
$this->assertNotEqual($build_id_first_ajax, $build_id_from_cache_first_ajax, 'Build id from first user is not reused');
$expected = [
'command' => 'update_build_id',
'old' => $build_id_from_cache_initial,
'new' => $build_id_from_cache_first_ajax,
];
$this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');
$edit = ['select' => 'red'];
$commands = $this->drupalPostAjaxForm(NULL, $edit, 'select');
$build_id_from_cache_second_ajax = $this->getFormBuildId();
$this->assertNotEqual($build_id_from_cache_first_ajax, $build_id_from_cache_second_ajax, 'Build id changes on subsequent AJAX submissions');
$expected = [
'command' => 'update_build_id',
'old' => $build_id_from_cache_first_ajax,
'new' => $build_id_from_cache_second_ajax,
];
$this->assertCommand($commands, $expected, 'Build id change command issued on subsequent AJAX submissions');
}
/**
* Tests a form that uses an #ajax callback.
*
* @see \Drupal\system\Tests\Ajax\ElementValidationTest::testAjaxElementValidation()
*/
public function testAjaxElementValidation() {
$edit = ['drivertext' => t('some dumb text')];
$this->drupalPostAjaxForm('ajax_validation_test', $edit, 'drivertext');
}
}
@@ -1,296 +0,0 @@
<?php
namespace Drupal\system\Tests\Asset;
use Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException;
use Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the library discovery and library discovery parser.
*
* @group Render
*/
class LibraryDiscoveryIntegrationTest extends KernelTestBase {
/**
* The library discovery service.
*
* @var \Drupal\Core\Asset\LibraryDiscoveryInterface
*/
protected $libraryDiscovery;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container->get('theme_installer')->install(['test_theme', 'classy']);
$this->libraryDiscovery = $this->container->get('library.discovery');
}
/**
* Tests that hook_library_info is invoked and the cache is cleared.
*/
public function testHookLibraryInfoByTheme() {
// Activate test_theme and verify that the library 'kitten' is added using
// hook_library_info_alter().
$this->activateTheme('test_theme');
$this->assertTrue($this->libraryDiscovery->getLibraryByName('test_theme', 'kitten'));
// Now make classy the active theme and assert that library is not added.
$this->activateTheme('classy');
$this->assertFalse($this->libraryDiscovery->getLibraryByName('test_theme', 'kitten'));
}
/**
* Tests that libraries-override are applied to library definitions.
*/
public function testLibrariesOverride() {
// Assert some classy libraries that will be overridden or removed.
$this->activateTheme('classy');
$this->assertAssetInLibrary('core/themes/classy/css/components/button.css', 'classy', 'base', 'css');
$this->assertAssetInLibrary('core/themes/classy/css/components/collapse-processed.css', 'classy', 'base', 'css');
$this->assertAssetInLibrary('core/themes/classy/css/components/container-inline.css', 'classy', 'base', 'css');
$this->assertAssetInLibrary('core/themes/classy/css/components/details.css', 'classy', 'base', 'css');
$this->assertAssetInLibrary('core/themes/classy/css/components/dialog.css', 'classy', 'dialog', 'css');
// Confirmatory assert on core library to be removed.
$this->assertTrue($this->libraryDiscovery->getLibraryByName('core', 'drupal.progress'), 'Confirmatory test on "core/drupal.progress"');
// Activate test theme that defines libraries overrides.
$this->activateTheme('test_theme');
// Assert that entire library was correctly overridden.
$this->assertEqual($this->libraryDiscovery->getLibraryByName('core', 'drupal.collapse'), $this->libraryDiscovery->getLibraryByName('test_theme', 'collapse'), 'Entire library correctly overridden.');
// Assert that classy library assets were correctly overridden or removed.
$this->assertNoAssetInLibrary('core/themes/classy/css/components/button.css', 'classy', 'base', 'css');
$this->assertNoAssetInLibrary('core/themes/classy/css/components/collapse-processed.css', 'classy', 'base', 'css');
$this->assertNoAssetInLibrary('core/themes/classy/css/components/container-inline.css', 'classy', 'base', 'css');
$this->assertNoAssetInLibrary('core/themes/classy/css/components/details.css', 'classy', 'base', 'css');
$this->assertNoAssetInLibrary('core/themes/classy/css/components/dialog.css', 'classy', 'dialog', 'css');
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme/css/my-button.css', 'classy', 'base', 'css');
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme/css/my-collapse-processed.css', 'classy', 'base', 'css');
$this->assertAssetInLibrary('themes/my_theme/css/my-container-inline.css', 'classy', 'base', 'css');
$this->assertAssetInLibrary('themes/my_theme/css/my-details.css', 'classy', 'base', 'css');
// Assert that entire library was correctly removed.
$this->assertFalse($this->libraryDiscovery->getLibraryByName('core', 'drupal.progress'), 'Entire library correctly removed.');
// Assert that overridden library asset still retains attributes.
$library = $this->libraryDiscovery->getLibraryByName('core', 'jquery');
foreach ($library['js'] as $definition) {
if ($definition['data'] == 'core/modules/system/tests/themes/test_theme/js/collapse.js') {
$this->assertTrue($definition['minified'] && $definition['weight'] == -20, 'Previous attributes retained');
break;
}
}
}
/**
* Tests libraries-override on drupalSettings.
*/
public function testLibrariesOverrideDrupalSettings() {
// Activate test theme that attempts to override drupalSettings.
$this->activateTheme('test_theme_libraries_override_with_drupal_settings');
// Assert that drupalSettings cannot be overridden and throws an exception.
try {
$this->libraryDiscovery->getLibraryByName('core', 'drupal.ajax');
$this->fail('Throw Exception when trying to override drupalSettings');
}
catch (InvalidLibrariesOverrideSpecificationException $e) {
$expected_message = 'drupalSettings may not be overridden in libraries-override. Trying to override core/drupal.ajax/drupalSettings. Use hook_library_info_alter() instead.';
$this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when trying to override drupalSettings');
}
}
/**
* Tests libraries-override on malformed assets.
*/
public function testLibrariesOverrideMalformedAsset() {
// Activate test theme that overrides with a malformed asset.
$this->activateTheme('test_theme_libraries_override_with_invalid_asset');
// Assert that improperly formed asset "specs" throw an exception.
try {
$this->libraryDiscovery->getLibraryByName('core', 'drupal.dialog');
$this->fail('Throw Exception when specifying invalid override');
}
catch (InvalidLibrariesOverrideSpecificationException $e) {
$expected_message = 'Library asset core/drupal.dialog/css is not correctly specified. It should be in the form "extension/library_name/sub_key/path/to/asset.js".';
$this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying invalid override');
}
}
/**
* Tests library assets with other ways for specifying paths.
*/
public function testLibrariesOverrideOtherAssetLibraryNames() {
// Activate a test theme that defines libraries overrides on other types of
// assets.
$this->activateTheme('test_theme');
// Assert Drupal-relative paths.
$this->assertAssetInLibrary('themes/my_theme/css/dropbutton.css', 'core', 'drupal.dropbutton', 'css');
// Assert stream wrapper paths.
$this->assertAssetInLibrary('public://my_css/vertical-tabs.css', 'core', 'drupal.vertical-tabs', 'css');
// Assert a protocol-relative URI.
$this->assertAssetInLibrary('//my-server/my_theme/css/jquery_ui.css', 'core', 'jquery.ui', 'css');
// Assert an absolute URI.
$this->assertAssetInLibrary('http://example.com/my_theme/css/farbtastic.css', 'core', 'jquery.farbtastic', 'css');
}
/**
* Tests that base theme libraries-override still apply in sub themes.
*/
public function testBaseThemeLibrariesOverrideInSubTheme() {
// Activate a test theme that has subthemes.
$this->activateTheme('test_subtheme');
// Assert that libraries-override specified in the base theme still applies
// in the sub theme.
$this->assertNoAssetInLibrary('core/misc/dialog/dialog.js', 'core', 'drupal.dialog', 'js');
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/farbtastic.css', 'core', 'jquery.farbtastic', 'css');
}
/**
* Tests libraries-extend.
*/
public function testLibrariesExtend() {
// Activate classy themes and verify the libraries are not extended.
$this->activateTheme('classy');
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_1.css', 'classy', 'book-navigation', 'css');
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/js/extend_1.js', 'classy', 'book-navigation', 'js');
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_2.css', 'classy', 'book-navigation', 'css');
// Activate the theme that extends the book-navigation library in classy.
$this->activateTheme('test_theme_libraries_extend');
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_1.css', 'classy', 'book-navigation', 'css');
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/js/extend_1.js', 'classy', 'book-navigation', 'js');
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_2.css', 'classy', 'book-navigation', 'css');
// Activate a sub theme and confirm that it inherits the library assets
// extended in the base theme as well as its own.
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/base-libraries-extend.css', 'classy', 'base', 'css');
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_subtheme/css/sub-libraries-extend.css', 'classy', 'base', 'css');
$this->activateTheme('test_subtheme');
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/base-libraries-extend.css', 'classy', 'base', 'css');
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_subtheme/css/sub-libraries-extend.css', 'classy', 'base', 'css');
// Activate test theme that extends with a non-existent library. An
// exception should be thrown.
$this->activateTheme('test_theme_libraries_extend');
try {
$this->libraryDiscovery->getLibraryByName('core', 'drupal.dialog');
$this->fail('Throw Exception when specifying non-existent libraries-extend.');
}
catch (InvalidLibrariesExtendSpecificationException $e) {
$expected_message = 'The specified library "test_theme_libraries_extend/non_existent_library" does not exist.';
$this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying non-existent libraries-extend.');
}
// Also, test non-string libraries-extend. An exception should be thrown.
$this->container->get('theme_installer')->install(['test_theme']);
try {
$this->libraryDiscovery->getLibraryByName('test_theme', 'collapse');
$this->fail('Throw Exception when specifying non-string libraries-extend.');
}
catch (InvalidLibrariesExtendSpecificationException $e) {
$expected_message = 'The libraries-extend specification for each library must be a list of strings.';
$this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying non-string libraries-extend.');
}
}
/**
* Activates a specified theme.
*
* Installs the theme if not already installed and makes it the active theme.
*
* @param string $theme_name
* The name of the theme to be activated.
*/
protected function activateTheme($theme_name) {
$this->container->get('theme_installer')->install([$theme_name]);
/** @var \Drupal\Core\Theme\ThemeInitializationInterface $theme_initializer */
$theme_initializer = $this->container->get('theme.initialization');
/** @var \Drupal\Core\Theme\ThemeManagerInterface $theme_manager */
$theme_manager = $this->container->get('theme.manager');
$theme_manager->setActiveTheme($theme_initializer->getActiveThemeByName($theme_name));
$this->libraryDiscovery->clearCachedDefinitions();
// Assert message.
$this->pass(sprintf('Activated theme "%s"', $theme_name));
}
/**
* Asserts that the specified asset is in the given library.
*
* @param string $asset
* The asset file with the path for the file.
* @param string $extension
* The extension in which the $library is defined.
* @param string $library_name
* Name of the library.
* @param mixed $sub_key
* The library sub key where the given asset is defined.
* @param string $message
* (optional) A message to display with the assertion.
*
* @return bool
* TRUE if the specified asset is found in the library.
*/
protected function assertAssetInLibrary($asset, $extension, $library_name, $sub_key, $message = NULL) {
if (!isset($message)) {
$message = sprintf('Asset %s found in library "%s/%s"', $asset, $extension, $library_name);
}
$library = $this->libraryDiscovery->getLibraryByName($extension, $library_name);
foreach ($library[$sub_key] as $definition) {
if ($asset == $definition['data']) {
return $this->pass($message);
}
}
return $this->fail($message);
}
/**
* Asserts that the specified asset is not in the given library.
*
* @param string $asset
* The asset file with the path for the file.
* @param string $extension
* The extension in which the $library_name is defined.
* @param string $library_name
* Name of the library.
* @param mixed $sub_key
* The library sub key where the given asset is defined.
* @param string $message
* (optional) A message to display with the assertion.
*
* @return bool
* TRUE if the specified asset is not found in the library.
*/
protected function assertNoAssetInLibrary($asset, $extension, $library_name, $sub_key, $message = NULL) {
if (!isset($message)) {
$message = sprintf('Asset %s not found in library "%s/%s"', $asset, $extension, $library_name);
}
$library = $this->libraryDiscovery->getLibraryByName($extension, $library_name);
foreach ($library[$sub_key] as $definition) {
if ($asset == $definition['data']) {
return $this->fail($message);
}
}
return $this->pass($message);
}
}
@@ -1,195 +0,0 @@
<?php
namespace Drupal\system\Tests\Asset;
use Drupal\simpletest\KernelTestBase;
/**
* Tests that the asset files for all core libraries exist.
*
* This test also changes the active theme to each core theme to verify
* the libraries after theme-level libraries-override and libraries-extend are
* applied.
*
* @group Asset
*/
class ResolvedLibraryDefinitionsFilesMatchTest extends KernelTestBase {
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* The theme initialization.
*
* @var \Drupal\Core\Theme\ThemeInitializationInterface
*/
protected $themeInitialization;
/**
* The theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* The library discovery service.
*
* @var \Drupal\Core\Asset\LibraryDiscoveryInterface
*/
protected $libraryDiscovery;
/**
* A list of all core modules.
*
* @var string[]
*/
protected $allModules;
/**
* A list of all core themes.
*
* We hardcode this because test themes don't use a 'package' or 'hidden' key
* so we don't have a good way of filtering to only get "real" themes.
*
* @var string[]
*/
protected $allThemes = [
'bartik',
'classy',
'seven',
'stable',
'stark',
];
/**
* A list of libraries to skip checking, in the format extension/library_name.
*
* @var string[]
*/
protected $librariesToSkip = [
// Locale has a "dummy" library that does not actually exist.
'locale/translations',
];
/**
* A list of all paths that have been checked.
*
* @var array[]
*/
protected $pathsChecked;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->themeHandler = $this->container->get('theme_handler');
$this->themeInitialization = $this->container->get('theme.initialization');
$this->themeManager = $this->container->get('theme.manager');
$this->libraryDiscovery = $this->container->get('library.discovery');
// Install all core themes.
sort($this->allThemes);
$this->container->get('theme_installer')->install($this->allThemes);
// Enable all core modules.
$all_modules = system_rebuild_module_data();
$all_modules = array_filter($all_modules, function ($module) {
// Filter contrib, hidden, already enabled modules and modules in the
// Testing package.
if ($module->origin !== 'core' || !empty($module->info['hidden']) || $module->status == TRUE || $module->info['package'] == 'Testing') {
return FALSE;
}
return TRUE;
});
$this->allModules = array_keys($all_modules);
sort($this->allModules);
$this->enableModules($this->allModules);
}
/**
* Ensures that all core module and theme library files exist.
*/
public function testCoreLibraryCompleteness() {
// First verify all libraries with no active theme.
$this->verifyLibraryFilesExist($this->getAllLibraries());
// Then verify all libraries for each core theme. This may seem like
// overkill but themes can override and extend other extensions' libraries
// and these changes are only applied for the active theme.
foreach ($this->allThemes as $theme) {
$this->themeManager->setActiveTheme($this->themeInitialization->getActiveThemeByName($theme));
$this->libraryDiscovery->clearCachedDefinitions();
$this->verifyLibraryFilesExist($this->getAllLibraries());
}
}
/**
* Checks that all the library files exist.
*
* @param array[]
* An array of library definitions, keyed by extension, then by library, and
* so on.
*/
protected function verifyLibraryFilesExist($library_definitions) {
$root = \Drupal::root();
foreach ($library_definitions as $extension => $libraries) {
foreach ($libraries as $library_name => $library) {
if (in_array("$extension/$library_name", $this->librariesToSkip)) {
continue;
}
// Check that all the assets exist.
foreach (['css', 'js'] as $asset_type) {
foreach ($library[$asset_type] as $asset) {
$file = $asset['data'];
$path = $root . '/' . $file;
// Only check and assert each file path once.
if (!isset($this->pathsChecked[$path])) {
$this->assertTrue(is_file($path), "$file file referenced from the $extension/$library_name library exists.");
$this->pathsChecked[$path] = TRUE;
}
}
}
}
}
}
/**
* Gets all libraries for core and all installed modules.
*
* @return \Drupal\Core\Extension\Extension[]
*/
protected function getAllLibraries() {
$modules = \Drupal::moduleHandler()->getModuleList();
$extensions = $modules;
$module_list = array_keys($modules);
sort($module_list);
$this->assertEqual($this->allModules, $module_list, 'All core modules are installed.');
$themes = $this->themeHandler->listInfo();
$extensions += $themes;
$theme_list = array_keys($themes);
sort($theme_list);
$this->assertEqual($this->allThemes, $theme_list, 'All core themes are installed.');
$libraries['core'] = $this->libraryDiscovery->getLibrariesByExtension('core');
$root = \Drupal::root();
foreach ($extensions as $extension_name => $extension) {
$library_file = $extension->getPath() . '/' . $extension_name . '.libraries.yml';
if (is_file($root . '/' . $library_file)) {
$libraries[$extension_name] = $this->libraryDiscovery->getLibrariesByExtension($extension_name);
}
}
return $libraries;
}
}
@@ -1,74 +0,0 @@
<?php
namespace Drupal\system\Tests\Batch;
use Drupal\simpletest\WebTestBase;
/**
* Tests the content of the progress page.
*
* @group Batch
*/
class PageTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('batch_test');
/**
* Tests that the batch API progress page uses the correct theme.
*/
function testBatchProgressPageTheme() {
// Make sure that the page which starts the batch (an administrative page)
// is using a different theme than would normally be used by the batch API.
$this->container->get('theme_handler')->install(array('seven', 'bartik'));
$this->config('system.theme')
->set('default', 'bartik')
->set('admin', 'seven')
->save();
// Log in as an administrator who can see the administrative theme.
$admin_user = $this->drupalCreateUser(array('view the administration theme'));
$this->drupalLogin($admin_user);
// Visit an administrative page that runs a test batch, and check that the
// theme that was used during batch execution (which the batch callback
// function saved as a variable) matches the theme used on the
// administrative page.
$this->drupalGet('admin/batch-test/test-theme');
// The stack should contain the name of the theme used on the progress
// page.
$this->assertEqual(batch_test_stack(), array('seven'), 'A progressive batch correctly uses the theme of the page that started the batch.');
}
/**
* Tests that the batch API progress page shows the title correctly.
*/
function testBatchProgressPageTitle() {
// Visit an administrative page that runs a test batch, and check that the
// title shown during batch execution (which the batch callback function
// saved as a variable) matches the theme used on the administrative page.
$this->drupalGet('batch-test/test-title');
// The stack should contain the title shown on the progress page.
$this->assertEqual(batch_test_stack(), ['Batch Test'], 'The batch title is shown on the batch page.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
}
/**
* Tests that the progress messages are correct.
*/
function testBatchProgressMessages() {
// Go to the initial step only.
$this->maximumMetaRefreshCount = 0;
$this->drupalGet('batch-test/test-title');
$this->assertRaw('<div class="progress__description">Initializing.<br />&nbsp;</div>', 'Initial progress message appears correctly.');
$this->assertNoRaw('&amp;nbsp;', 'Initial progress message is not double escaped.');
// Now also go to the next step.
$this->maximumMetaRefreshCount = 1;
$this->drupalGet('batch-test/test-title');
$this->assertRaw('<div class="progress__description">Completed 1 of 1.</div>', 'Progress message for second step appears correctly.');
}
}
@@ -1,296 +0,0 @@
<?php
namespace Drupal\system\Tests\Batch;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Tests batch processing in form and non-form workflow.
*
* @group Batch
*/
class ProcessingTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('batch_test', 'test_page_test');
/**
* Tests batches triggered outside of form submission.
*/
function testBatchNoForm() {
// Displaying the page triggers batch 1.
$this->drupalGet('batch-test/no-form');
$this->assertBatchMessages($this->_resultMessages('batch_1'), 'Batch for step 2 performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), 'Execution order was correct.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
}
/**
* Tests batches that redirect in the batch finished callback.
*/
function testBatchRedirectFinishedCallback() {
// Displaying the page triggers batch 1.
$this->drupalGet('batch-test/finish-redirect');
$this->assertBatchMessages($this->_resultMessages('batch_1'), 'Batch for step 2 performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), 'Execution order was correct.');
$this->assertText('Test page text.', 'Custom redirection after batch execution displays the correct page.');
$this->assertUrl(Url::fromRoute('test_page_test.test_page'));
}
/**
* Tests batches defined in a form submit handler.
*/
function testBatchForm() {
// Batch 0: no operation.
$edit = array('batch' => 'batch_0');
$this->drupalPostForm('batch-test', $edit, 'Submit');
$this->assertNoEscaped('<', 'No escaped markup is present.');
$this->assertBatchMessages($this->_resultMessages('batch_0'), 'Batch with no operation performed successfully.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
// Batch 1: several simple operations.
$edit = array('batch' => 'batch_1');
$this->drupalPostForm('batch-test', $edit, 'Submit');
$this->assertNoEscaped('<', 'No escaped markup is present.');
$this->assertBatchMessages($this->_resultMessages('batch_1'), 'Batch with simple operations performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), 'Execution order was correct.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
// Batch 2: one multistep operation.
$edit = array('batch' => 'batch_2');
$this->drupalPostForm('batch-test', $edit, 'Submit');
$this->assertNoEscaped('<', 'No escaped markup is present.');
$this->assertBatchMessages($this->_resultMessages('batch_2'), 'Batch with multistep operation performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), 'Execution order was correct.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
// Batch 3: simple + multistep combined.
$edit = array('batch' => 'batch_3');
$this->drupalPostForm('batch-test', $edit, 'Submit');
$this->assertNoEscaped('<', 'No escaped markup is present.');
$this->assertBatchMessages($this->_resultMessages('batch_3'), 'Batch with simple and multistep operations performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_3'), 'Execution order was correct.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
// Batch 4: nested batch.
$edit = array('batch' => 'batch_4');
$this->drupalPostForm('batch-test', $edit, 'Submit');
$this->assertNoEscaped('<', 'No escaped markup is present.');
$this->assertBatchMessages($this->_resultMessages('batch_4'), 'Nested batch performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_4'), 'Execution order was correct.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
}
/**
* Tests batches defined in a multistep form.
*/
function testBatchFormMultistep() {
$this->drupalGet('batch-test/multistep');
$this->assertNoEscaped('<', 'No escaped markup is present.');
$this->assertText('step 1', 'Form is displayed in step 1.');
// First step triggers batch 1.
$this->drupalPostForm(NULL, array(), 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_1'), 'Batch for step 1 performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), 'Execution order was correct.');
$this->assertText('step 2', 'Form is displayed in step 2.');
$this->assertNoEscaped('<', 'No escaped markup is present.');
// Second step triggers batch 2.
$this->drupalPostForm(NULL, array(), 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_2'), 'Batch for step 2 performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), 'Execution order was correct.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
$this->assertNoEscaped('<', 'No escaped markup is present.');
}
/**
* Tests batches defined in different submit handlers on the same form.
*/
function testBatchFormMultipleBatches() {
// Batches 1, 2 and 3 are triggered in sequence by different submit
// handlers. Each submit handler modify the submitted 'value'.
$value = rand(0, 255);
$edit = array('value' => $value);
$this->drupalPostForm('batch-test/chained', $edit, 'Submit');
// Check that result messages are present and in the correct order.
$this->assertBatchMessages($this->_resultMessages('chained'), 'Batches defined in separate submit handlers performed successfully.');
// The stack contains execution order of batch callbacks and submit
// handlers and logging of corresponding $form_state->getValues().
$this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), 'Execution order was correct, and $form_state is correctly persisted.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
}
/**
* Tests batches defined in a programmatically submitted form.
*
* Same as above, but the form is submitted through drupal_form_execute().
*/
function testBatchFormProgrammatic() {
// Batches 1, 2 and 3 are triggered in sequence by different submit
// handlers. Each submit handler modify the submitted 'value'.
$value = rand(0, 255);
$this->drupalGet('batch-test/programmatic/' . $value);
// Check that result messages are present and in the correct order.
$this->assertBatchMessages($this->_resultMessages('chained'), 'Batches defined in separate submit handlers performed successfully.');
// The stack contains execution order of batch callbacks and submit
// handlers and logging of corresponding $form_state->getValues().
$this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), 'Execution order was correct, and $form_state is correctly persisted.');
$this->assertText('Got out of a programmatic batched form.', 'Page execution continues normally.');
}
/**
* Test form submission during a batch operation.
*/
function testDrupalFormSubmitInBatch() {
// Displaying the page triggers a batch that programmatically submits a
// form.
$value = rand(0, 255);
$this->drupalGet('batch-test/nested-programmatic/' . $value);
$this->assertEqual(batch_test_stack(), array('mock form submitted with value = ' . $value), '\Drupal::formBuilder()->submitForm() ran successfully within a batch operation.');
}
/**
* Tests batches that return $context['finished'] > 1 do in fact complete.
*
* @see https://www.drupal.org/node/600836
*/
function testBatchLargePercentage() {
// Displaying the page triggers batch 5.
$this->drupalGet('batch-test/large-percentage');
$this->assertBatchMessages($this->_resultMessages('batch_5'), 'Batch for step 2 performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_5'), 'Execution order was correct.');
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
}
/**
* Triggers a pass if the texts were found in order in the raw content.
*
* @param $texts
* Array of raw strings to look for .
* @param $message
* Message to display.
*
* @return
* TRUE on pass, FALSE on fail.
*/
function assertBatchMessages($texts, $message) {
$pattern = '|' . implode('.*', $texts) . '|s';
return $this->assertPattern($pattern, $message);
}
/**
* Returns expected execution stacks for the test batches.
*/
function _resultStack($id, $value = 0) {
$stack = array();
switch ($id) {
case 'batch_1':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
break;
case 'batch_2':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 2 id $i";
}
break;
case 'batch_3':
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 1 id $i";
}
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 2 id $i";
}
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 2 id $i";
}
break;
case 'batch_4':
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 1 id $i";
}
$stack[] = 'setting up batch 2';
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
$stack = array_merge($stack, $this->_resultStack('batch_2'));
break;
case 'batch_5':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 5 id $i";
}
break;
case 'chained':
$stack[] = 'submit handler 1';
$stack[] = 'value = ' . $value;
$stack = array_merge($stack, $this->_resultStack('batch_1'));
$stack[] = 'submit handler 2';
$stack[] = 'value = ' . ($value + 1);
$stack = array_merge($stack, $this->_resultStack('batch_2'));
$stack[] = 'submit handler 3';
$stack[] = 'value = ' . ($value + 2);
$stack[] = 'submit handler 4';
$stack[] = 'value = ' . ($value + 3);
$stack = array_merge($stack, $this->_resultStack('batch_3'));
break;
}
return $stack;
}
/**
* Returns expected result messages for the test batches.
*/
function _resultMessages($id) {
$messages = array();
switch ($id) {
case 'batch_0':
$messages[] = 'results for batch 0<div class="item-list"><ul><li>none</li></ul></div>';
break;
case 'batch_1':
$messages[] = 'results for batch 1<div class="item-list"><ul><li>op 1: processed 10 elements</li></ul></div>';
break;
case 'batch_2':
$messages[] = 'results for batch 2<div class="item-list"><ul><li>op 2: processed 10 elements</li></ul></div>';
break;
case 'batch_3':
$messages[] = 'results for batch 3<div class="item-list"><ul><li>op 1: processed 10 elements</li><li>op 2: processed 10 elements</li></ul></div>';
break;
case 'batch_4':
$messages[] = 'results for batch 4<div class="item-list"><ul><li>op 1: processed 10 elements</li></ul></div>';
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
break;
case 'batch_5':
$messages[] = 'results for batch 5<div class="item-list"><ul><li>op 5: processed 10 elements</li></ul></div>';
break;
case 'chained':
$messages = array_merge($messages, $this->_resultMessages('batch_1'));
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
$messages = array_merge($messages, $this->_resultMessages('batch_3'));
break;
}
return $messages;
}
}
@@ -1,311 +0,0 @@
<?php
namespace Drupal\system\Tests\Block;
use Drupal\system\Entity\Menu;
use Drupal\block\Entity\Block;
use Drupal\Core\Render\Element;
use Drupal\simpletest\KernelTestBase;
use Drupal\system\Tests\Routing\MockRouteProvider;
use Drupal\Tests\Core\Menu\MenuLinkMock;
use Drupal\user\Entity\User;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Tests \Drupal\system\Plugin\Block\SystemMenuBlock.
*
* @group Block
* @todo Expand test coverage to all SystemMenuBlock functionality, including
* block_menu_delete().
*
* @see \Drupal\system\Plugin\Derivative\SystemMenuBlock
* @see \Drupal\system\Plugin\Block\SystemMenuBlock
*/
class SystemMenuBlockTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array(
'system',
'block',
'menu_test',
'menu_link_content',
'field',
'user',
'link',
);
/**
* The block under test.
*
* @var \Drupal\system\Plugin\Block\SystemMenuBlock
*/
protected $block;
/**
* The menu for testing.
*
* @var \Drupal\system\MenuInterface
*/
protected $menu;
/**
* The menu link tree service.
*
* @var \Drupal\Core\Menu\MenuLinkTree
*/
protected $linkTree;
/**
* The menu link plugin manager service.
*
* @var \Drupal\Core\Menu\MenuLinkManagerInterface $menuLinkManager
*/
protected $menuLinkManager;
/**
* The block manager service.
*
* @var \Drupal\Core\block\BlockManagerInterface
*/
protected $blockManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'sequences');
$this->installEntitySchema('user');
$this->installEntitySchema('menu_link_content');
$account = User::create([
'name' => $this->randomMachineName(),
'status' => 1,
]);
$account->save();
$this->container->get('current_user')->setAccount($account);
$this->menuLinkManager = $this->container->get('plugin.manager.menu.link');
$this->linkTree = $this->container->get('menu.link_tree');
$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));
$mock_route_provider = new MockRouteProvider($routes);
$this->container->set('router.route_provider', $mock_route_provider);
// Add a new custom menu.
$menu_name = 'mock';
$label = $this->randomMachineName(16);
$this->menu = Menu::create(array(
'id' => $menu_name,
'label' => $label,
'description' => 'Description text',
));
$this->menu->save();
// This creates a tree with the following structure:
// - 1
// - 2
// - 3
// - 4
// - 5
// - 7
// - 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)),
);
foreach ($links as $instance) {
$this->menuLinkManager->addDefinition($instance->getPluginId(), $instance->getPluginDefinition());
}
}
/**
* Tests calculation of a system menu block's configuration dependencies.
*/
public function testSystemMenuBlockConfigDependencies() {
$block = Block::create(array(
'plugin' => 'system_menu_block:' . $this->menu->id(),
'region' => 'footer',
'id' => 'machinename',
'theme' => 'stark',
));
$dependencies = $block->calculateDependencies()->getDependencies();
$expected = array(
'config' => array(
'system.menu.' . $this->menu->id()
),
'module' => array(
'system'
),
'theme' => array(
'stark'
),
);
$this->assertIdentical($expected, $dependencies);
}
/**
* Tests the config start level and depth.
*/
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(
'region' => 'footer',
'id' => 'machinename',
'theme' => 'stark',
'level' => $level,
'depth' => $depth,
));
};
// All the different block instances we're going to test.
$blocks = [
'all' => $place_block(1, 0),
'level_1_only' => $place_block(1, 1),
'level_2_only' => $place_block(2, 1),
'level_3_only' => $place_block(3, 1),
'level_1_and_beyond' => $place_block(1, 0),
'level_2_and_beyond' => $place_block(2, 0),
'level_3_and_beyond' => $place_block(3, 0),
];
// Scenario 1: test all block instances when there's no active trail.
$no_active_trail_expectations = [];
$no_active_trail_expectations['all'] = [
'test.example1' => [],
'test.example2' => [],
'test.example5' => [
'test.example7' => [],
],
'test.example6' => [],
'test.example8' => [],
];
$no_active_trail_expectations['level_1_only'] = [
'test.example1' => [],
'test.example2' => [],
'test.example5' => [],
'test.example6' => [],
'test.example8' => [],
];
$no_active_trail_expectations['level_2_only'] = [
'test.example7' => [],
];
$no_active_trail_expectations['level_3_only'] = [];
$no_active_trail_expectations['level_1_and_beyond'] = $no_active_trail_expectations['all'];
$no_active_trail_expectations['level_2_and_beyond'] = $no_active_trail_expectations['level_2_only'];
$no_active_trail_expectations['level_3_and_beyond'] = [];
foreach ($blocks as $id => $block) {
$block_build = $block->build();
$items = isset($block_build['#items']) ? $block_build['#items'] : [];
$this->assertIdentical($no_active_trail_expectations[$id], $this->convertBuiltMenuToIdTree($items), format_string('Menu block %id with no active trail renders the expected tree.', ['%id' => $id]));
}
// Scenario 2: test all block instances when there's an active trail.
$route = $this->container->get('router.route_provider')->getRouteByName('example3');
$request = new Request();
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'example3');
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $route);
$this->container->get('request_stack')->push($request);
// \Drupal\Core\Menu\MenuActiveTrail uses the cache collector pattern, which
// includes static caching. Since this second scenario simulates a second
// request, we must also simulate it for the MenuActiveTrail service, by
// clearing the cache collector's static cache.
\Drupal::service('menu.active_trail')->clear();
$active_trail_expectations = [];
$active_trail_expectations['all'] = [
'test.example1' => [],
'test.example2' => [
'test.example3' => [
'test.example4' => [],
]
],
'test.example5' => [
'test.example7' => [],
],
'test.example6' => [],
'test.example8' => [],
];
$active_trail_expectations['level_1_only'] = [
'test.example1' => [],
'test.example2' => [],
'test.example5' => [],
'test.example6' => [],
'test.example8' => [],
];
$active_trail_expectations['level_2_only'] = [
'test.example3' => [],
'test.example7' => [],
];
$active_trail_expectations['level_3_only'] = [
'test.example4' => [],
];
$active_trail_expectations['level_1_and_beyond'] = $active_trail_expectations['all'];
$active_trail_expectations['level_2_and_beyond'] = [
'test.example3' => [
'test.example4' => [],
],
'test.example7' => [],
];
$active_trail_expectations['level_3_and_beyond'] = $active_trail_expectations['level_3_only'];
foreach ($blocks as $id => $block) {
$block_build = $block->build();
$items = isset($block_build['#items']) ? $block_build['#items'] : [];
$this->assertIdentical($active_trail_expectations[$id], $this->convertBuiltMenuToIdTree($items), format_string('Menu block %id with an active trail renders the expected tree.', ['%id' => $id]));
}
}
/**
* Helper method to allow for easy menu link tree structure assertions.
*
* Converts the result of MenuLinkTree::build() in a "menu link ID tree".
*
* @param array $build
* The return value of MenuLinkTree::build()
*
* @return array
* The "menu link ID tree" representation of the given render array.
*/
protected function convertBuiltMenuToIdTree(array $build) {
$level = [];
foreach (Element::children($build) as $id) {
$level[$id] = [];
if (isset($build[$id]['below'])) {
$level[$id] = $this->convertBuiltMenuToIdTree($build[$id]['below']);
}
}
return $level;
}
}
@@ -1,48 +0,0 @@
<?php
namespace Drupal\system\Tests\Bootstrap;
use Drupal\simpletest\WebTestBase;
/**
* Tests drupal_set_message() and related functions.
*
* @group Bootstrap
*/
class DrupalSetMessageTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system_test');
/**
* Tests drupal_set_message().
*/
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>.');
}
}
@@ -1,61 +0,0 @@
<?php
namespace Drupal\system\Tests\Bootstrap;
use Drupal\simpletest\KernelTestBase;
/**
* Tests that drupal_get_filename() works correctly.
*
* @group Bootstrap
*/
class GetFilenameUnitTest extends KernelTestBase {
/**
* Tests that drupal_get_filename() works when the file is not in database.
*/
function testDrupalGetFilename() {
// drupal_get_profile() is using obtaining the profile from state if the
// install_state global is not set.
global $install_state;
$install_state['parameters']['profile'] = 'testing';
// Rebuild system.module.files state data.
// @todo Remove as part of https://www.drupal.org/node/2186491
drupal_static_reset('system_rebuild_module_data');
system_rebuild_module_data();
// Retrieving the location of a module.
$this->assertIdentical(drupal_get_filename('module', 'system'), 'core/modules/system/system.info.yml');
// Retrieving the location of a theme.
\Drupal::service('theme_handler')->install(array('stark'));
$this->assertIdentical(drupal_get_filename('theme', 'stark'), 'core/themes/stark/stark.info.yml');
// Retrieving the location of a theme engine.
$this->assertIdentical(drupal_get_filename('theme_engine', 'twig'), 'core/themes/engines/twig/twig.info.yml');
// Retrieving the location of a profile. Profiles are a special case with
// a fixed location and naming.
$this->assertIdentical(drupal_get_filename('profile', 'testing'), 'core/profiles/testing/testing.info.yml');
// Generate a non-existing module name.
$non_existing_module = uniqid("", TRUE);
// Set a custom error handler so we can ignore the file not found error.
set_error_handler(function($severity, $message, $file, $line) {
// Skip error handling if this is a "file not found" error.
if (strstr($message, 'is missing from the file system:')) {
\Drupal::state()->set('get_filename_test_triggered_error', TRUE);
return;
}
throw new \ErrorException($message, 0, $severity, $file, $line);
});
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for an item that does not exist returns NULL.');
$this->assertTrue(\Drupal::state()->get('get_filename_test_triggered_error'), 'Searching for an item that does not exist triggers an error.');
// Restore the original error handler.
restore_error_handler();
}
}
@@ -1,41 +0,0 @@
<?php
namespace Drupal\system\Tests\Bootstrap;
use Drupal\simpletest\KernelTestBase;
/**
* Tests that drupal_static() and drupal_static_reset() work.
*
* @group Bootstrap
*/
class ResettableStaticUnitTest extends KernelTestBase {
/**
* Tests drupal_static() function.
*
* Tests that a variable reference returned by drupal_static() gets reset when
* drupal_static_reset() is called.
*/
function testDrupalStatic() {
$name = __CLASS__ . '_' . __METHOD__;
$var = &drupal_static($name, 'foo');
$this->assertEqual($var, 'foo', 'Variable returned by drupal_static() was set to its default.');
// Call the specific reset and the global reset each twice to ensure that
// multiple resets can be issued without odd side effects.
$var = 'bar';
drupal_static_reset($name);
$this->assertEqual($var, 'foo', 'Variable was reset after first invocation of name-specific reset.');
$var = 'bar';
drupal_static_reset($name);
$this->assertEqual($var, 'foo', 'Variable was reset after second invocation of name-specific reset.');
$var = 'bar';
drupal_static_reset();
$this->assertEqual($var, 'foo', 'Variable was reset after first invocation of global reset.');
$var = 'bar';
drupal_static_reset();
$this->assertEqual($var, 'foo', 'Variable was reset after second invocation of global reset.');
}
}
@@ -1,207 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\Apcu4Backend;
use Drupal\Core\Cache\ApcuBackend;
/**
* Tests the APCu cache backend.
*
* @group Cache
* @requires extension apcu
*/
class ApcuBackendUnitTest extends GenericCacheBackendUnitTestBase {
/**
* Get a list of failed requirements.
*
* This specifically bypasses checkRequirements because it fails tests. PHP 7
* does not have APCu and simpletest does not have a explicit "skip"
* functionality so to emulate it we override all test methods and explicitly
* pass when requirements are not met.
*
* @return array
*/
protected function getRequirements() {
$requirements = [];
if (!extension_loaded('apcu')) {
$requirements[] = 'APCu extension not found.';
}
else {
if (PHP_SAPI === 'cli' && !ini_get('apc.enable_cli')) {
$requirements[] = 'apc.enable_cli must be enabled to run this test.';
}
}
return $requirements;
}
/**
* Check if requirements fail.
*
* If the requirements fail the test method should return immediately instead
* of running any tests. Messages will be output to display why the test was
* skipped.
*/
protected function requirementsFail() {
$requirements = $this->getRequirements();
if (!empty($requirements)) {
foreach ($requirements as $message) {
$this->pass($message);
}
return TRUE;
}
return FALSE;
}
/**
* {@inheritdoc}
*/
protected function createCacheBackend($bin) {
if (version_compare(phpversion('apcu'), '5.0.0', '>=')) {
return new ApcuBackend($bin, $this->databasePrefix, \Drupal::service('cache_tags.invalidator.checksum'));
}
else {
return new Apcu4Backend($bin, $this->databasePrefix, \Drupal::service('cache_tags.invalidator.checksum'));
}
}
/**
* {@inheritdoc}
*/
protected function tearDown() {
foreach ($this->cachebackends as $bin => $cachebackend) {
$this->cachebackends[$bin]->removeBin();
}
parent::tearDown();
}
/**
* {@inheritdoc}
*/
public function testSetGet() {
if ($this->requirementsFail()) {
return;
}
parent::testSetGet();
// Make sure entries are permanent (i.e. no TTL).
$backend = $this->getCacheBackend($this->getTestBin());
$key = $backend->getApcuKey('TEST8');
if (class_exists('\APCUIterator')) {
$iterator = new \APCUIterator('/^' . $key . '/');
}
else {
$iterator = new \APCIterator('user', '/^' . $key . '/');
}
foreach ($iterator as $item) {
$this->assertEqual(0, $item['ttl']);
$found = TRUE;
}
$this->assertTrue($found);
}
/**
* {@inheritdoc}
*/
public function testDelete() {
if ($this->requirementsFail()) {
return;
}
parent::testDelete();
}
/**
* {@inheritdoc}
*/
public function testValueTypeIsKept() {
if ($this->requirementsFail()) {
return;
}
parent::testValueTypeIsKept();
}
/**
* {@inheritdoc}
*/
public function testGetMultiple() {
if ($this->requirementsFail()) {
return;
}
parent::testGetMultiple();
}
/**
* {@inheritdoc}
*/
public function testSetMultiple() {
if ($this->requirementsFail()) {
return;
}
parent::testSetMultiple();
}
/**
* {@inheritdoc}
*/
public function testDeleteMultiple() {
if ($this->requirementsFail()) {
return;
}
parent::testDeleteMultiple();
}
/**
* {@inheritdoc}
*/
public function testDeleteAll() {
if ($this->requirementsFail()) {
return;
}
parent::testDeleteAll();
}
/**
* {@inheritdoc}
*/
public function testInvalidate() {
if ($this->requirementsFail()) {
return;
}
parent::testInvalidate();
}
/**
* {@inheritdoc}
*/
public function testInvalidateTags() {
if ($this->requirementsFail()) {
return;
}
parent::testInvalidateTags();
}
/**
* {@inheritdoc}
*/
public function testInvalidateAll() {
if ($this->requirementsFail()) {
return;
}
parent::testInvalidateAll();
}
/**
* {@inheritdoc}
*/
public function testRemoveBin() {
if ($this->requirementsFail()) {
return;
}
parent::testRemoveBin();
}
}
@@ -1,29 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\BackendChain;
use Drupal\Core\Cache\MemoryBackend;
/**
* Unit test of the backend chain using the generic cache unit test base.
*
* @group Cache
*/
class BackendChainUnitTest extends GenericCacheBackendUnitTestBase {
protected function createCacheBackend($bin) {
$chain = new BackendChain($bin);
// We need to create some various backends in the chain.
$chain
->appendBackend(new MemoryBackend('foo'))
->prependBackend(new MemoryBackend('bar'))
->appendBackend(new MemoryBackend('baz'));
\Drupal::service('cache_tags.invalidator')->addInvalidator($chain);
return $chain;
}
}
@@ -1,119 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\simpletest\KernelTestBase;
use Drupal\simpletest\UserCreationTrait;
use Drupal\user\Entity\Role;
/**
* Tests the cache context optimization.
*
* @group Render
*/
class CacheContextOptimizationTest extends KernelTestBase {
use UserCreationTrait;
/**
* Modules to enable.
*
* @var string[]
*/
public static $modules = ['user', 'system'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
$this->installConfig(['user']);
$this->installSchema('system', ['sequences']);
}
/**
* Ensures that 'user.permissions' cache context is able to define cache tags.
*/
public function testUserPermissionCacheContextOptimization() {
$user1 = $this->createUser();
$this->assertEqual($user1->id(), 1);
$authenticated_user = $this->createUser(['administer permissions']);
$role = $authenticated_user->getRoles()[1];
$test_element = [
'#cache' => [
'keys' => ['test'],
'contexts' => ['user', 'user.permissions'],
],
];
\Drupal::service('account_switcher')->switchTo($authenticated_user);
$element = $test_element;
$element['#markup'] = 'content for authenticated users';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'content for authenticated users');
// Verify that the render caching is working so that other tests can be
// trusted.
$element = $test_element;
$element['#markup'] = 'this should not be visible';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'content for authenticated users');
// Even though the cache contexts have been optimized to only include 'user'
// cache context, the element should have been changed because
// 'user.permissions' cache context defined a cache tags for permission
// changes, which should have bubbled up for the element when it was
// optimized away.
Role::load($role)
->revokePermission('administer permissions')
->save();
$element = $test_element;
$element['#markup'] = 'this should be visible';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'this should be visible');
}
/**
* Ensures that 'user.roles' still works when it is optimized away.
*/
public function testUserRolesCacheContextOptimization() {
$root_user = $this->createUser();
$this->assertEqual($root_user->id(), 1);
$authenticated_user = $this->createUser(['administer permissions']);
$role = $authenticated_user->getRoles()[1];
$test_element = [
'#cache' => [
'keys' => ['test'],
'contexts' => ['user', 'user.roles'],
],
];
\Drupal::service('account_switcher')->switchTo($authenticated_user);
$element = $test_element;
$element['#markup'] = 'content for authenticated users';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'content for authenticated users');
// Verify that the render caching is working so that other tests can be
// trusted.
$element = $test_element;
$element['#markup'] = 'this should not be visible';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'content for authenticated users');
// Even though the cache contexts have been optimized to only include 'user'
// cache context, the element should have been changed because 'user.roles'
// cache context defined a cache tag for user entity changes, which should
// have bubbled up for the element when it was optimized away.
$authenticated_user->removeRole($role);
$authenticated_user->save();
$element = $test_element;
$element['#markup'] = 'this should be visible';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'this should be visible');
}
}
@@ -1,32 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\ChainedFastBackend;
use Drupal\Core\Cache\DatabaseBackend;
use Drupal\Core\Cache\PhpBackend;
/**
* Unit test of the fast chained backend using the generic cache unit test base.
*
* @group Cache
*/
class ChainedFastBackendUnitTest extends GenericCacheBackendUnitTestBase {
/**
* Creates a new instance of ChainedFastBackend.
*
* @return \Drupal\Core\Cache\ChainedFastBackend
* A new ChainedFastBackend object.
*/
protected function createCacheBackend($bin) {
$consistent_backend = new DatabaseBackend(\Drupal::service('database'), \Drupal::service('cache_tags.invalidator.checksum'), $bin);
$fast_backend = new PhpBackend($bin, \Drupal::service('cache_tags.invalidator.checksum'));
$backend = new ChainedFastBackend($consistent_backend, $fast_backend, $bin);
// Explicitly register the cache bin as it can not work through the
// cache bin list in the container.
\Drupal::service('cache_tags.invalidator')->addInvalidator($backend);
return $backend;
}
}
@@ -1,42 +0,0 @@
<?php
namespace Drupal\system\Tests\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().
*/
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.', array('@bin' => $bin)));
}
}
}
@@ -1,60 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\Cache;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\DependencyInjection\Reference;
/**
* Tests DatabaseBackend cache tag implementation.
*
* @group Cache
*/
class DatabaseBackendTagTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system');
/**
* {@inheritdoc}
*/
public function containerBuild(ContainerBuilder $container) {
parent::containerBuild($container);
// Change container to database cache backends.
$container
->register('cache_factory', 'Drupal\Core\Cache\CacheFactory')
->addArgument(new Reference('settings'))
->addMethodCall('setContainer', array(new Reference('service_container')));
}
public function testTagInvalidations() {
// Create cache entry in multiple bins.
$tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
$bins = array('data', 'bootstrap', 'render');
foreach ($bins as $bin) {
$bin = \Drupal::cache($bin);
$bin->set('test', 'value', Cache::PERMANENT, $tags);
$this->assertTrue($bin->get('test'), 'Cache item was set in bin.');
}
$invalidations_before = intval(db_select('cachetags')->fields('cachetags', array('invalidations'))->condition('tag', 'test_tag:2')->execute()->fetchField());
Cache::invalidateTags(array('test_tag:2'));
// Test that cache entry has been invalidated in multiple bins.
foreach ($bins as $bin) {
$bin = \Drupal::cache($bin);
$this->assertFalse($bin->get('test'), 'Tag invalidation affected item in bin.');
}
// Test that only one tag invalidation has occurred.
$invalidations_after = intval(db_select('cachetags')->fields('cachetags', array('invalidations'))->condition('tag', 'test_tag:2')->execute()->fetchField());
$this->assertEqual($invalidations_after, $invalidations_before + 1, 'Only one addition cache tag invalidation has occurred after invalidating a tag used in multiple bins.');
}
}
@@ -1,51 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\DatabaseBackend;
/**
* Unit test of the database backend using the generic cache unit test base.
*
* @group Cache
*/
class DatabaseBackendUnitTest extends GenericCacheBackendUnitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system');
/**
* Creates a new instance of DatabaseBackend.
*
* @return
* A new DatabaseBackend object.
*/
protected function createCacheBackend($bin) {
return new DatabaseBackend($this->container->get('database'), $this->container->get('cache_tags.invalidator.checksum'), $bin);
}
/**
* {@inheritdoc}
*/
public function testSetGet() {
parent::testSetGet();
$backend = $this->getCacheBackend();
// Set up a cache ID that is not ASCII and longer than 255 characters so we
// can test cache ID normalization.
$cid_long = str_repeat('愛€', 500);
$cached_value_long = $this->randomMachineName();
$backend->set($cid_long, $cached_value_long);
$this->assertIdentical($cached_value_long, $backend->get($cid_long)->data, "Backend contains the correct value for long, non-ASCII cache id.");
$cid_short = '愛1€';
$cached_value_short = $this->randomMachineName();
$backend->set($cid_short, $cached_value_short);
$this->assertIdentical($cached_value_short, $backend->get($cid_short)->data, "Backend contains the correct value for short, non-ASCII cache id.");
}
}
@@ -1,26 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\MemoryBackend;
/**
* Unit test of the memory cache backend using the generic cache unit test base.
*
* @group Cache
*/
class MemoryBackendUnitTest extends GenericCacheBackendUnitTestBase {
/**
* Creates a new instance of MemoryBackend.
*
* @return
* A new MemoryBackend object.
*/
protected function createCacheBackend($bin) {
$backend = new MemoryBackend($bin);
\Drupal::service('cache_tags.invalidator')->addInvalidator($backend);
return $backend;
}
}
@@ -1,25 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\PhpBackend;
/**
* Unit test of the PHP cache backend using the generic cache unit test base.
*
* @group Cache
*/
class PhpBackendUnitTest extends GenericCacheBackendUnitTestBase {
/**
* Creates a new instance of MemoryBackend.
*
* @return
* A new MemoryBackend object.
*/
protected function createCacheBackend($bin) {
$backend = new PhpBackend($bin, \Drupal::service('cache_tags.invalidator.checksum'));
return $backend;
}
}
@@ -1,66 +0,0 @@
<?php
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Tests the 'session.exists' cache context service.
*
* @group Cache
*/
class SessionExistsCacheContextTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['session_exists_cache_context_test'];
/**
* Tests \Drupal\Core\Cache\Context\SessionExistsCacheContext::getContext().
*/
public function testCacheContext() {
$this->dumpHeaders = TRUE;
// 1. No session (anonymous).
$this->assertSessionCookieOnClient(FALSE);
$this->drupalGet(Url::fromRoute('<front>'));
$this->assertSessionCookieOnClient(FALSE);
$this->assertRaw('Session does not exist!');
$this->assertRaw('[session.exists]=0');
// 2. Session (authenticated).
$this->assertSessionCookieOnClient(FALSE);
$this->drupalLogin($this->rootUser);
$this->assertSessionCookieOnClient(TRUE);
$this->assertRaw('Session exists!');
$this->assertRaw('[session.exists]=1');
$this->drupalLogout();
$this->assertSessionCookieOnClient(FALSE);
$this->assertRaw('Session does not exist!');
$this->assertRaw('[session.exists]=0');
// 3. Session (anonymous).
$this->assertSessionCookieOnClient(FALSE);
$this->drupalGet(Url::fromRoute('<front>', [], ['query' => ['trigger_session' => 1]]));
$this->assertSessionCookieOnClient(TRUE);
$this->assertRaw('Session does not exist!');
$this->assertRaw('[session.exists]=0');
$this->drupalGet(Url::fromRoute('<front>'));
$this->assertSessionCookieOnClient(TRUE);
$this->assertRaw('Session exists!');
$this->assertRaw('[session.exists]=1');
}
/**
* Asserts whether a session cookie is present on the client or not.
*/
public function assertSessionCookieOnClient($expected_present) {
$non_deleted_cookies = array_filter($this->cookies, function ($item) { return $item['value'] !== 'deleted'; });
$this->assertEqual($expected_present, isset($non_deleted_cookies[$this->getSessionName()]), 'Session cookie exists.');
}
}
@@ -1,477 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Asset\AttachedAssets;
use Drupal\simpletest\KernelTestBase;
/**
* Tests #attached assets: attached asset libraries and JavaScript settings.
*
* i.e. tests:
*
* @code
* $build['#attached']['library'] =
* $build['#attached']['drupalSettings'] =
* @endcode
*
* @group Common
* @group Asset
*/
class AttachedAssetsTest extends KernelTestBase {
/**
* The asset resolver service.
*
* @var \Drupal\Core\Asset\AssetResolverInterface
*/
protected $assetResolver;
/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* {@inheritdoc}
*/
public static $modules = array('language', 'simpletest', 'common_test', 'system');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container->get('router.builder')->rebuild();
$this->assetResolver = $this->container->get('asset.resolver');
$this->renderer = $this->container->get('renderer');
}
/**
* Tests that default CSS and JavaScript is empty.
*/
function testDefault() {
$assets = new AttachedAssets();
$this->assertEqual(array(), $this->assetResolver->getCssAssets($assets, FALSE), 'Default CSS is empty.');
list($js_assets_header, $js_assets_footer) = $this->assetResolver->getJsAssets($assets, FALSE);
$this->assertEqual(array(), $js_assets_header, 'Default header JavaScript is empty.');
$this->assertEqual(array(), $js_assets_footer, 'Default footer JavaScript is empty.');
}
/**
* Tests non-existing libraries.
*/
function testLibraryUnknown() {
$build['#attached']['library'][] = 'core/unknown';
$assets = AttachedAssets::createFromRenderArray($build);
$this->assertIdentical([], $this->assetResolver->getJsAssets($assets, FALSE)[0], 'Unknown library was not added to the page.');
}
/**
* Tests adding a CSS and a JavaScript file.
*/
function testAddFiles() {
$build['#attached']['library'][] = 'common_test/files';
$assets = AttachedAssets::createFromRenderArray($build);
$css = $this->assetResolver->getCssAssets($assets, FALSE);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/bar.css', $css), 'CSS files are correctly added.');
$this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/foo.js', $js), 'JavaScript files are correctly added.');
$css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_css = $this->renderer->renderPlain($css_render_array);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/bar.css')) . '?' . $query_string . '" media="all" />'), FALSE, 'Rendering an external CSS file.');
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/foo.js')) . '?' . $query_string . '"></script>'), FALSE, 'Rendering an external JavaScript file.');
}
/**
* Tests adding JavaScript settings.
*/
function testAddJsSettings() {
// Add a file in order to test default settings.
$build['#attached']['library'][] = 'core/drupalSettings';
$assets = AttachedAssets::createFromRenderArray($build);
$this->assertEqual([], $assets->getSettings(), 'JavaScript settings on $assets are empty.');
$javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$this->assertTrue(array_key_exists('currentPath', $javascript['drupalSettings']['data']['path']), 'The current path JavaScript setting is set correctly.');
$this->assertTrue(array_key_exists('currentPath', $assets->getSettings()['path']), 'JavaScript settings on $assets are resolved after retrieving JavaScript assets, and are equal to the returned JavaScript settings.');
$assets->setSettings(['drupal' => 'rocks', 'dries' => 280342800]);
$javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$this->assertEqual(280342800, $javascript['drupalSettings']['data']['dries'], 'JavaScript setting is set correctly.');
$this->assertEqual('rocks', $javascript['drupalSettings']['data']['drupal'], 'The other JavaScript setting is set correctly.');
}
/**
* Tests adding external CSS and JavaScript files.
*/
function testAddExternalFiles() {
$build['#attached']['library'][] = 'common_test/external';
$assets = AttachedAssets::createFromRenderArray($build);
$css = $this->assetResolver->getCssAssets($assets, FALSE);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$this->assertTrue(array_key_exists('http://example.com/stylesheet.css', $css), 'External CSS files are correctly added.');
$this->assertTrue(array_key_exists('http://example.com/script.js', $js), 'External JavaScript files are correctly added.');
$css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_css = $this->renderer->renderPlain($css_render_array);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="http://example.com/stylesheet.css" media="all" />'), FALSE, 'Rendering an external CSS file.');
$this->assertNotIdentical(strpos($rendered_js, '<script src="http://example.com/script.js"></script>'), FALSE, 'Rendering an external JavaScript file.');
}
/**
* Tests adding JavaScript files with additional attributes.
*/
function testAttributes() {
$build['#attached']['library'][] = 'common_test/js-attributes';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
$expected_2 = '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js')) . '?v=1" defer bar="foo"></script>';
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered external JavaScript with correct defer and random attributes.');
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered internal JavaScript with correct defer and random attributes.');
}
/**
* Tests that attributes are maintained when JS aggregation is enabled.
*/
function testAggregatedAttributes() {
$build['#attached']['library'][] = 'common_test/js-attributes';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, TRUE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
$expected_2 = '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js')) . '?v=1" defer bar="foo"></script>';
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered external JavaScript with correct defer and random attributes.');
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered internal JavaScript with correct defer and random attributes.');
}
/**
* Integration test for CSS/JS aggregation.
*/
function testAggregation() {
$build['#attached']['library'][] = 'core/drupal.timezone';
$build['#attached']['library'][] = 'core/drupal.vertical-tabs';
$assets = AttachedAssets::createFromRenderArray($build);
$this->assertEqual(1, count($this->assetResolver->getCssAssets($assets, TRUE)), 'There is a sole aggregated CSS asset.');
list($header_js, $footer_js) = $this->assetResolver->getJsAssets($assets, TRUE);
$this->assertEqual([], \Drupal::service('asset.js.collection_renderer')->render($header_js), 'There are 0 JavaScript assets in the header.');
$rendered_footer_js = \Drupal::service('asset.js.collection_renderer')->render($footer_js);
$this->assertEqual(2, count($rendered_footer_js), 'There are 2 JavaScript assets in the footer.');
$this->assertEqual('drupal-settings-json', $rendered_footer_js[0]['#attributes']['data-drupal-selector'], 'The first of the two JavaScript assets in the footer has drupal settings.');
$this->assertEqual(0, strpos($rendered_footer_js[1]['#attributes']['src'], base_path()), 'The second of the two JavaScript assets in the footer has the sole aggregated JavaScript asset.');
}
/**
* Tests JavaScript settings.
*/
function testSettings() {
$build = array();
$build['#attached']['library'][] = 'core/drupalSettings';
// Nonsensical value to verify if it's possible to override path settings.
$build['#attached']['drupalSettings']['path']['pathPrefix'] = 'yarhar';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
// Parse the generated drupalSettings <script> back to a PHP representation.
$startToken = '{';
$endToken = '}';
$start = strpos($rendered_js, $startToken);
$end = strrpos($rendered_js, $endToken);
$json = Unicode::substr($rendered_js, $start, $end - $start + 1);
$parsed_settings = Json::decode($json);
// Test whether the settings for core/drupalSettings are available.
$this->assertTrue(isset($parsed_settings['path']['baseUrl']), 'drupalSettings.path.baseUrl is present.');
$this->assertIdentical($parsed_settings['path']['pathPrefix'], 'yarhar', 'drupalSettings.path.pathPrefix is present and has the correct (overridden) value.');
$this->assertIdentical($parsed_settings['path']['currentPath'], '', 'drupalSettings.path.currentPath is present and has the correct value.');
$this->assertIdentical($parsed_settings['path']['currentPathIsAdmin'], FALSE, 'drupalSettings.path.currentPathIsAdmin is present and has the correct value.');
$this->assertIdentical($parsed_settings['path']['isFront'], FALSE, 'drupalSettings.path.isFront is present and has the correct value.');
$this->assertIdentical($parsed_settings['path']['currentLanguage'], 'en', 'drupalSettings.path.currentLanguage is present and has the correct value.');
// Tests whether altering JavaScript settings via hook_js_settings_alter()
// is working as expected.
// @see common_test_js_settings_alter()
$this->assertIdentical($parsed_settings['pluralDelimiter'], '☃');
$this->assertIdentical($parsed_settings['foo'], 'bar');
}
/**
* Tests JS assets depending on the 'core/<head>' virtual library.
*/
function testHeaderHTML() {
$build['#attached']['library'][] = 'common_test/js-header';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[0];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/header.js')) . '?' . $query_string . '"></script>'), FALSE, 'The JS asset in common_test/js-header appears in the header.');
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/misc/drupal.js'))), FALSE, 'The JS asset of the direct dependency (core/drupal) of common_test/js-header appears in the header.');
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/assets/vendor/domready/ready.min.js'))), FALSE, 'The JS asset of the indirect dependency (core/domready) of common_test/js-header appears in the header.');
}
/**
* Tests that for assets with cache = FALSE, Drupal sets preprocess = FALSE.
*/
function testNoCache() {
$build['#attached']['library'][] = 'common_test/no-cache';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$this->assertFalse($js['core/modules/system/tests/modules/common_test/nocache.js']['preprocess'], 'Setting cache to FALSE sets preprocess to FALSE when adding JavaScript.');
}
/**
* Tests adding JavaScript within conditional comments.
*
* @see \Drupal\Core\Render\Element\HtmlTag::preRenderConditionalComments()
*/
function testBrowserConditionalComments() {
$default_query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
$build['#attached']['library'][] = 'common_test/browsers';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/old-ie.js')) . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
$expected_2 = "<!--[if !IE]><!-->\n" . '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/no-ie.js')) . '?' . $default_query_string . '"></script>' . "\n<!--<![endif]-->";
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered JavaScript within downlevel-hidden conditional comments.');
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered JavaScript within downlevel-revealed conditional comments.');
}
/**
* Tests JavaScript versioning.
*/
function testVersionQueryString() {
$build['#attached']['library'][] = 'core/backbone';
$build['#attached']['library'][] = 'core/domready';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$this->assertTrue(strpos($rendered_js, 'core/assets/vendor/backbone/backbone-min.js?v=1.2.3') > 0 && strpos($rendered_js, 'core/assets/vendor/domready/ready.min.js?v=1.0.8') > 0, 'JavaScript version identifiers correctly appended to URLs');
}
/**
* Tests JavaScript and CSS asset ordering.
*/
function testRenderOrder() {
$build['#attached']['library'][] = 'common_test/order';
$assets = AttachedAssets::createFromRenderArray($build);
// Construct the expected result from the regex.
$expected_order_js = [
"-8_1",
"-8_2",
"-8_3",
"-8_4",
"-5_1", // The external script.
"-3_1",
"-3_2",
"0_1",
"0_2",
"0_3",
];
// Retrieve the rendered JavaScript and test against the regex.
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$matches = array();
if (preg_match_all('/weight_([-0-9]+_[0-9]+)/', $rendered_js, $matches)) {
$result = $matches[1];
}
else {
$result = array();
}
$this->assertIdentical($result, $expected_order_js, 'JavaScript is added in the expected weight order.');
// Construct the expected result from the regex.
$expected_order_css = [
// Base.
'base_weight_-101_1',
'base_weight_-8_1',
'layout_weight_-101_1',
'base_weight_0_1',
'base_weight_0_2',
// Layout.
'layout_weight_-8_1',
'component_weight_-101_1',
'layout_weight_0_1',
'layout_weight_0_2',
// Component.
'component_weight_-8_1',
'state_weight_-101_1',
'component_weight_0_1',
'component_weight_0_2',
// State.
'state_weight_-8_1',
'theme_weight_-101_1',
'state_weight_0_1',
'state_weight_0_2',
// Theme.
'theme_weight_-8_1',
'theme_weight_0_1',
'theme_weight_0_2',
];
// Retrieve the rendered CSS and test against the regex.
$css = $this->assetResolver->getCssAssets($assets, FALSE);
$css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
$rendered_css = $this->renderer->renderPlain($css_render_array);
$matches = array();
if (preg_match_all('/([a-z]+)_weight_([-0-9]+_[0-9]+)/', $rendered_css, $matches)) {
$result = $matches[0];
}
else {
$result = array();
}
$this->assertIdentical($result, $expected_order_css, 'CSS is added in the expected weight order.');
}
/**
* Tests rendering the JavaScript with a file's weight above jQuery's.
*/
function testRenderDifferentWeight() {
// If a library contains assets A and B, and A is listed first, then B can
// still make itself appear first by defining a lower weight.
$build['#attached']['library'][] = 'core/jquery';
$build['#attached']['library'][] = 'common_test/weight';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$this->assertTrue(strpos($rendered_js, 'lighter.css') < strpos($rendered_js, 'first.js'), 'Lighter CSS assets are rendered first.');
$this->assertTrue(strpos($rendered_js, 'lighter.js') < strpos($rendered_js, 'first.js'), 'Lighter JavaScript assets are rendered first.');
$this->assertTrue(strpos($rendered_js, 'before-jquery.js') < strpos($rendered_js, 'core/assets/vendor/jquery/jquery.min.js'), 'Rendering a JavaScript file above jQuery.');
}
/**
* Tests altering a JavaScript's weight via hook_js_alter().
*
* @see simpletest_js_alter()
*/
function testAlter() {
// Add both tableselect.js and simpletest.js.
$build['#attached']['library'][] = 'core/drupal.tableselect';
$build['#attached']['library'][] = 'simpletest/drupal.simpletest';
$assets = AttachedAssets::createFromRenderArray($build);
// Render the JavaScript, testing if simpletest.js was altered to be before
// tableselect.js. See simpletest_js_alter() to see where this alteration
// takes place.
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$this->assertTrue(strpos($rendered_js, 'simpletest.js') < strpos($rendered_js, 'core/misc/tableselect.js'), 'Altering JavaScript weight through the alter hook.');
}
/**
* Adds a JavaScript library to the page and alters it.
*
* @see common_test_library_info_alter()
*/
function testLibraryAlter() {
// Verify that common_test altered the title of Farbtastic.
/** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
$library_discovery = \Drupal::service('library.discovery');
$library = $library_discovery->getLibraryByName('core', 'jquery.farbtastic');
$this->assertEqual($library['version'], '0.0', 'Registered libraries were altered.');
// common_test_library_info_alter() also added a dependency on jQuery Form.
$build['#attached']['library'][] = 'core/jquery.farbtastic';
$assets = AttachedAssets::createFromRenderArray($build);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$this->assertTrue(strpos($rendered_js, 'core/assets/vendor/jquery-form/jquery.form.min.js'), 'Altered library dependencies are added to the page.');
}
/**
* Dynamically defines an asset library and alters it.
*/
function testDynamicLibrary() {
/** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
$library_discovery = \Drupal::service('library.discovery');
// Retrieve a dynamic library definition.
// @see common_test_library_info_build()
\Drupal::state()->set('common_test.library_info_build_test', TRUE);
$library_discovery->clearCachedDefinitions();
$dynamic_library = $library_discovery->getLibraryByName('common_test', 'dynamic_library');
$this->assertTrue(is_array($dynamic_library));
if ($this->assertTrue(isset($dynamic_library['version']))) {
$this->assertIdentical('1.0', $dynamic_library['version']);
}
// Make sure the dynamic library definition could be altered.
// @see common_test_library_info_alter()
if ($this->assertTrue(isset($dynamic_library['dependencies']))) {
$this->assertIdentical(['core/jquery'], $dynamic_library['dependencies']);
}
}
/**
* Tests that multiple modules can implement libraries with the same name.
*
* @see common_test.library.yml
*/
function testLibraryNameConflicts() {
/** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
$library_discovery = \Drupal::service('library.discovery');
$farbtastic = $library_discovery->getLibraryByName('common_test', 'jquery.farbtastic');
$this->assertEqual($farbtastic['version'], '0.1', 'Alternative libraries can be added to the page.');
}
/**
* Tests JavaScript files that have querystrings attached get added right.
*/
function testAddJsFileWithQueryString() {
$build['#attached']['library'][] = 'common_test/querystring';
$assets = AttachedAssets::createFromRenderArray($build);
$css = $this->assetResolver->getCssAssets($assets, FALSE);
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2', $css), 'CSS file with query string is correctly added.');
$this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2', $js), 'JavaScript file with query string is correctly added.');
$css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
$rendered_css = $this->renderer->renderPlain($css_render_array);
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . str_replace('&', '&amp;', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2'))) . '&amp;' . $query_string . '" media="all" />'), FALSE, 'CSS file with query string gets version query string correctly appended..');
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . str_replace('&', '&amp;', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2'))) . '&amp;' . $query_string . '"></script>'), FALSE, 'JavaScript file with query string gets version query string correctly appended.');
}
}
@@ -1,82 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\KernelTestBase;
/**
* Test page rendering hooks.
*
* @group system
*/
class PageRenderTest extends KernelTestBase {
/**
* Tests hook_page_attachments() exceptions.
*/
function testHookPageAttachmentsExceptions() {
$this->enableModules(['common_test', 'system']);
\Drupal::service('router.builder')->rebuild();
$this->assertPageRenderHookExceptions('common_test', 'hook_page_attachments');
}
/**
* Tests hook_page_attachments_alter() exceptions.
*/
function testHookPageAlter() {
$this->enableModules(['common_test', 'system']);
\Drupal::service('router.builder')->rebuild();
$this->assertPageRenderHookExceptions('common_test', 'hook_page_attachments_alter');
}
/**
* Asserts whether expected exceptions are thrown for invalid hook implementations.
*
* @param string $module
* The module whose invalid logic in its hooks to enable.
* @param string $hook
* The page render hook to assert expected exceptions for.
*/
function assertPageRenderHookExceptions($module, $hook) {
$html_renderer = \Drupal::getContainer()->get('main_content_renderer.html');
// Assert a valid hook implementation doesn't trigger an exception.
$page = [];
$html_renderer->invokePageAttachmentHooks($page);
// Assert that hooks can set cache tags.
$this->assertEqual($page['#cache']['tags'], ['example']);
$this->assertEqual($page['#cache']['contexts'], ['user.permissions']);
// Assert an invalid hook implementation doesn't trigger an exception.
\Drupal::state()->set($module . '.' . $hook . '.descendant_attached', TRUE);
$assertion = $hook . '() implementation that sets #attached on a descendant triggers an exception';
$page = [];
try {
$html_renderer->invokePageAttachmentHooks($page);
$this->error($assertion);
}
catch (\LogicException $e) {
$this->pass($assertion);
$this->assertEqual($e->getMessage(), 'Only #attached and #cache may be set in ' . $hook . '().');
}
\Drupal::state()->set('bc_test.' . $hook . '.descendant_attached', FALSE);
// Assert an invalid hook implementation doesn't trigger an exception.
\Drupal::state()->set('bc_test.' . $hook . '.render_array', TRUE);
$assertion = $hook . '() implementation that sets a child render array triggers an exception';
$page = [];
try {
$html_renderer->invokePageAttachmentHooks($page);
$this->error($assertion);
}
catch (\LogicException $e) {
$this->pass($assertion);
$this->assertEqual($e->getMessage(), 'Only #attached and #cache may be set in ' . $hook . '().');
}
\Drupal::state()->set($module . '.' . $hook . '.render_array', FALSE);
}
}
@@ -1,243 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Drupal\simpletest\KernelTestBase;
/**
* Tests the markup of core render element types passed to drupal_render().
*
* @group Common
*/
class RenderElementTypesTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'router_test');
protected function setUp() {
parent::setUp();
$this->installConfig(array('system'));
\Drupal::service('router.builder')->rebuild();
}
/**
* Asserts that an array of elements is rendered properly.
*
* @param array $elements
* The render element array to test.
* @param string $expected_html
* The expected markup.
* @param string $message
* Assertion message.
*/
protected function assertElements(array $elements, $expected_html, $message) {
$actual_html = (string) \Drupal::service('renderer')->renderRoot($elements);
$out = '<table><tr>';
$out .= '<td valign="top"><pre>' . Html::escape($expected_html) . '</pre></td>';
$out .= '<td valign="top"><pre>' . Html::escape($actual_html) . '</pre></td>';
$out .= '</tr></table>';
$this->verbose($out);
$this->assertIdentical($actual_html, $expected_html, Html::escape($message));
}
/**
* Tests system #type 'container'.
*/
function testContainer() {
// Basic container with no attributes.
$this->assertElements(array(
'#type' => 'container',
'#markup' => 'foo',
), "<div>foo</div>\n", "#type 'container' with no HTML attributes");
// Container with a class.
$this->assertElements(array(
'#type' => 'container',
'#markup' => 'foo',
'#attributes' => array(
'class' => array('bar'),
),
), '<div class="bar">foo</div>' . "\n", "#type 'container' with a class HTML attribute");
// Container with children.
$this->assertElements(array(
'#type' => 'container',
'child' => array(
'#markup' => 'foo',
),
), "<div>foo</div>\n", "#type 'container' with child elements");
}
/**
* Tests system #type 'html_tag'.
*/
function testHtmlTag() {
// Test void element.
$this->assertElements(array(
'#type' => 'html_tag',
'#tag' => 'meta',
'#value' => 'ignored',
'#attributes' => array(
'name' => 'description',
'content' => 'Drupal test',
),
), '<meta name="description" content="Drupal test" />' . "\n", "#type 'html_tag', void element renders properly");
// Test non-void element.
$this->assertElements(array(
'#type' => 'html_tag',
'#tag' => 'section',
'#value' => 'value',
'#attributes' => array(
'class' => array('unicorns'),
),
), '<section class="unicorns">value</section>' . "\n", "#type 'html_tag', non-void element renders properly");
// Test empty void element tag.
$this->assertElements(array(
'#type' => 'html_tag',
'#tag' => 'link',
), "<link />\n", "#type 'html_tag' empty void element renders properly");
// Test empty non-void element tag.
$this->assertElements(array(
'#type' => 'html_tag',
'#tag' => 'section',
), "<section></section>\n", "#type 'html_tag' empty non-void element renders properly");
}
/**
* Tests system #type 'more_link'.
*/
function testMoreLink() {
$elements = array(
array(
'name' => "#type 'more_link' anchor tag generation without extra classes",
'value' => array(
'#type' => 'more_link',
'#url' => Url::fromUri('https://www.drupal.org'),
),
'expected' => '//div[@class="more-link"]/a[@href="https://www.drupal.org" and text()="More"]',
),
array(
'name' => "#type 'more_link' anchor tag generation with different link text",
'value' => array(
'#type' => 'more_link',
'#url' => Url::fromUri('https://www.drupal.org'),
'#title' => 'More Titles',
),
'expected' => '//div[@class="more-link"]/a[@href="https://www.drupal.org" and text()="More Titles"]',
),
array(
'name' => "#type 'more_link' anchor tag generation with attributes on wrapper",
'value' => array(
'#type' => 'more_link',
'#url' => Url::fromUri('https://www.drupal.org'),
'#theme_wrappers' => array(
'container' => array(
'#attributes' => array(
'title' => 'description',
'class' => array('more-link', 'drupal', 'test'),
),
),
),
),
'expected' => '//div[@title="description" and contains(@class, "more-link") and contains(@class, "drupal") and contains(@class, "test")]/a[@href="https://www.drupal.org" and text()="More"]',
),
array(
'name' => "#type 'more_link' anchor tag with a relative path",
'value' => array(
'#type' => 'more_link',
'#url' => Url::fromRoute('router_test.1'),
),
'expected' => '//div[@class="more-link"]/a[@href="' . Url::fromRoute('router_test.1')->toString() . '" and text()="More"]',
),
array(
'name' => "#type 'more_link' anchor tag with a route",
'value' => array(
'#type' => 'more_link',
'#url' => Url::fromRoute('router_test.1'),
),
'expected' => '//div[@class="more-link"]/a[@href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" and text()="More"]',
),
array(
'name' => "#type 'more_link' anchor tag with an absolute path",
'value' => array(
'#type' => 'more_link',
'#url' => Url::fromRoute('system.admin_content'),
'#options' => array('absolute' => TRUE),
),
'expected' => '//div[@class="more-link"]/a[@href="' . Url::fromRoute('system.admin_content')->setAbsolute()->toString() . '" and text()="More"]',
),
array(
'name' => "#type 'more_link' anchor tag to the front page",
'value' => array(
'#type' => 'more_link',
'#url' => Url::fromRoute('<front>'),
),
'expected' => '//div[@class="more-link"]/a[@href="' . Url::fromRoute('<front>')->toString() . '" and text()="More"]',
),
);
foreach ($elements as $element) {
$xml = new \SimpleXMLElement(\Drupal::service('renderer')->renderRoot($element['value']));
$result = $xml->xpath($element['expected']);
$this->assertTrue($result, '"' . $element['name'] . '" input rendered correctly by drupal_render().');
}
}
/**
* Tests system #type 'system_compact_link'.
*/
function testSystemCompactLink() {
$elements = array(
array(
'name' => "#type 'system_compact_link' when admin compact mode is off",
'value' => array(
'#type' => 'system_compact_link',
),
'expected' => '//div[@class="compact-link"]/a[contains(@href, "admin/compact/on?") and text()="Hide descriptions"]',
),
array(
'name' => "#type 'system_compact_link' when adding extra attributes",
'value' => array(
'#type' => 'system_compact_link',
'#attributes' => array(
'class' => array('kittens-rule'),
),
),
'expected' => '//div[@class="compact-link"]/a[contains(@href, "admin/compact/on?") and @class="kittens-rule" and text()="Hide descriptions"]',
),
);
foreach ($elements as $element) {
$xml = new \SimpleXMLElement(\Drupal::service('renderer')->renderRoot($element['value']));
$result = $xml->xpath($element['expected']);
$this->assertTrue($result, '"' . $element['name'] . '" is rendered correctly by drupal_render().');
}
// Set admin compact mode on for additional tests.
\Drupal::request()->cookies->set('Drupal_visitor_admin_compact_mode', TRUE);
$element = array(
'name' => "#type 'system_compact_link' when admin compact mode is on",
'value' => array(
'#type' => 'system_compact_link',
),
'expected' => '//div[@class="compact-link"]/a[contains(@href, "admin/compact?") and text()="Show descriptions"]',
);
$xml = new \SimpleXMLElement(\Drupal::service('renderer')->renderRoot($element['value']));
$result = $xml->xpath($element['expected']);
$this->assertTrue($result, '"' . $element['name'] . '" is rendered correctly by drupal_render().');
}
}
@@ -1,63 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\KernelTestBase;
/**
* Performs functional tests on drupal_render().
*
* @group Common
*/
class RenderTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'common_test');
/**
* Tests theme preprocess functions being able to attach assets.
*/
function testDrupalRenderThemePreprocessAttached() {
\Drupal::state()->set('theme_preprocess_attached_test', TRUE);
$test_element = [
'#theme' => 'common_test_render_element',
'foo' => [
'#markup' => 'Kittens!',
],
];
\Drupal::service('renderer')->renderRoot($test_element);
$expected_attached = [
'library' => [
'test/generic_preprocess',
'test/specific_preprocess',
]
];
$this->assertEqual($expected_attached, $test_element['#attached'], 'All expected assets from theme preprocess hooks attached.');
\Drupal::state()->set('theme_preprocess_attached_test', FALSE);
}
/**
* Tests that we get an exception when we try to attach an illegal type.
*/
public function testProcessAttached() {
// Specify invalid attachments in a render array.
$build['#attached']['library'][] = 'core/drupal.states';
$build['#attached']['drupal_process_states'][] = [];
$renderer = $this->container->get('bare_html_page_renderer');
try {
$renderer->renderBarePage($build, '', 'maintenance_page');
$this->fail("Invalid #attachment 'drupal_process_states' allowed");
}
catch (\LogicException $e) {
$this->pass("Invalid #attachment 'drupal_process_states' not allowed");
}
}
}
@@ -1,69 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\Bytes;
use Drupal\simpletest\KernelTestBase;
/**
* Parse a predefined amount of bytes and compare the output with the expected
* value.
*
* @group Common
*/
class SizeUnitTest extends KernelTestBase {
protected $exactTestCases;
protected $roundedTestCases;
protected function setUp() {
parent::setUp();
$kb = Bytes::KILOBYTE;
$this->exactTestCases = array(
'1 byte' => 1,
'1 KB' => $kb,
'1 MB' => $kb * $kb,
'1 GB' => $kb * $kb * $kb,
'1 TB' => $kb * $kb * $kb * $kb,
'1 PB' => $kb * $kb * $kb * $kb * $kb,
'1 EB' => $kb * $kb * $kb * $kb * $kb * $kb,
'1 ZB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
'1 YB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
);
$this->roundedTestCases = array(
'2 bytes' => 2,
'1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!)
round(3623651 / ($this->exactTestCases['1 MB']), 2) . ' MB' => 3623651, // megabytes
round(67234178751368124 / ($this->exactTestCases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes
round(235346823821125814962843827 / ($this->exactTestCases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes
);
}
/**
* Checks that format_size() returns the expected string.
*/
function testCommonFormatSize() {
foreach (array($this->exactTestCases, $this->roundedTestCases) as $test_cases) {
foreach ($test_cases as $expected => $input) {
$this->assertEqual(
($result = format_size($input, NULL)),
$expected,
$expected . ' == ' . $result . ' (' . $input . ' bytes)'
);
}
}
}
/**
* Cross-tests Bytes::toInt() and format_size().
*/
function testCommonParseSizeFormatSize() {
foreach ($this->exactTestCases as $size) {
$this->assertEqual(
$size,
($parsed_size = Bytes::toInt($string = format_size($size, NULL))),
$size . ' == ' . $parsed_size . ' (' . $string . ')'
);
}
}
}
@@ -1,54 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\simpletest\KernelTestBase;
/**
* Tests scanning system directories in drupal_system_listing().
*
* @group Common
*/
class SystemListingTest extends KernelTestBase {
/**
* Tests that files in different directories take precedence as expected.
*/
function testDirectoryPrecedence() {
// Define the module files we will search for, and the directory precedence
// we expect.
$expected_directories = array(
// 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(
'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
// meaning, so assert their presence first.
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)));
}
}
// Now scan the directories and check that the files take precedence as
// expected.
$listing = new ExtensionDiscovery(\Drupal::root());
$listing->setProfileDirectories(array('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(
'@actual' => $files[$module]->getPathname(),
'@expected' => $expected_uri,
)));
}
}
}
@@ -1,141 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\Html;
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests table sorting.
*
* @group Common
*/
class TableSortExtenderUnitTest extends KernelTestBase {
/**
* Tests tablesort_init().
*/
function testTableSortInit() {
// Test simple table headers.
$headers = array('foo', 'bar', 'baz');
// Reset $request->query to prevent parameters from Simpletest and Batch API
// ending up in $ts['query'].
$expected_ts = array(
'name' => 'foo',
'sql' => '',
'sort' => 'asc',
'query' => array(),
);
$request = Request::createFromGlobals();
$request->query->replace(array());
\Drupal::getContainer()->get('request_stack')->push($request);
$ts = tablesort_init($headers);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Simple table headers sorted correctly.');
// Test with simple table headers plus $_GET parameters that should _not_
// override the default.
$request = Request::createFromGlobals();
$request->query->replace(array(
// This should not override the table order because only complex
// headers are overridable.
'order' => 'bar',
));
\Drupal::getContainer()->get('request_stack')->push($request);
$ts = tablesort_init($headers);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Simple table headers plus non-overriding $_GET parameters sorted correctly.');
// Test with simple table headers plus $_GET parameters that _should_
// override the default.
$request = Request::createFromGlobals();
$request->query->replace(array(
'sort' => 'DESC',
// Add an unrelated parameter to ensure that tablesort will include
// it in the links that it creates.
'alpha' => 'beta',
));
\Drupal::getContainer()->get('request_stack')->push($request);
$expected_ts['sort'] = 'desc';
$expected_ts['query'] = array('alpha' => 'beta');
$ts = tablesort_init($headers);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Simple table headers plus $_GET parameters sorted correctly.');
// Test complex table headers.
$headers = array(
'foo',
array(
'data' => '1',
'field' => 'one',
'sort' => 'asc',
'colspan' => 1,
),
array(
'data' => '2',
'field' => 'two',
'sort' => 'desc',
),
);
// Reset $_GET from previous assertion.
$request = Request::createFromGlobals();
$request->query->replace(array(
'order' => '2',
));
\Drupal::getContainer()->get('request_stack')->push($request);
$ts = tablesort_init($headers);
$expected_ts = array(
'name' => '2',
'sql' => 'two',
'sort' => 'desc',
'query' => array(),
);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Complex table headers sorted correctly.');
// Test complex table headers plus $_GET parameters that should _not_
// override the default.
$request = Request::createFromGlobals();
$request->query->replace(array(
// This should not override the table order because this header does not
// exist.
'order' => 'bar',
));
\Drupal::getContainer()->get('request_stack')->push($request);
$ts = tablesort_init($headers);
$expected_ts = array(
'name' => '1',
'sql' => 'one',
'sort' => 'asc',
'query' => array(),
);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Complex table headers plus non-overriding $_GET parameters sorted correctly.');
// Test complex table headers plus $_GET parameters that _should_
// override the default.
$request = Request::createFromGlobals();
$request->query->replace(array(
'order' => '1',
'sort' => 'ASC',
// Add an unrelated parameter to ensure that tablesort will include
// it in the links that it creates.
'alpha' => 'beta',
));
\Drupal::getContainer()->get('request_stack')->push($request);
$expected_ts = array(
'name' => '1',
'sql' => 'one',
'sort' => 'asc',
'query' => array('alpha' => 'beta'),
);
$ts = tablesort_init($headers);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Complex table headers plus $_GET parameters sorted correctly.');
}
}
@@ -1,57 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\UrlHelper;
use Drupal\simpletest\KernelTestBase;
/**
* Confirm that \Drupal\Component\Utility\Xss::filter() and check_url() work
* correctly, including invalid multi-byte sequences.
*
* @group Common
*/
class XssUnitTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('filter', 'system');
protected function setUp() {
parent::setUp();
$this->installConfig(array('system'));
}
/**
* Tests t() functionality.
*/
function testT() {
$text = t('Simple text');
$this->assertEqual($text, 'Simple text', 't leaves simple text alone.');
$text = t('Escaped text: @value', array('@value' => '<script>'));
$this->assertEqual($text, 'Escaped text: &lt;script&gt;', 't replaces and escapes string.');
$text = t('Placeholder text: %value', array('%value' => '<script>'));
$this->assertEqual($text, 'Placeholder text: <em class="placeholder">&lt;script&gt;</em>', 't replaces, escapes and themes string.');
}
/**
* Checks that harmful protocols are stripped.
*/
function testBadProtocolStripping() {
// Ensure that check_url() strips out harmful protocols, and encodes for
// HTML.
// Ensure \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() can
// be used to return a plain-text string stripped of harmful protocols.
$url = 'javascript:http://www.example.com/?x=1&y=2';
$expected_plain = 'http://www.example.com/?x=1&y=2';
$expected_html = 'http://www.example.com/?x=1&amp;y=2';
$this->assertIdentical(check_url($url), $expected_html, 'check_url() filters a URL and encodes it for HTML.');
$this->assertIdentical(UrlHelper::filterBadProtocol($url), $expected_html, '\Drupal\Component\Utility\UrlHelper::filterBadProtocol() filters a URL and encodes it for HTML.');
$this->assertIdentical(UrlHelper::stripDangerousProtocols($url), $expected_plain, '\Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() filters a URL and returns plain text.');
}
}
@@ -1,49 +0,0 @@
<?php
namespace Drupal\system\Tests\Condition;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\KernelTestBase;
/**
* Tests the CurrentThemeCondition plugin.
*
* @group Condition
*/
class CurrentThemeConditionTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('system', 'theme_test');
/**
* Tests the current theme condition.
*/
public function testCurrentTheme() {
\Drupal::service('theme_handler')->install(array('test_theme'));
$manager = \Drupal::service('plugin.manager.condition');
/** @var $condition \Drupal\Core\Condition\ConditionInterface */
$condition = $manager->createInstance('current_theme');
$condition->setConfiguration(array('theme' => 'test_theme'));
/** @var $condition_negated \Drupal\Core\Condition\ConditionInterface */
$condition_negated = $manager->createInstance('current_theme');
$condition_negated->setConfiguration(array('theme' => 'test_theme', 'negate' => TRUE));
$this->assertEqual($condition->summary(), SafeMarkup::format('The current theme is @theme', array('@theme' => 'test_theme')));
$this->assertEqual($condition_negated->summary(), SafeMarkup::format('The current theme is not @theme', array('@theme' => 'test_theme')));
// The expected theme has not been set up yet.
$this->assertFalse($condition->execute());
$this->assertTrue($condition_negated->execute());
// Set the expected theme to be used.
\Drupal::service('theme_handler')->setDefault('test_theme');
\Drupal::theme()->resetActiveTheme();
$this->assertTrue($condition->execute());
$this->assertFalse($condition_negated->execute());
}
}
@@ -1,168 +0,0 @@
<?php
namespace Drupal\system\Tests\Database;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests the pager query select extender.
*
* @group Database
*/
class SelectPagerDefaultTest extends DatabaseWebTestBase {
/**
* Confirms that a pager query returns the correct results.
*
* Note that we have to make an HTTP request to a test page handler
* because the pager depends on GET parameters.
*/
function testEvenPagerQuery() {
// To keep the test from being too brittle, we determine up front
// what the page count should be dynamically, and pass the control
// information forward to the actual query on the other side of the
// HTTP request.
$limit = 2;
$count = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
$correct_number = $limit;
$num_pages = floor($count / $limit);
// If there is no remainder from rounding, subtract 1 since we index from 0.
if (!($num_pages * $limit < $count)) {
$num_pages--;
}
for ($page = 0; $page <= $num_pages; ++$page) {
$this->drupalGet('database_test/pager_query_even/' . $limit, array('query' => array('page' => $page)));
$data = json_decode($this->getRawContent());
if ($page == $num_pages) {
$correct_number = $count - ($limit * $page);
}
$this->assertEqual(count($data->names), $correct_number, format_string('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
}
}
/**
* Confirms that a pager query returns the correct results.
*
* Note that we have to make an HTTP request to a test page handler
* because the pager depends on GET parameters.
*/
function testOddPagerQuery() {
// To keep the test from being too brittle, we determine up front
// what the page count should be dynamically, and pass the control
// information forward to the actual query on the other side of the
// HTTP request.
$limit = 2;
$count = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
$correct_number = $limit;
$num_pages = floor($count / $limit);
// If there is no remainder from rounding, subtract 1 since we index from 0.
if (!($num_pages * $limit < $count)) {
$num_pages--;
}
for ($page = 0; $page <= $num_pages; ++$page) {
$this->drupalGet('database_test/pager_query_odd/' . $limit, array('query' => array('page' => $page)));
$data = json_decode($this->getRawContent());
if ($page == $num_pages) {
$correct_number = $count - ($limit * $page);
}
$this->assertEqual(count($data->names), $correct_number, format_string('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
}
}
/**
* Confirms that a pager query results with an inner pager query are valid.
*
* This is a regression test for #467984.
*/
function testInnerPagerQuery() {
$query = db_select('test', 't')
->extend('Drupal\Core\Database\Query\PagerSelectExtender');
$query
->fields('t', array('age'))
->orderBy('age')
->limit(5);
$outer_query = db_select($query);
$outer_query->addField('subquery', 'age');
$ages = $outer_query
->execute()
->fetchCol();
$this->assertEqual($ages, array(25, 26, 27, 28), 'Inner pager query returned the correct ages.');
}
/**
* Confirms that a paging query results with a having expression are valid.
*
* This is a regression test for #467984.
*/
function testHavingPagerQuery() {
$query = db_select('test', 't')
->extend('Drupal\Core\Database\Query\PagerSelectExtender');
$query
->fields('t', array('name'))
->orderBy('name')
->groupBy('name')
->having('MAX(age) > :count', array(':count' => 26))
->limit(5);
$ages = $query
->execute()
->fetchCol();
$this->assertEqual($ages, array('George', 'Ringo'), 'Pager query with having expression returned the correct ages.');
}
/**
* Confirms that every pager gets a valid, non-overlapping element ID.
*/
function testElementNumbers() {
$request = Request::createFromGlobals();
$request->query->replace(array(
'page' => '3, 2, 1, 0',
));
\Drupal::getContainer()->get('request_stack')->push($request);
$name = db_select('test', 't')
->extend('Drupal\Core\Database\Query\PagerSelectExtender')
->element(2)
->fields('t', array('name'))
->orderBy('age')
->limit(1)
->execute()
->fetchField();
$this->assertEqual($name, 'Paul', 'Pager query #1 with a specified element ID returned the correct results.');
// Setting an element smaller than the previous one
// should not overwrite the pager $maxElement with a smaller value.
$name = db_select('test', 't')
->extend('Drupal\Core\Database\Query\PagerSelectExtender')
->element(1)
->fields('t', array('name'))
->orderBy('age')
->limit(1)
->execute()
->fetchField();
$this->assertEqual($name, 'George', 'Pager query #2 with a specified element ID returned the correct results.');
$name = db_select('test', 't')
->extend('Drupal\Core\Database\Query\PagerSelectExtender')
->fields('t', array('name'))
->orderBy('age')
->limit(1)
->execute()
->fetchField();
$this->assertEqual($name, 'John', 'Pager query #3 with a generated element ID returned the correct results.');
}
}
@@ -1,87 +0,0 @@
<?php
namespace Drupal\system\Tests\Database;
/**
* Tests the tablesort query extender.
*
* @group Database
*/
class SelectTableSortDefaultTest extends DatabaseWebTestBase {
/**
* Confirms that a tablesort query returns the correct results.
*
* Note that we have to make an HTTP request to a test page handler
* because the pager depends on GET parameters.
*/
function testTableSortQuery() {
$sorts = array(
array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
// more elements here
);
foreach ($sorts as $sort) {
$this->drupalGet('database_test/tablesort/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
$data = json_decode($this->getRawContent());
$first = array_shift($data->tasks);
$last = array_pop($data->tasks);
$this->assertEqual($first->task, $sort['first'], 'Items appear in the correct order.');
$this->assertEqual($last->task, $sort['last'], 'Items appear in the correct order.');
}
}
/**
* Confirms precedence of tablesorts headers.
*
* If a tablesort's orderByHeader is called before another orderBy, then its
* header happens first.
*/
function testTableSortQueryFirst() {
$sorts = array(
array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
// more elements here
);
foreach ($sorts as $sort) {
$this->drupalGet('database_test/tablesort_first/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
$data = json_decode($this->getRawContent());
$first = array_shift($data->tasks);
$last = array_pop($data->tasks);
$this->assertEqual($first->task, $sort['first'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
$this->assertEqual($last->task, $sort['last'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
}
}
/**
* Confirms that tableselect is rendered without error.
*
* Specifically that no sort is set in a tableselect, and that header links
* are correct.
*/
function testTableSortDefaultSort() {
$this->drupalGet('database_test/tablesort_default_sort');
// Verify that the table was displayed. Just the header is checked for
// because if there were any fatal errors or exceptions in displaying the
// sorted table, it would not print the table.
$this->assertText(t('Username'));
// Verify that the header links are built properly.
$this->assertLinkByHref('database_test/tablesort_default_sort');
$this->assertPattern('/\<a.*title\=\"' . t('sort by Username') . '\".*\>/');
}
}
@@ -1,57 +0,0 @@
<?php
namespace Drupal\system\Tests\Database;
/**
* Tests the temporary query functionality.
*
* @group Database
*/
class TemporaryQueryTest extends DatabaseWebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('database_test');
/**
* Returns the number of rows of a table.
*/
function countTableRows($table_name) {
return db_select($table_name)->countQuery()->execute()->fetchField();
}
/**
* Confirms that temporary tables work and are limited to one request.
*/
function testTemporaryQuery() {
$this->drupalGet('database_test/db_query_temporary');
$data = json_decode($this->getRawContent());
if ($data) {
$this->assertEqual($this->countTableRows('test'), $data->row_count, 'The temporary table contains the correct amount of rows.');
$this->assertFalse(db_table_exists($data->table_name), 'The temporary table is, indeed, temporary.');
}
else {
$this->fail('The creation of the temporary table failed.');
}
// Now try to run two db_query_temporary() in the same request.
$table_name_test = db_query_temporary('SELECT name FROM {test}', array());
$table_name_task = db_query_temporary('SELECT pid FROM {test_task}', array());
$this->assertEqual($this->countTableRows($table_name_test), $this->countTableRows('test'), 'A temporary table was created successfully in this request.');
$this->assertEqual($this->countTableRows($table_name_task), $this->countTableRows('test_task'), 'A second temporary table was created successfully in this request.');
// Check that leading whitespace and comments do not cause problems
// in the modified query.
$sql = "
-- Let's select some rows into a temporary table
SELECT name FROM {test}
";
$table_name_test = db_query_temporary($sql, array());
$this->assertEqual($this->countTableRows($table_name_test), $this->countTableRows('test'), 'Leading white space and comments do not interfere with temporary table creation.');
}
}
@@ -1,119 +0,0 @@
<?php
namespace Drupal\system\Tests\Datetime;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\simpletest\WebTestBase;
use Drupal\user\Entity\User;
/**
* Tests DrupalDateTime functionality.
*
* @group Datetime
*/
class DrupalDateTimeTest extends WebTestBase {
/**
* Set up required modules.
*/
public static $modules = array();
/**
* Test setup.
*/
protected function setUp() {
parent::setUp();
}
/**
* Test that the AJAX Timezone Callback can deal with various formats.
*/
public function testSystemTimezone() {
$options = array(
'query' => array(
'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(array());
$this->drupalLogin($test_user);
// Set up the user with a different timezone than the site.
$edit = array('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.
*/
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', array('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');
}
}
@@ -1,55 +0,0 @@
<?php
namespace Drupal\system\Tests\DrupalKernel;
use Drupal\simpletest\WebTestBase;
/**
* Ensures that the container rebuild works as expected.
*
* @group DrupalKernel
*/
class ContainerRebuildWebTest extends WebTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['service_provider_test'];
/**
* Sets a different deployment identifier.
*/
public function testSetContainerRebuildWithDifferentDeploymentIdentifier() {
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_indicator', FALSE);
$this->writeSettings(['settings' => ['deployment_identifier' => (object) ['value' => 'new-identifier', 'required' => TRUE]]]);
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_indicator', 'new-identifier');
}
/**
* Tests container invalidation.
*/
public function testContainerInvalidation() {
// Ensure that parameter is not set.
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', FALSE);
// Ensure that after setting the parameter, without a container rebuild the
// parameter is still not set.
$this->writeSettings(['settings' => ['container_rebuild_test_parameter' => (object) ['value' => 'rebuild_me_please', 'required' => TRUE]]]);
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', FALSE);
// Ensure that after container invalidation the parameter is set.
\Drupal::service('kernel')->invalidateContainer();
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', 'rebuild_me_please');
}
}
@@ -1,45 +0,0 @@
<?php
namespace Drupal\system\Tests\DrupalKernel;
use Drupal\simpletest\WebTestBase;
/**
* Tests content negotiation.
*
* @group DrupalKernel
*/
class ContentNegotiationTest extends WebTestBase {
/**
* 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
*/
function testBogusAcceptHeader() {
$tests = array(
// 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('', array(), array('Accept: ' . $header));
$this->assertNoText('Unsupported Media Type', '"Unsupported Media Type" not found for ' . $case);
$this->assertText(t('Log in'), '"Log in" found for ' . $case);
}
}
}
@@ -1,47 +0,0 @@
<?php
namespace Drupal\system\Tests\DrupalKernel;
use Drupal\Core\Site\Settings;
use Drupal\simpletest\KernelTestBase;
/**
* Tests site-specific service overrides.
*
* @group DrupalKernel
*/
class DrupalKernelSiteTest extends KernelTestBase {
/**
* Tests services.yml in site directory.
*/
public function testServicesYml() {
$container_yamls = Settings::get('container_yamls');
$container_yamls[] = $this->siteDirectory . '/services.yml';
$this->settingsSet('container_yamls', $container_yamls);
$this->assertFalse($this->container->has('site.service.yml'));
// A service provider class always has precedence over services.yml files.
// KernelTestBase::buildContainer() swaps out many services with in-memory
// implementations already, so those cannot be tested.
$this->assertIdentical(get_class($this->container->get('cache.backend.database')), 'Drupal\Core\Cache\DatabaseBackendFactory');
$class = __CLASS__;
$doc = <<<EOD
services:
# Add a new service.
site.service.yml:
class: $class
# Swap out a core service.
cache.backend.database:
class: Drupal\Core\Cache\MemoryBackendFactory
EOD;
file_put_contents($this->siteDirectory . '/services.yml', $doc);
// Rebuild the container.
$this->container->get('kernel')->rebuildContainer();
$this->assertTrue($this->container->has('site.service.yml'));
$this->assertIdentical(get_class($this->container->get('cache.backend.database')), 'Drupal\Core\Cache\MemoryBackendFactory');
}
}
@@ -1,58 +0,0 @@
<?php
namespace Drupal\system\Tests\DrupalKernel;
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\HttpFoundation\Response;
/**
* Tests that services are correctly destructed.
*
* @group DrupalKernel
*/
class ServiceDestructionTest extends KernelTestBase {
/**
* Verifies that services are destructed when used.
*/
public function testDestructionUsed() {
// Enable the test module to add it to the container.
$this->enableModules(array('service_provider_test'));
$request = $this->container->get('request_stack')->getCurrentRequest();
$kernel = $this->container->get('kernel');
$kernel->preHandle($request);
// The service has not been destructed yet.
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
// Call the class and then terminate the kernel
$this->container->get('service_provider_test_class');
$response = new Response();
$kernel->terminate($request, $response);
$this->assertTrue(\Drupal::state()->get('service_provider_test.destructed'));
}
/**
* Verifies that services are not unnecessarily destructed when not used.
*/
public function testDestructionUnused() {
// Enable the test module to add it to the container.
$this->enableModules(array('service_provider_test'));
$request = $this->container->get('request_stack')->getCurrentRequest();
$kernel = $this->container->get('kernel');
$kernel->preHandle($request);
// The service has not been destructed yet.
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
// Terminate the kernel. The test class has not been called, so it should not
// be destructed.
$response = new Response();
$kernel->terminate($request, $response);
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
}
}
@@ -1,205 +0,0 @@
<?php
namespace Drupal\system\Tests\Element;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\PathElement;
use Drupal\Core\Url;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
/**
* Tests PathElement validation and conversion functionality.
*
* @group Form
*/
class PathElementFormTest extends KernelTestBase implements FormInterface {
/**
* User for testing.
*
* @var \Drupal\user\UserInterface
*/
protected $testUser;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'user');
/**
* Sets up the test.
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['sequences', 'key_value_expire']);
$this->installEntitySchema('user');
\Drupal::service('router.builder')->rebuild();
/** @var \Drupal\user\RoleInterface $role */
$role = Role::create(array(
'id' => 'admin',
'label' => 'admin',
));
$role->grantPermission('link to any page');
$role->save();
$this->testUser = User::create(array(
'name' => 'foobar',
'mail' => 'foobar@example.com',
));
$this->testUser->addRole($role->id());
$this->testUser->save();
\Drupal::service('current_user')->setAccount($this->testUser);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'test_path_element';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// A required validated path.
$form['required_validate'] = array(
'#type' => 'path',
'#required' => TRUE,
'#title' => 'required_validate',
'#convert_path' => PathElement::CONVERT_NONE,
);
// A non validated required path.
$form['required_non_validate'] = array(
'#type' => 'path',
'#required' => TRUE,
'#title' => 'required_non_validate',
'#convert_path' => PathElement::CONVERT_NONE,
'#validate_path' => FALSE,
);
// A non required validated path.
$form['optional_validate'] = array(
'#type' => 'path',
'#required' => FALSE,
'#title' => 'optional_validate',
'#convert_path' => PathElement::CONVERT_NONE,
);
// A non required converted path.
$form['optional_validate'] = array(
'#type' => 'path',
'#required' => FALSE,
'#title' => 'optional_validate',
'#convert_path' => PathElement::CONVERT_ROUTE,
);
// A converted required validated path.
$form['required_validate_route'] = array(
'#type' => 'path',
'#required' => TRUE,
'#title' => 'required_validate_route',
);
// A converted required validated path.
$form['required_validate_url'] = array(
'#type' => 'path',
'#required' => TRUE,
'#title' => 'required_validate_url',
'#convert_path' => PathElement::CONVERT_URL,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {}
/**
* Form validation handler.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public function validateForm(array &$form, FormStateInterface $form_state) {}
/**
* Tests that default handlers are added even if custom are specified.
*/
public function testPathElement() {
$form_state = (new FormState())
->setValues([
'required_validate' => 'user/' . $this->testUser->id(),
'required_non_validate' => 'magic-ponies',
'required_validate_route' => 'user/' . $this->testUser->id(),
'required_validate_url' => 'user/' . $this->testUser->id(),
]);
$form_builder = $this->container->get('form_builder');
$form_builder->submitForm($this, $form_state);
// Valid form state.
$this->assertEqual(count($form_state->getErrors()), 0);
$this->assertEqual($form_state->getValue('required_validate_route'), array(
'route_name' => 'entity.user.canonical',
'route_parameters' => array(
'user' => $this->testUser->id(),
),
));
/** @var \Drupal\Core\Url $url */
$url = $form_state->getValue('required_validate_url');
$this->assertTrue($url instanceof Url);
$this->assertEqual($url->getRouteName(), 'entity.user.canonical');
$this->assertEqual($url->getRouteParameters(), array(
'user' => $this->testUser->id(),
));
// Test #required.
$form_state = (new FormState())
->setValues([
'required_non_validate' => 'magic-ponies',
'required_validate_route' => 'user/' . $this->testUser->id(),
'required_validate_url' => 'user/' . $this->testUser->id(),
]);
$form_builder->submitForm($this, $form_state);
$errors = $form_state->getErrors();
// Should be missing 'required_validate' field.
$this->assertEqual(count($errors), 1);
$this->assertEqual($errors, array('required_validate' => t('@name field is required.', array('@name' => 'required_validate'))));
// Test invalid parameters.
$form_state = (new FormState())
->setValues([
'required_validate' => 'user/74',
'required_non_validate' => 'magic-ponies',
'required_validate_route' => 'user/74',
'required_validate_url' => 'user/74',
]);
$form_builder = $this->container->get('form_builder');
$form_builder->submitForm($this, $form_state);
// Valid form state.
$errors = $form_state->getErrors();
$this->assertEqual(count($errors), 3);
$this->assertEqual($errors, array(
'required_validate' => t('This path does not exist or you do not have permission to link to %path.', array('%path' => 'user/74')),
'required_validate_route' => t('This path does not exist or you do not have permission to link to %path.', array('%path' => 'user/74')),
'required_validate_url' => t('This path does not exist or you do not have permission to link to %path.', array('%path' => 'user/74')),
));
}
}
@@ -1,233 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity;
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\search\Entity\SearchPage;
use Drupal\simpletest\WebTestBase;
use Drupal\system\Entity\Action;
/**
* Tests ConfigEntity importing.
*
* @group Entity
*/
class ConfigEntityImportTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('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(array(
'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', array(
'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 = entity_load('filter_format', '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);
}
}
@@ -1,150 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity;
use Drupal\entity_test\Entity\EntityTestBundle;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\entity_test\Entity\EntityTestWithBundle;
use Drupal\simpletest\WebTestBase;
/**
* Tests the /add and /add/{type} controllers.
*
* @group entity
*/
class EntityAddUITest extends WebTestBase {
/**
* {@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());
}
}
@@ -415,7 +415,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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');
@@ -434,7 +433,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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);
@@ -444,7 +442,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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');
@@ -461,7 +458,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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.
@@ -478,7 +474,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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');
@@ -492,7 +487,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
// 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
@@ -512,7 +506,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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
@@ -546,7 +539,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
}
}
if ($this->entity->getEntityType()->get('field_ui_base_route')) {
// Verify that after modifying a configurable field on the entity, there
// is a cache miss.
@@ -564,7 +556,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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');
@@ -582,7 +573,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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.
@@ -614,7 +604,6 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$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
@@ -1,77 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity;
use Drupal\Core\Language\LanguageInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\simpletest\WebTestBase;
/**
* Tests entity list builder functionality.
*
* @group Entity
*/
class EntityListBuilderTest extends WebTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('entity_test');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create and log in user.
$this->webUser = $this->drupalCreateUser(array(
'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(array('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');
}
}
@@ -1,56 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity;
use Drupal\simpletest\WebTestBase;
/**
* Tests that operations can be injected from the hook.
*
* @group Entity
*/
class EntityOperationsTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('entity_test');
protected function setUp() {
parent::setUp();
// Create and log in user.
$this->drupalLogin($this->drupalCreateUser(array('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', array('@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);
}
}
@@ -1,515 +0,0 @@
<?php
namespace Drupal\system\Tests\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\simpletest\WebTestBase;
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 WebTestBase {
use CommentTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'comment');
protected function setUp() {
parent::setUp();
// Create an Article node type.
$this->drupalCreateContentType(array('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(array($handler, 'getReferenceableEntities'), $arguments);
$this->assertEqual($result, $test['result'], format_string('Valid result set returned by @handler.', array('@handler' => $handler_name)));
$result = call_user_func_array(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.', array('@handler' => $handler_name)));
}
}
}
/**
* Test the node-specific overrides of the entity handler.
*/
public function testNodeHandler() {
$selection_options = array(
'target_type' => 'node',
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => NULL,
),
);
// Build a set of test data.
// Titles contain HTML-special characters to test escaping.
$node_values = array(
'published1' => array(
'type' => 'article',
'status' => NODE_PUBLISHED,
'title' => 'Node published1 (<&>)',
'uid' => 1,
),
'published2' => array(
'type' => 'article',
'status' => NODE_PUBLISHED,
'title' => 'Node published2 (<&>)',
'uid' => 1,
),
'unpublished' => array(
'type' => 'article',
'status' => NODE_NOT_PUBLISHED,
'title' => 'Node unpublished (<&>)',
'uid' => 1,
),
);
$nodes = array();
$node_labels = array();
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(array('access content'));
\Drupal::currentUser()->setAccount($normal_user);
$referenceable_tests = array(
array(
'arguments' => array(
array(NULL, 'CONTAINS'),
),
'result' => array(
'article' => array(
$nodes['published1']->id() => $node_labels['published1'],
$nodes['published2']->id() => $node_labels['published2'],
),
),
),
array(
'arguments' => array(
array('published1', 'CONTAINS'),
array('Published1', 'CONTAINS'),
),
'result' => array(
'article' => array(
$nodes['published1']->id() => $node_labels['published1'],
),
),
),
array(
'arguments' => array(
array('published2', 'CONTAINS'),
array('Published2', 'CONTAINS'),
),
'result' => array(
'article' => array(
$nodes['published2']->id() => $node_labels['published2'],
),
),
),
array(
'arguments' => array(
array('invalid node', 'CONTAINS'),
),
'result' => array(),
),
array(
'arguments' => array(
array('Node unpublished', 'CONTAINS'),
),
'result' => array(),
),
);
$this->assertReferenceable($selection_options, $referenceable_tests, 'Node handler');
// Test as an admin.
$admin_user = $this->drupalCreateUser(array('access content', 'bypass node access'));
\Drupal::currentUser()->setAccount($admin_user);
$referenceable_tests = array(
array(
'arguments' => array(
array(NULL, 'CONTAINS'),
),
'result' => array(
'article' => array(
$nodes['published1']->id() => $node_labels['published1'],
$nodes['published2']->id() => $node_labels['published2'],
$nodes['unpublished']->id() => $node_labels['unpublished'],
),
),
),
array(
'arguments' => array(
array('Node unpublished', 'CONTAINS'),
),
'result' => array(
'article' => array(
$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 = array(
'target_type' => 'user',
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => NULL,
'include_anonymous' => TRUE,
),
);
// Build a set of test data.
$user_values = array(
'anonymous' => User::load(0),
'admin' => User::load(1),
'non_admin' => array(
'name' => 'non_admin <&>',
'mail' => 'non_admin@example.com',
'roles' => array(),
'pass' => user_password(),
'status' => 1,
),
'blocked' => array(
'name' => 'blocked <&>',
'mail' => 'blocked@example.com',
'roles' => array(),
'pass' => user_password(),
'status' => 0,
),
);
$user_values['anonymous']->name = $this->config('user.settings')->get('anonymous');
$users = array();
$user_labels = array();
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 = array(
array(
'arguments' => array(
array(NULL, 'CONTAINS'),
),
'result' => array(
'user' => array(
$users['admin']->id() => $user_labels['admin'],
$users['non_admin']->id() => $user_labels['non_admin'],
),
),
),
array(
'arguments' => array(
array('non_admin', 'CONTAINS'),
array('NON_ADMIN', 'CONTAINS'),
),
'result' => array(
'user' => array(
$users['non_admin']->id() => $user_labels['non_admin'],
),
),
),
array(
'arguments' => array(
array('invalid user', 'CONTAINS'),
),
'result' => array(),
),
array(
'arguments' => array(
array('blocked', 'CONTAINS'),
),
'result' => array(),
),
);
$this->assertReferenceable($selection_options, $referenceable_tests, 'User handler');
\Drupal::currentUser()->setAccount($users['admin']);
$referenceable_tests = array(
array(
'arguments' => array(
array(NULL, 'CONTAINS'),
),
'result' => array(
'user' => array(
$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'],
),
),
),
array(
'arguments' => array(
array('blocked', 'CONTAINS'),
),
'result' => array(
'user' => array(
$users['blocked']->id() => $user_labels['blocked'],
),
),
),
array(
'arguments' => array(
array('Anonymous', 'CONTAINS'),
array('anonymous', 'CONTAINS'),
),
'result' => array(
'user' => array(
$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 = array(
array(
'arguments' => array(
array('Anonymous', 'CONTAINS'),
array('anonymous', 'CONTAINS'),
),
'result' => array(),
),
);
$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 = array(
array(
'arguments' => array(
array(NULL),
),
'result' => array(
'user' => array(
$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 = array(
'target_type' => 'comment',
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => NULL,
),
);
// Build a set of test data.
$node_values = array(
'published' => array(
'type' => 'article',
'status' => 1,
'title' => 'Node published',
'uid' => 1,
),
'unpublished' => array(
'type' => 'article',
'status' => 0,
'title' => 'Node unpublished',
'uid' => 1,
),
);
$nodes = array();
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 = array(
'published_published' => array(
'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' => array(
'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' => array(
'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 = array();
$comment_labels = array();
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(array('access content', 'access comments'));
\Drupal::currentUser()->setAccount($normal_user);
$referenceable_tests = array(
array(
'arguments' => array(
array(NULL, 'CONTAINS'),
),
'result' => array(
'comment' => array(
$comments['published_published']->cid->value => $comment_labels['published_published'],
),
),
),
array(
'arguments' => array(
array('Published', 'CONTAINS'),
),
'result' => array(
'comment' => array(
$comments['published_published']->cid->value => $comment_labels['published_published'],
),
),
),
array(
'arguments' => array(
array('invalid comment', 'CONTAINS'),
),
'result' => array(),
),
array(
'arguments' => array(
array('Comment Unpublished', 'CONTAINS'),
),
'result' => array(),
),
);
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler');
// Test as a comment admin.
$admin_user = $this->drupalCreateUser(array('access content', 'access comments', 'administer comments'));
\Drupal::currentUser()->setAccount($admin_user);
$referenceable_tests = array(
array(
'arguments' => array(
array(NULL, 'CONTAINS'),
),
'result' => array(
'comment' => array(
$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(array('access content', 'access comments', 'administer comments', 'bypass node access'));
\Drupal::currentUser()->setAccount($admin_user);
$referenceable_tests = array(
array(
'arguments' => array(
array(NULL, 'CONTAINS'),
),
'result' => array(
'comment' => array(
$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)');
}
}
@@ -1,158 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\simpletest\WebTestBase;
/**
* Create a entity with revisions and test viewing, saving, reverting, and
* deleting revisions.
*
* @group Entity
*/
class EntityRevisionsTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('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(array(
'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(array(
'name' => 'foo',
'user_id' => $this->webUser->id(),
));
$entity->field_test_text->value = 'bar';
$entity->save();
$names = array();
$texts = array();
$created = array();
$revision_ids = array();
// 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 = entity_load($entity_type, $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.', array('%entity_type' => $entity_type)));
$this->assertNotEqual($entity->name->value, $legacy_name, format_string('%entity_type: Name changed.', array('%entity_type' => $entity_type)));
$this->assertNotEqual($entity->field_test_text->value, $legacy_text, format_string('%entity_type: Text changed.', array('%entity_type' => $entity_type)));
}
for ($i = 0; $i < $revision_count; $i++) {
// Load specific revision.
$entity_revision = entity_revision_load($entity_type, $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.', array('%entity_type' => $entity_type)));
$this->assertEqual($entity_revision->name->value, $names[$i], format_string('%entity_type: Name matches.', array('%entity_type' => $entity_type)));
$this->assertEqual($entity_revision->field_test_text->value, $texts[$i], format_string('%entity_type: Text matches.', array('%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.', array('%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.', array('%entity_type' => $entity_type)));
}
// Confirm the correct revision text appears in the edit form.
$entity = entity_load($entity_type, $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.', array('%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.', array('%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');
}
}
@@ -1,149 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\simpletest\WebTestBase;
/**
* Tests EntityViewController functionality.
*
* @group Entity
*/
class EntityViewControllerTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('entity_test');
/**
* Array of test entities.
*
* @var array
*/
protected $entities = array();
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.
*/
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', array('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]', array(':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(array('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', array(
'properties' => array('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]', array(':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 = array(
'bundle' => $entity_type,
'name' => $this->randomMachineName(),
);
return $this->container->get('entity.manager')->getStorage($entity_type)->create($data);
}
}
@@ -34,7 +34,6 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
$view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTags();
$render_cache_tag = 'rendered';
$this->pass("Test entity.", 'Debug');
$this->verifyPageCache($entity_url, 'MISS');
@@ -65,7 +64,6 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
// Verify a cache hit.
$this->verifyPageCache($entity_url, 'HIT');
// Verify that after modifying the entity's display, there is a cache miss.
$this->pass("Test modification of entity's '$view_mode' display.", 'Debug');
$entity_display = entity_get_display($entity_type, $this->entity->bundle(), $view_mode);
@@ -75,7 +73,6 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
// Verify a cache hit.
$this->verifyPageCache($entity_url, 'HIT');
if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) {
// Verify that after modifying the corresponding bundle entity, there is a
// cache miss.
@@ -90,7 +87,6 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
$this->verifyPageCache($entity_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.
@@ -115,7 +111,6 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
$this->verifyPageCache($entity_url, 'HIT');
}
// Verify that after invalidating the entity's cache tag directly, there is
// a cache miss.
$this->pass("Test invalidation of entity's cache tag.", 'Debug');
@@ -125,7 +120,6 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
// Verify a cache hit.
$this->verifyPageCache($entity_url, 'HIT');
// Verify that after invalidating the generic entity type's view cache tag
// directly, there is a cache miss.
$this->pass("Test invalidation of entity's 'view' cache tag.", 'Debug');
@@ -135,7 +129,6 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
// Verify a cache hit.
$this->verifyPageCache($entity_url, 'HIT');
// Verify that after deleting the entity, there is a cache miss.
$this->pass('Test deletion of entity.', 'Debug');
$this->entity->delete();
@@ -1,21 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity\Update;
/**
* Runs LangcodeToAsciiUpdateTest with a dump filled with content.
*
* @group Entity
*/
class LangcodeToAsciiUpdateFilledTest extends LangcodeToAsciiUpdateTest {
/**
* {@inheritdoc}
*/
public function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../tests/fixtures/update/drupal-8.filled.standard.php.gz',
];
}
}
@@ -1,74 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity\Update;
use Drupal\Core\Database\Database;
use Drupal\system\Tests\Update\UpdatePathTestBase;
/**
* Tests that the entity langcode fields have been updated to varchar_ascii.
*
* @group Entity
*/
class LangcodeToAsciiUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
public function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
}
/**
* Tests that the column collation has been updated on MySQL.
*/
public function testLangcodeColumnCollation() {
// Only testable on MySQL.
// @see https://www.drupal.org/node/301038
if (Database::getConnection()->databaseType() !== 'mysql') {
$this->pass('This test can only run on MySQL');
return;
}
// Check a few different tables.
$tables = [
'node_field_data' => ['langcode'],
'users_field_data' => ['langcode', 'preferred_langcode', 'preferred_admin_langcode'],
];
foreach ($tables as $table => $columns) {
foreach ($columns as $column) {
$this->assertEqual('utf8mb4_general_ci', $this->getColumnCollation($table, $column), 'Found correct starting collation for ' . $table . '.' . $column);
}
}
// Apply updates.
$this->runUpdates();
foreach ($tables as $table => $columns) {
foreach ($columns as $column) {
$this->assertEqual('ascii_general_ci', $this->getColumnCollation($table, $column), 'Found correct updated collation for ' . $table . '.' . $column);
}
}
}
/**
* Determine the column collation.
*
* @param string $table
* The table name.
* @param string $column
* The column name.
*/
protected function getColumnCollation($table, $column) {
$query = Database::getConnection()->query("SHOW FULL COLUMNS FROM {" . $table . "}");
while ($row = $query->fetchAssoc()) {
if ($row['Field'] === $column) {
return $row['Collation'];
}
}
$this->fail('No collation found for ' . $table . '.' . $column);
}
}
@@ -1,20 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity\Update;
/**
* Runs SqlContentEntityStorageSchemaIndexTest with a dump filled with content.
*
* @group Entity
*/
class SqlContentEntityStorageSchemaIndexFilledTest extends SqlContentEntityStorageSchemaIndexTest {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
parent::setDatabaseDumpFiles();
$this->databaseDumpFiles[0] = __DIR__ . '/../../../../tests/fixtures/update/drupal-8.filled.standard.php.gz';
}
}
@@ -1,42 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity\Update;
use Drupal\system\Tests\Update\UpdatePathTestBase;
/**
* Tests that a newly-added index is properly created during database updates.
*
* @group Entity
*/
class SqlContentEntityStorageSchemaIndexTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
}
/**
* Tests entity and field schema database updates and execution order.
*/
public function testIndex() {
// The initial Drupal 8 database dump before any updates does not include
// the entity ID in the entity field data table indices that were added in
// https://www.drupal.org/node/2261669.
$this->assertTrue(db_index_exists('node_field_data', 'node__default_langcode'), 'Index node__default_langcode exists prior to running updates.');
$this->assertFalse(db_index_exists('node_field_data', 'node__id__default_langcode__langcode'), 'Index node__id__default_langcode__langcode does not exist prior to running updates.');
$this->assertFalse(db_index_exists('users_field_data', 'user__id__default_langcode__langcode'), 'Index users__id__default_langcode__langcode does not exist prior to running updates.');
// Running database updates should update the entity schemata to add the
// indices from https://www.drupal.org/node/2261669.
$this->runUpdates();
$this->assertFalse(db_index_exists('node_field_data', 'node__default_langcode'), 'Index node__default_langcode properly removed.');
$this->assertTrue(db_index_exists('node_field_data', 'node__id__default_langcode__langcode'), 'Index node__id__default_langcode__langcode properly created on the node_field_data table.');
$this->assertTrue(db_index_exists('users_field_data', 'user__id__default_langcode__langcode'), 'Index users__id__default_langcode__langcode properly created on the user_field_data table.');
}
}
@@ -1,209 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity\Update;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\simpletest\WebTestBase;
use Drupal\system\Tests\Update\DbUpdatesTrait;
/**
* Tests performing entity updates through the Update API.
*
* @group Entity
*/
class UpdateApiEntityDefinitionUpdateTest extends WebTestBase {
use DbUpdatesTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['entity_test'];
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The entity definition update manager.
*
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
*/
protected $updatesManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->entityManager = $this->container->get('entity.manager');
$this->updatesManager = $this->container->get('entity.definition_update_manager');
$admin = $this->drupalCreateUser([], FALSE, TRUE);
$this->drupalLogin($admin);
}
/**
* Tests that individual updates applied sequentially work as expected.
*/
public function testSingleUpdates() {
// Create a test entity.
$user_ids = [mt_rand(), mt_rand()];
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => $user_ids]);
$entity->save();
// Check that only a single value is stored for 'user_id'.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Make 'user_id' multiple by applying updates.
$this->enableUpdates('entity_test', 'entity_definition_updates', 8001);
$this->applyUpdates();
// Ensure the 'entity_test__user_id' table got created.
$this->assertTrue(\Drupal::database()->schema()->tableExists('entity_test__user_id'));
// Check that data was correctly migrated.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Store multiple data and check it is correctly stored.
$entity->user_id = $user_ids;
$entity->save();
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 2);
$this->assertEqual($entity->user_id[0]->target_id, $user_ids[0]);
$this->assertEqual($entity->user_id[1]->target_id, $user_ids[1]);
// Make 'user_id' single again by applying updates.
$this->enableUpdates('entity_test', 'entity_definition_updates', 8002);
$this->applyUpdates();
// Check that data was correctly migrated/dropped.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Check that only a single value is stored for 'user_id' again.
$entity->user_id = $user_ids;
$entity->save();
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id[0]->target_id, $user_ids[0]);
}
/**
* Tests that multiple updates applied in bulk work as expected.
*/
public function testMultipleUpdates() {
// Create a test entity.
$user_ids = [mt_rand(), mt_rand()];
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => $user_ids]);
$entity->save();
// Check that only a single value is stored for 'user_id'.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Make 'user_id' multiple and then single again by applying updates.
$this->enableUpdates('entity_test', 'entity_definition_updates', 8002);
$this->applyUpdates();
// Check that data was correctly migrated back and forth.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Check that only a single value is stored for 'user_id' again.
$entity->user_id = $user_ids;
$entity->save();
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id[0]->target_id, $user_ids[0]);
}
/**
* Tests that entity updates are correctly reported in the status report page.
*/
public function testStatusReport() {
// Create a test entity.
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => mt_rand()]);
$entity->save();
// Check that the status report initially displays no error.
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertNoRaw('Mismatched entity and/or field definitions');
// Enable an entity update and check that we have a dedicated status report
// item.
$this->container->get('state')->set('entity_test.remove_name_field', TRUE);
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertRaw('Mismatched entity and/or field definitions');
// Enable a db update and check that now the entity update status report
// item is no longer displayed. We assume an update function will fix the
// mismatch.
$this->enableUpdates('entity_test', 'status_report', 8001);
$this->drupalGet('admin/reports/status');
$this->assertRaw('Out of date');
$this->assertRaw('Mismatched entity and/or field definitions');
// Apply db updates and check that entity updates were not applied.
$this->applyUpdates();
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertRaw('Mismatched entity and/or field definitions');
// Check that en exception would be triggered when trying to apply them with
// existing data.
$message = 'Entity updates cannot run if entity data exists.';
try {
$this->updatesManager->applyUpdates();
$this->fail($message);
}
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
$this->pass($message);
}
// Check the status report is the same after trying to apply updates.
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertRaw('Mismatched entity and/or field definitions');
// Delete entity data, enable a new update, run updates again and check that
// entity updates were not applied even when no data exists.
$entity->delete();
$this->enableUpdates('entity_test', 'status_report', 8002);
$this->applyUpdates();
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertRaw('Mismatched entity and/or field definitions');
}
/**
* Reloads the specified entity.
*
* @param \Drupal\entity_test\Entity\EntityTest $entity
* An entity object.
*
* @return \Drupal\entity_test\Entity\EntityTest
* The reloaded entity object.
*/
protected function reloadEntity(EntityTest $entity) {
$this->entityManager->useCaches(FALSE);
$this->entityManager->getStorage('entity_test')->resetCache([$entity->id()]);
return EntityTest::load($entity->id());
}
}
@@ -1,395 +0,0 @@
<?php
namespace Drupal\system\Tests\Extension;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Extension\ExtensionNameLengthException;
use Drupal\simpletest\KernelTestBase;
/**
* Tests installing and uninstalling of themes.
*
* @group Extension
*/
class ThemeInstallerTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system');
public function containerBuild(ContainerBuilder $container) {
parent::containerBuild($container);
// Some test methods involve ModuleHandler operations, which attempt to
// rebuild and dump routes.
$container
->register('router.dumper', 'Drupal\Core\Routing\NullMatcherDumper');
}
protected function setUp() {
parent::setUp();
$this->installConfig(array('system'));
}
/**
* Verifies that no themes are installed by default.
*/
function testEmpty() {
$this->assertFalse($this->extensionConfig()->get('theme'));
$this->assertFalse(array_keys($this->themeHandler()->listInfo()));
$this->assertFalse(array_keys(system_list('theme')));
// Rebuilding available themes should always yield results though.
$this->assertTrue($this->themeHandler()->rebuildThemeData()['stark'], 'ThemeHandler::rebuildThemeData() yields all available themes.');
// theme_get_setting() should return global default theme settings.
$this->assertIdentical(theme_get_setting('features.favicon'), TRUE);
}
/**
* Tests installing a theme.
*/
function testInstall() {
$name = 'test_basetheme';
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(isset($themes[$name]));
$this->themeInstaller()->install(array($name));
$this->assertIdentical($this->extensionConfig()->get("theme.$name"), 0);
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]));
$this->assertEqual($themes[$name]->getName(), $name);
$this->assertEqual(array_keys(system_list('theme')), array_keys($themes));
// Verify that test_basetheme.settings is active.
$this->assertIdentical(theme_get_setting('features.favicon', $name), FALSE);
$this->assertEqual(theme_get_setting('base', $name), 'only');
$this->assertEqual(theme_get_setting('override', $name), 'base');
}
/**
* Tests installing a sub-theme.
*/
function testInstallSubTheme() {
$name = 'test_subtheme';
$base_name = 'test_basetheme';
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(array_keys($themes));
$this->themeInstaller()->install(array($name));
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]));
$this->assertTrue(isset($themes[$base_name]));
$this->themeInstaller()->uninstall(array($name));
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(isset($themes[$name]));
$this->assertTrue(isset($themes[$base_name]));
}
/**
* Tests installing a non-existing theme.
*/
function testInstallNonExisting() {
$name = 'non_existing_theme';
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(array_keys($themes));
try {
$message = 'ThemeHandler::install() throws InvalidArgumentException upon installing a non-existing theme.';
$this->themeInstaller()->install(array($name));
$this->fail($message);
}
catch (\InvalidArgumentException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(array_keys($themes));
}
/**
* Tests installing a theme with a too long name.
*/
function testInstallNameTooLong() {
$name = 'test_theme_having_veery_long_name_which_is_too_long';
try {
$message = 'ThemeHandler::install() throws ExtensionNameLengthException upon installing a theme with a too long name.';
$this->themeInstaller()->install(array($name));
$this->fail($message);
}
catch (ExtensionNameLengthException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
}
/**
* Tests uninstalling the default theme.
*/
function testUninstallDefault() {
$name = 'stark';
$other_name = 'bartik';
$this->themeInstaller()->install(array($name, $other_name));
$this->themeHandler()->setDefault($name);
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]));
$this->assertTrue(isset($themes[$other_name]));
try {
$message = 'ThemeHandler::uninstall() throws InvalidArgumentException upon disabling default theme.';
$this->themeHandler()->uninstall(array($name));
$this->fail($message);
}
catch (\InvalidArgumentException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]));
$this->assertTrue(isset($themes[$other_name]));
}
/**
* Tests uninstalling the admin theme.
*/
function testUninstallAdmin() {
$name = 'stark';
$other_name = 'bartik';
$this->themeInstaller()->install(array($name, $other_name));
$this->config('system.theme')->set('admin', $name)->save();
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]));
$this->assertTrue(isset($themes[$other_name]));
try {
$message = 'ThemeHandler::uninstall() throws InvalidArgumentException upon disabling admin theme.';
$this->themeHandler()->uninstall(array($name));
$this->fail($message);
}
catch (\InvalidArgumentException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]));
$this->assertTrue(isset($themes[$other_name]));
}
/**
* Tests uninstalling a sub-theme.
*/
function testUninstallSubTheme() {
$name = 'test_subtheme';
$base_name = 'test_basetheme';
$this->themeInstaller()->install(array($name));
$this->themeInstaller()->uninstall(array($name));
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(isset($themes[$name]));
$this->assertTrue(isset($themes[$base_name]));
}
/**
* Tests uninstalling a base theme before its sub-theme.
*/
function testUninstallBaseBeforeSubTheme() {
$name = 'test_basetheme';
$sub_name = 'test_subtheme';
$this->themeInstaller()->install(array($sub_name));
try {
$message = 'ThemeHandler::install() throws InvalidArgumentException upon uninstalling base theme before sub theme.';
$this->themeInstaller()->uninstall(array($name));
$this->fail($message);
}
catch (\InvalidArgumentException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]));
$this->assertTrue(isset($themes[$sub_name]));
// Verify that uninstalling both at the same time works.
$this->themeInstaller()->uninstall(array($name, $sub_name));
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(isset($themes[$name]));
$this->assertFalse(isset($themes[$sub_name]));
}
/**
* Tests uninstalling a non-existing theme.
*/
function testUninstallNonExisting() {
$name = 'non_existing_theme';
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(array_keys($themes));
try {
$message = 'ThemeHandler::uninstall() throws InvalidArgumentException upon uninstalling a non-existing theme.';
$this->themeInstaller()->uninstall(array($name));
$this->fail($message);
}
catch (\InvalidArgumentException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(array_keys($themes));
}
/**
* Tests uninstalling a theme.
*/
function testUninstall() {
$name = 'test_basetheme';
$this->themeInstaller()->install(array($name));
$this->assertTrue($this->config("$name.settings")->get());
$this->themeInstaller()->uninstall(array($name));
$this->assertFalse(array_keys($this->themeHandler()->listInfo()));
$this->assertFalse(array_keys(system_list('theme')));
$this->assertFalse($this->config("$name.settings")->get());
// Ensure that the uninstalled theme can be installed again.
$this->themeInstaller()->install(array($name));
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]));
$this->assertEqual($themes[$name]->getName(), $name);
$this->assertEqual(array_keys(system_list('theme')), array_keys($themes));
$this->assertTrue($this->config("$name.settings")->get());
}
/**
* Tests uninstalling a theme that is not installed.
*/
function testUninstallNotInstalled() {
$name = 'test_basetheme';
try {
$message = 'ThemeHandler::uninstall() throws InvalidArgumentException upon uninstalling a theme that is not installed.';
$this->themeInstaller()->uninstall(array($name));
$this->fail($message);
}
catch (\InvalidArgumentException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
}
/**
* Tests that theme info can be altered by a module.
*
* @see module_test_system_info_alter()
*/
function testThemeInfoAlter() {
$name = 'seven';
$this->container->get('state')->set('module_test.hook_system_info_alter', TRUE);
$this->themeInstaller()->install(array($name));
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(isset($themes[$name]->info['regions']['test_region']));
// Rebuild module data so we know where module_test is located.
// @todo Remove as part of https://www.drupal.org/node/2186491
system_rebuild_module_data();
$this->moduleInstaller()->install(array('module_test'), FALSE);
$this->assertTrue($this->moduleHandler()->moduleExists('module_test'));
$themes = $this->themeHandler()->listInfo();
$this->assertTrue(isset($themes[$name]->info['regions']['test_region']));
// Legacy assertions.
// @todo Remove once theme initialization/info has been modernized.
// @see https://www.drupal.org/node/2228093
$info = system_get_info('theme', $name);
$this->assertTrue(isset($info['regions']['test_region']));
$regions = system_region_list($name);
$this->assertTrue(isset($regions['test_region']));
$system_list = system_list('theme');
$this->assertTrue(isset($system_list[$name]->info['regions']['test_region']));
$this->moduleInstaller()->uninstall(array('module_test'));
$this->assertFalse($this->moduleHandler()->moduleExists('module_test'));
$themes = $this->themeHandler()->listInfo();
$this->assertFalse(isset($themes[$name]->info['regions']['test_region']));
// Legacy assertions.
// @todo Remove once theme initialization/info has been modernized.
// @see https://www.drupal.org/node/2228093
$info = system_get_info('theme', $name);
$this->assertFalse(isset($info['regions']['test_region']));
$regions = system_region_list($name);
$this->assertFalse(isset($regions['test_region']));
$system_list = system_list('theme');
$this->assertFalse(isset($system_list[$name]->info['regions']['test_region']));
}
/**
* Returns the theme handler service.
*
* @return \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected function themeHandler() {
return $this->container->get('theme_handler');
}
/**
* Returns the theme installer service.
*
* @return \Drupal\Core\Extension\ThemeInstallerInterface
*/
protected function themeInstaller() {
return $this->container->get('theme_installer');
}
/**
* Returns the system.theme config object.
*
* @return \Drupal\Core\Config\Config
*/
protected function extensionConfig() {
return $this->config('core.extension');
}
/**
* Returns the ModuleHandler.
*
* @return \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected function moduleHandler() {
return $this->container->get('module_handler');
}
/**
* Returns the ModuleInstaller.
*
* @return \Drupal\Core\Extension\ModuleInstallerInterface
*/
protected function moduleInstaller() {
return $this->container->get('module_installer');
}
}
@@ -1,32 +0,0 @@
<?php
namespace Drupal\system\Tests\Extension;
use Drupal\simpletest\KernelTestBase;
use Drupal\Core\Updater\Updater;
/**
* Tests InfoParser class and exception.
*
* Files for this test are stored in core/modules/system/tests/fixtures and end
* with .info.txt instead of info.yml in order not not be considered as real
* extensions.
*
* @group Extension
*/
class UpdaterTest extends KernelTestBase {
/**
* Tests project and child project showing correct title.
*
* @see https://drupal.org/node/2409515
*/
public function testGetProjectTitleWithChild() {
// Get the project title from it's directory. If it can't find the title
// it will choose the first project title in the directory.
$directory = \Drupal::root() . '/core/modules/system/tests/modules/module_handler_test_multiple';
$title = Updater::getProjectTitle($directory);
$this->assertEqual('module handler test multiple', $title);
}
}
@@ -1,58 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\simpletest\WebTestBase;
/**
* Tests file system configuration operations.
*
* @group File
*/
class ConfigTest extends WebTestBase {
protected function setUp(){
parent::setUp();
$this->drupalLogin ($this->drupalCreateUser(array('administer site configuration')));
}
/**
* Tests file configuration page.
*/
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 = array(
'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) array(
'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.');
}
}
@@ -1,163 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\Component\PhpStorage\FileStorage;
/**
* Tests operations dealing with directories.
*
* @group File
*/
class DirectoryTest extends FileTestBase {
/**
* Test local directory handling functions.
*/
function testFileCheckLocalDirectoryHandling() {
$site_path = $this->container->get('site.path');
$directory = $site_path . '/files';
// Check a new recursively created local directory for correct file system
// permissions.
$parent = $this->randomMachineName();
$child = $this->randomMachineName();
// Files directory already exists.
$this->assertTrue(is_dir($directory), t('Files directory already exists.'), 'File');
// Make files directory writable only.
$old_mode = fileperms($directory);
// Create the directories.
$parent_path = $directory . DIRECTORY_SEPARATOR . $parent;
$child_path = $parent_path . DIRECTORY_SEPARATOR . $child;
$this->assertTrue(drupal_mkdir($child_path, 0775, TRUE), t('No error reported when creating new local directories.'), 'File');
// Ensure new directories also exist.
$this->assertTrue(is_dir($parent_path), t('New parent directory actually exists.'), 'File');
$this->assertTrue(is_dir($child_path), t('New child directory actually exists.'), 'File');
// Check that new directory permissions were set properly.
$this->assertDirectoryPermissions($parent_path, 0775);
$this->assertDirectoryPermissions($child_path, 0775);
// Check that existing directory permissions were not modified.
$this->assertDirectoryPermissions($directory, $old_mode);
// Check creating a directory using an absolute path.
$absolute_path = drupal_realpath($directory) . DIRECTORY_SEPARATOR . $this->randomMachineName() . DIRECTORY_SEPARATOR . $this->randomMachineName();
$this->assertTrue(drupal_mkdir($absolute_path, 0775, TRUE), 'No error reported when creating new absolute directories.', 'File');
$this->assertDirectoryPermissions($absolute_path, 0775);
}
/**
* Test directory handling functions.
*/
function testFileCheckDirectoryHandling() {
// A directory to operate on.
$directory = file_default_scheme() . '://' . $this->randomMachineName() . '/' . $this->randomMachineName();
$this->assertFalse(is_dir($directory), 'Directory does not exist prior to testing.');
// Non-existent directory.
$this->assertFalse(file_prepare_directory($directory, 0), 'Error reported for non-existing directory.', 'File');
// Make a directory.
$this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), 'No error reported when creating a new directory.', 'File');
// Make sure directory actually exists.
$this->assertTrue(is_dir($directory), 'Directory actually exists.', 'File');
if (substr(PHP_OS, 0, 3) != 'WIN') {
// PHP on Windows doesn't support any kind of useful read-only mode for
// directories. When executing a chmod() on a directory, PHP only sets the
// read-only flag, which doesn't prevent files to actually be written
// in the directory on any recent version of Windows.
// Make directory read only.
@drupal_chmod($directory, 0444);
$this->assertFalse(file_prepare_directory($directory, 0), 'Error reported for a non-writeable directory.', 'File');
// Test directory permission modification.
$this->settingsSet('file_chmod_directory', 0777);
$this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), 'No error reported when making directory writeable.', 'File');
}
// Test that the directory has the correct permissions.
$this->assertDirectoryPermissions($directory, 0777, 'file_chmod_directory setting is respected.');
// Remove .htaccess file to then test that it gets re-created.
@drupal_unlink(file_default_scheme() . '://.htaccess');
$this->assertFalse(is_file(file_default_scheme() . '://.htaccess'), 'Successfully removed the .htaccess file in the files directory.', 'File');
file_ensure_htaccess();
$this->assertTrue(is_file(file_default_scheme() . '://.htaccess'), 'Successfully re-created the .htaccess file in the files directory.', 'File');
// Verify contents of .htaccess file.
$file = file_get_contents(file_default_scheme() . '://.htaccess');
$this->assertEqual($file, FileStorage::htaccessLines(FALSE), 'The .htaccess file contains the proper content.', 'File');
}
/**
* This will take a directory and path, and find a valid filepath that is not
* taken by another file.
*/
function testFileCreateNewFilepath() {
// First we test against an imaginary file that does not exist in a
// directory.
$basename = 'xyz.txt';
$directory = 'core/misc';
$original = $directory . '/' . $basename;
$path = file_create_filename($basename, $directory);
$this->assertEqual($path, $original, format_string('New filepath %new equals %original.', array('%new' => $path, '%original' => $original)), 'File');
// Then we test against a file that already exists within that directory.
$basename = 'druplicon.png';
$original = $directory . '/' . $basename;
$expected = $directory . '/druplicon_0.png';
$path = file_create_filename($basename, $directory);
$this->assertEqual($path, $expected, format_string('Creating a new filepath from %original equals %new (expected %expected).', array('%new' => $path, '%original' => $original, '%expected' => $expected)), 'File');
// @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix.
}
/**
* This will test the filepath for a destination based on passed flags and
* whether or not the file exists.
*
* If a file exists, file_destination($destination, $replace) will either
* return:
* - the existing filepath, if $replace is FILE_EXISTS_REPLACE
* - a new filepath if FILE_EXISTS_RENAME
* - an error (returning FALSE) if FILE_EXISTS_ERROR.
* If the file doesn't currently exist, then it will simply return the
* filepath.
*/
function testFileDestination() {
// First test for non-existent file.
$destination = 'core/misc/xyz.txt';
$path = file_destination($destination, FILE_EXISTS_REPLACE);
$this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_REPLACE.', 'File');
$path = file_destination($destination, FILE_EXISTS_RENAME);
$this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_RENAME.', 'File');
$path = file_destination($destination, FILE_EXISTS_ERROR);
$this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_ERROR.', 'File');
$destination = 'core/misc/druplicon.png';
$path = file_destination($destination, FILE_EXISTS_REPLACE);
$this->assertEqual($path, $destination, 'Existing filepath destination remains the same with FILE_EXISTS_REPLACE.', 'File');
$path = file_destination($destination, FILE_EXISTS_RENAME);
$this->assertNotEqual($path, $destination, 'A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.', 'File');
$path = file_destination($destination, FILE_EXISTS_ERROR);
$this->assertEqual($path, FALSE, 'An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.', 'File');
}
/**
* Ensure that the file_directory_temp() function always returns a value.
*/
function testFileDirectoryTemp() {
// Start with an empty variable to ensure we have a clean slate.
$config = $this->config('system.file');
$config->set('path.temporary', '')->save();
$tmp_directory = file_directory_temp();
$this->assertEqual(empty($tmp_directory), FALSE, 'file_directory_temp() returned a non-empty value.');
$this->assertEqual($config->get('path.temporary'), $tmp_directory);
}
}
@@ -1,38 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\Component\PhpStorage\FileStorage;
use Drupal\simpletest\WebTestBase;
/**
* Tests the log message added by file_save_htacess().
*
* @group File
*/
class FileSaveHtaccessLoggingTest extends WebTestBase {
protected static $modules = ['dblog'];
/**
* Tests file_save_htaccess().
*/
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);
}
}
}
@@ -1,168 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\simpletest\KernelTestBase;
/**
* Base class for file tests that adds some additional file specific
* assertions and helper functions.
*/
abstract class FileTestBase extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme;
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname;
protected function setUp() {
parent::setUp();
$this->installConfig(array('system'));
$this->registerStreamWrapper('private', 'Drupal\Core\StreamWrapper\PrivateStream');
if (isset($this->scheme)) {
$this->registerStreamWrapper($this->scheme, $this->classname);
}
}
/**
* Helper function to test the permissions of a file.
*
* @param $filepath
* String file path.
* @param $expected_mode
* Octal integer like 0664 or 0777.
* @param $message
* Optional message.
*/
function assertFilePermissions($filepath, $expected_mode, $message = NULL) {
// Clear out PHP's file stat cache to be sure we see the current value.
clearstatcache(TRUE, $filepath);
// Mask out all but the last three octets.
$actual_mode = fileperms($filepath) & 0777;
// PHP on Windows has limited support for file permissions. Usually each of
// "user", "group" and "other" use one octal digit (3 bits) to represent the
// read/write/execute bits. On Windows, chmod() ignores the "group" and
// "other" bits, and fileperms() returns the "user" bits in all three
// positions. $expected_mode is updated to reflect this.
if (substr(PHP_OS, 0, 3) == 'WIN') {
// Reset the "group" and "other" bits.
$expected_mode = $expected_mode & 0700;
// Shift the "user" bits to the "group" and "other" positions also.
$expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6;
}
if (!isset($message)) {
$message = t('Expected file permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
}
$this->assertEqual($actual_mode, $expected_mode, $message);
}
/**
* Helper function to test the permissions of a directory.
*
* @param $directory
* String directory path.
* @param $expected_mode
* Octal integer like 0664 or 0777.
* @param $message
* Optional message.
*/
function assertDirectoryPermissions($directory, $expected_mode, $message = NULL) {
// Clear out PHP's file stat cache to be sure we see the current value.
clearstatcache(TRUE, $directory);
// Mask out all but the last three octets.
$actual_mode = fileperms($directory) & 0777;
$expected_mode = $expected_mode & 0777;
// PHP on Windows has limited support for file permissions. Usually each of
// "user", "group" and "other" use one octal digit (3 bits) to represent the
// read/write/execute bits. On Windows, chmod() ignores the "group" and
// "other" bits, and fileperms() returns the "user" bits in all three
// positions. $expected_mode is updated to reflect this.
if (substr(PHP_OS, 0, 3) == 'WIN') {
// Reset the "group" and "other" bits.
$expected_mode = $expected_mode & 0700;
// Shift the "user" bits to the "group" and "other" positions also.
$expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6;
}
if (!isset($message)) {
$message = t('Expected directory permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
}
$this->assertEqual($actual_mode, $expected_mode, $message);
}
/**
* Create a directory and assert it exists.
*
* @param $path
* Optional string with a directory path. If none is provided, a random
* name in the site's files directory will be used.
* @return
* The path to the directory.
*/
function createDirectory($path = NULL) {
// A directory to operate on.
if (!isset($path)) {
$path = file_default_scheme() . '://' . $this->randomMachineName();
}
$this->assertTrue(drupal_mkdir($path) && is_dir($path), 'Directory was created successfully.');
return $path;
}
/**
* Create a file and return the URI of it.
*
* @param $filepath
* Optional string specifying the file path. If none is provided then a
* randomly named file will be created in the site's files directory.
* @param $contents
* Optional contents to save into the file. If a NULL value is provided an
* arbitrary string will be used.
* @param $scheme
* Optional string indicating the stream scheme to use. Drupal core includes
* public, private, and temporary. The public wrapper is the default.
* @return
* File URI.
*/
function createUri($filepath = NULL, $contents = NULL, $scheme = NULL) {
if (!isset($filepath)) {
// Prefix with non-latin characters to ensure that all file-related
// tests work with international filenames.
$filepath = 'Файл для тестирования ' . $this->randomMachineName();
}
if (!isset($scheme)) {
$scheme = file_default_scheme();
}
$filepath = $scheme . '://' . $filepath;
if (!isset($contents)) {
$contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data.";
}
file_put_contents($filepath, $contents);
$this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file');
return $filepath;
}
}
@@ -1,91 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\KernelTestBase;
/**
* Tests .htaccess file saving.
*
* @group File
*/
class HtaccessUnitTest extends KernelTestBase {
/**
* Tests file_save_htaccess().
*/
function testHtaccessSave() {
// Prepare test directories.
$public = $this->publicFilesDirectory . '/test/public';
$private = $this->publicFilesDirectory . '/test/private';
$stream = 'public://test/stream';
// Verify that file_save_htaccess() returns FALSE if .htaccess cannot be
// written.
// Note: We cannot test the condition of a directory lacking write
// permissions, since at least on Windows file_save_htaccess() succeeds
// even when changing directory permissions to 0000.
$this->assertFalse(file_save_htaccess($public, FALSE));
// Create public .htaccess file.
mkdir($public, 0777, TRUE);
$this->assertTrue(file_save_htaccess($public, FALSE));
$content = file_get_contents($public . '/.htaccess');
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006") !== FALSE);
$this->assertFalse(strpos($content, "Require all denied") !== FALSE);
$this->assertFalse(strpos($content, "Deny from all") !== FALSE);
$this->assertTrue(strpos($content, "Options -Indexes -ExecCGI -Includes -MultiViews") !== FALSE);
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003") !== FALSE);
$this->assertFilePermissions($public . '/.htaccess', 0444);
$this->assertTrue(file_save_htaccess($public, FALSE));
// Create private .htaccess file.
mkdir($private, 0777, TRUE);
$this->assertTrue(file_save_htaccess($private));
$content = file_get_contents($private . '/.htaccess');
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006") !== FALSE);
$this->assertTrue(strpos($content, "Require all denied") !== FALSE);
$this->assertTrue(strpos($content, "Deny from all") !== FALSE);
$this->assertTrue(strpos($content, "Options -Indexes -ExecCGI -Includes -MultiViews") !== FALSE);
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003") !== FALSE);
$this->assertFilePermissions($private . '/.htaccess', 0444);
$this->assertTrue(file_save_htaccess($private));
// Create an .htaccess file using a stream URI.
mkdir($stream, 0777, TRUE);
$this->assertTrue(file_save_htaccess($stream));
$content = file_get_contents($stream . '/.htaccess');
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006") !== FALSE);
$this->assertTrue(strpos($content, "Require all denied") !== FALSE);
$this->assertTrue(strpos($content, "Deny from all") !== FALSE);
$this->assertTrue(strpos($content, "Options -Indexes -ExecCGI -Includes -MultiViews") !== FALSE);
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003") !== FALSE);
$this->assertFilePermissions($stream . '/.htaccess', 0444);
$this->assertTrue(file_save_htaccess($stream));
}
/**
* Asserts expected file permissions for a given file.
*
* @param string $uri
* The URI of the file to check.
* @param int $expected
* The expected file permissions; e.g., 0444.
*
* @return bool
* Whether the actual file permissions match the expected.
*/
protected function assertFilePermissions($uri, $expected) {
$actual = fileperms($uri) & 0777;
return $this->assertIdentical($actual, $expected, SafeMarkup::format('@uri file permissions @actual are identical to @expected.', array(
'@uri' => $uri,
'@actual' => 0 . decoct($actual),
'@expected' => 0 . decoct($expected),
)));
}
}
@@ -1,92 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests filename mimetype detection.
*
* @group File
*/
class MimeTypeTest extends FileTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* Test mapping of mimetypes from filenames.
*/
public function testFileMimeTypeDetection() {
$prefixes = ['public://', 'private://', 'temporary://', 'dummy-remote://'];
$test_case = array(
'test.jar' => 'application/java-archive',
'test.jpeg' => 'image/jpeg',
'test.JPEG' => 'image/jpeg',
'test.jpg' => 'image/jpeg',
'test.jar.jpg' => 'image/jpeg',
'test.jpg.jar' => 'application/java-archive',
'test.pcf.Z' => 'application/x-font',
'pcf.z' => 'application/octet-stream',
'jar' => 'application/octet-stream',
'some.junk' => 'application/octet-stream',
'foo.file_test_1' => 'madeup/file_test_1',
'foo.file_test_2' => 'madeup/file_test_2',
'foo.doc' => 'madeup/doc',
'test.ogg' => 'audio/ogg',
);
$guesser = $this->container->get('file.mime_type.guesser');
// Test using default mappings.
foreach ($test_case as $input => $expected) {
// Test stream [URI].
foreach ($prefixes as $prefix) {
$output = $guesser->guess($prefix . $input);
$this->assertIdentical($output, $expected, format_string('Mimetype for %input is %output (expected: %expected).', array('%input' => $prefix . $input, '%output' => $output, '%expected' => $expected)));
}
// Test normal path equivalent
$output = $guesser->guess($input);
$this->assertIdentical($output, $expected, format_string('Mimetype (using default mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
}
// Now test the extension gusser by passing in a custom mapping.
$mapping = array(
'mimetypes' => array(
0 => 'application/java-archive',
1 => 'image/jpeg',
),
'extensions' => array(
'jar' => 0,
'jpg' => 1,
)
);
$test_case = array(
'test.jar' => 'application/java-archive',
'test.jpeg' => 'application/octet-stream',
'test.jpg' => 'image/jpeg',
'test.jar.jpg' => 'image/jpeg',
'test.jpg.jar' => 'application/java-archive',
'test.pcf.z' => 'application/octet-stream',
'pcf.z' => 'application/octet-stream',
'jar' => 'application/octet-stream',
'some.junk' => 'application/octet-stream',
'foo.file_test_1' => 'application/octet-stream',
'foo.file_test_2' => 'application/octet-stream',
'foo.doc' => 'application/octet-stream',
'test.ogg' => 'application/octet-stream',
);
$extension_guesser = $this->container->get('file.mime_type.guesser.extension');
$extension_guesser->setMapping($mapping);
foreach ($test_case as $input => $expected) {
$output = $extension_guesser->guess($input);
$this->assertIdentical($output, $expected, format_string('Mimetype (using passed-in mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
}
}
}
@@ -1,87 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests filename munging and unmunging.
*
* @group File
*/
class NameMungingTest extends FileTestBase {
/**
* @var string
*/
protected $badExtension;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $nameWithUcExt;
protected function setUp() {
parent::setUp();
$this->badExtension = 'php';
$this->name = $this->randomMachineName() . '.' . $this->badExtension . '.txt';
$this->nameWithUcExt = $this->randomMachineName() . '.' . strtoupper($this->badExtension) . '.txt';
}
/**
* Create a file and munge/unmunge the name.
*/
function testMunging() {
// Disable insecure uploads.
$this->config('system.file')->set('allow_insecure_uploads', 0)->save();
$munged_name = file_munge_filename($this->name, '', TRUE);
$messages = drupal_get_messages();
$this->assertTrue(in_array(strtr('For security reasons, your upload has been renamed to <em class="placeholder">%filename</em>.', array('%filename' => $munged_name)), $messages['status']), 'Alert properly set when a file is renamed.');
$this->assertNotEqual($munged_name, $this->name, format_string('The new filename (%munged) has been modified from the original (%original)', array('%munged' => $munged_name, '%original' => $this->name)));
}
/**
* Tests munging with a null byte in the filename.
*/
function testMungeNullByte() {
$prefix = $this->randomMachineName();
$filename = $prefix . '.' . $this->badExtension . "\0.txt";
$this->assertEqual(file_munge_filename($filename, ''), $prefix . '.' . $this->badExtension . '_.txt', 'A filename with a null byte is correctly munged to remove the null byte.');
}
/**
* If the system.file.allow_insecure_uploads setting evaluates to true, the file should
* come out untouched, no matter how evil the filename.
*/
function testMungeIgnoreInsecure() {
$this->config('system.file')->set('allow_insecure_uploads', 1)->save();
$munged_name = file_munge_filename($this->name, '');
$this->assertIdentical($munged_name, $this->name, format_string('The original filename (%original) matches the munged filename (%munged) when insecure uploads are enabled.', array('%munged' => $munged_name, '%original' => $this->name)));
}
/**
* White listed extensions are ignored by file_munge_filename().
*/
function testMungeIgnoreWhitelisted() {
// Declare our extension as whitelisted. The declared extensions should
// be case insensitive so test using one with a different case.
$munged_name = file_munge_filename($this->nameWithUcExt, $this->badExtension);
$this->assertIdentical($munged_name, $this->nameWithUcExt, format_string('The new filename (%munged) matches the original (%original) once the extension has been whitelisted.', array('%munged' => $munged_name, '%original' => $this->nameWithUcExt)));
// The allowed extensions should also be normalized.
$munged_name = file_munge_filename($this->name, strtoupper($this->badExtension));
$this->assertIdentical($munged_name, $this->name, format_string('The new filename (%munged) matches the original (%original) also when the whitelisted extension is in uppercase.', array('%munged' => $munged_name, '%original' => $this->name)));
}
/**
* Ensure that unmunge gets your name back.
*/
function testUnMunge() {
$munged_name = file_munge_filename($this->name, '', FALSE);
$unmunged_name = file_unmunge_filename($munged_name);
$this->assertIdentical($unmunged_name, $this->name, format_string('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name)));
}
}
@@ -1,98 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper;
/**
* Tests the read-only stream wrapper write functions.
*
* @group File
*/
class ReadOnlyStreamWrapperTest extends FileTestBase {
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy-readonly';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper';
/**
* Test read-only specific behavior.
*/
function testReadOnlyBehavior() {
$type = DummyReadOnlyStreamWrapper::getType();
// Checks that the stream wrapper type is not declared as writable.
$this->assertIdentical(0, $type & StreamWrapperInterface::WRITE);
// Checks that the stream wrapper type is declared as local.
$this->assertIdentical(1, $type & StreamWrapperInterface::LOCAL);
// Generate a test file
$filename = $this->randomMachineName();
$site_path = $this->container->get('site.path');
$filepath = $site_path . '/files/' . $filename;
file_put_contents($filepath, $filename);
// Generate a read-only stream wrapper instance
$uri = $this->scheme . '://' . $filename;
\Drupal::service('stream_wrapper_manager')->getViaScheme($this->scheme);
// Attempt to open a file in read/write mode
$handle = @fopen($uri, 'r+');
$this->assertFalse($handle, 'Unable to open a file for reading and writing with the read-only stream wrapper.');
// Attempt to open a file in binary read mode
$handle = fopen($uri, 'rb');
$this->assertTrue($handle, 'Able to open a file for reading in binary mode with the read-only stream wrapper.');
$this->assertTrue(fclose($handle), 'Able to close file opened in binary mode using the read_only stream wrapper.');
// Attempt to open a file in text read mode
$handle = fopen($uri, 'rt');
$this->assertTrue($handle, 'Able to open a file for reading in text mode with the read-only stream wrapper.');
$this->assertTrue(fclose($handle), 'Able to close file opened in text mode using the read_only stream wrapper.');
// Attempt to open a file in read mode
$handle = fopen($uri, 'r');
$this->assertTrue($handle, 'Able to open a file for reading with the read-only stream wrapper.');
// Attempt to change file permissions
$this->assertFalse(@chmod($uri, 0777), 'Unable to change file permissions when using read-only stream wrapper.');
// Attempt to acquire an exclusive lock for writing
$this->assertFalse(@flock($handle, LOCK_EX | LOCK_NB), 'Unable to acquire an exclusive lock using the read-only stream wrapper.');
// Attempt to obtain a shared lock
$this->assertTrue(flock($handle, LOCK_SH | LOCK_NB), 'Able to acquire a shared lock using the read-only stream wrapper.');
// Attempt to release a shared lock
$this->assertTrue(flock($handle, LOCK_UN | LOCK_NB), 'Able to release a shared lock using the read-only stream wrapper.');
// Attempt to truncate the file
$this->assertFalse(@ftruncate($handle, 0), 'Unable to truncate using the read-only stream wrapper.');
// Attempt to write to the file
$this->assertFalse(@fwrite($handle, $this->randomMachineName()), 'Unable to write to file using the read-only stream wrapper.');
// Attempt to flush output to the file
$this->assertFalse(@fflush($handle), 'Unable to flush output to file using the read-only stream wrapper.');
// Attempt to close the stream. (Suppress errors, as fclose triggers fflush.)
$this->assertTrue(fclose($handle), 'Able to close file using the read_only stream wrapper.');
// Test the rename() function
$this->assertFalse(@rename($uri, $this->scheme . '://newname.txt'), 'Unable to rename files using the read-only stream wrapper.');
// Test the unlink() function
$this->assertTrue(@drupal_unlink($uri), 'Able to unlink file using read-only stream wrapper.');
$this->assertTrue(file_exists($filepath), 'Unlink File was not actually deleted.');
// Test the mkdir() function by attempting to create a directory.
$dirname = $this->randomMachineName();
$dir = $site_path . '/files/' . $dirname;
$readonlydir = $this->scheme . '://' . $dirname;
$this->assertFalse(@drupal_mkdir($readonlydir, 0775, 0), 'Unable to create directory with read-only stream wrapper.');
// Create a temporary directory for testing purposes
$this->assertTrue(drupal_mkdir($dir), 'Test directory created.');
// Test the rmdir() function by attempting to remove the directory.
$this->assertFalse(@drupal_rmdir($readonlydir), 'Unable to delete directory with read-only stream wrapper.');
// Remove the temporary directory.
drupal_rmdir($dir);
}
}
@@ -1,38 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests operations dealing with directories.
*
* @group File
*/
class RemoteFileDirectoryTest extends DirectoryTest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy-remote';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
protected function setUp() {
parent::setUp();
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
}
}
@@ -1,38 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the file_scan_directory() function.
*
* @group File
*/
class RemoteFileScanDirectoryTest extends ScanDirectoryTest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy-remote';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
protected function setUp() {
parent::setUp();
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
}
}
@@ -1,38 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the unmanaged file copy function.
*
* @group File
*/
class RemoteFileUnmanagedCopyTest extends UnmanagedCopyTest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy-remote';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
protected function setUp() {
parent::setUp();
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
}
}
@@ -1,38 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the unmanaged file delete recursive function.
*
* @group File
*/
class RemoteFileUnmanagedDeleteRecursiveTest extends UnmanagedDeleteRecursiveTest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy-remote';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
protected function setUp() {
parent::setUp();
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
}
}
@@ -1,38 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the unmanaged file delete function.
*
* @group File
*/
class RemoteFileUnmanagedDeleteTest extends UnmanagedDeleteTest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy-remote';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
protected function setUp() {
parent::setUp();
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
}
}
@@ -1,38 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the unmanaged file move function.
*
* @group File
*/
class RemoteFileUnmanagedMoveTest extends UnmanagedMoveTest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy-remote';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
protected function setUp() {
parent::setUp();
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
}
}
@@ -1,38 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the unmanaged file save data function.
*
* @group File
*/
class RemoteFileUnmanagedSaveDataTest extends UnmanagedSaveDataTest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy-remote';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
protected function setUp() {
parent::setUp();
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
}
}
@@ -1,145 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the file_scan_directory() function.
*
* @group File
*/
class ScanDirectoryTest extends FileTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* @var string
*/
protected $path;
protected function setUp() {
parent::setUp();
// Hardcode the location of the simpletest files as it is already known
// and shouldn't change, and we don't yet have a way to retrieve their
// location from drupal_get_filename() in a cached way.
// @todo Remove as part of https://www.drupal.org/node/2186491
$this->path = 'core/modules/simpletest/files';
}
/**
* Check the format of the returned values.
*/
function testReturn() {
// Grab a listing of all the JavaScript files and check that they're
// passed to the callback.
$all_files = file_scan_directory($this->path, '/^javascript-/');
ksort($all_files);
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
// Check the first file.
$file = reset($all_files);
$this->assertEqual(key($all_files), $file->uri, 'Correct array key was used for the first returned file.');
$this->assertEqual($file->uri, $this->path . '/javascript-1.txt', 'First file name was set correctly.');
$this->assertEqual($file->filename, 'javascript-1.txt', 'First basename was set correctly');
$this->assertEqual($file->name, 'javascript-1', 'First name was set correctly.');
// Check the second file.
$file = next($all_files);
$this->assertEqual(key($all_files), $file->uri, 'Correct array key was used for the second returned file.');
$this->assertEqual($file->uri, $this->path . '/javascript-2.script', 'Second file name was set correctly.');
$this->assertEqual($file->filename, 'javascript-2.script', 'Second basename was set correctly');
$this->assertEqual($file->name, 'javascript-2', 'Second name was set correctly.');
}
/**
* Check that the callback function is called correctly.
*/
function testOptionCallback() {
// When nothing is matched nothing should be passed to the callback.
$all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', array('callback' => 'file_test_file_scan_callback'));
$this->assertEqual(0, count($all_files), 'No files were found.');
$results = file_test_file_scan_callback();
file_test_file_scan_callback_reset();
$this->assertEqual(0, count($results), 'No files were passed to the callback.');
// Grab a listing of all the JavaScript files and check that they're
// passed to the callback.
$all_files = file_scan_directory($this->path, '/^javascript-/', array('callback' => 'file_test_file_scan_callback'));
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
$results = file_test_file_scan_callback();
file_test_file_scan_callback_reset();
$this->assertEqual(2, count($results), 'Files were passed to the callback.');
}
/**
* Check that the no-mask parameter is honored.
*/
function testOptionNoMask() {
// Grab a listing of all the JavaScript files.
$all_files = file_scan_directory($this->path, '/^javascript-/');
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
// Now use the nomask parameter to filter out the .script file.
$filtered_files = file_scan_directory($this->path, '/^javascript-/', array('nomask' => '/.script$/'));
$this->assertEqual(1, count($filtered_files), 'Filtered correctly.');
}
/**
* Check that key parameter sets the return value's key.
*/
function testOptionKey() {
// "filename", for the path starting with $dir.
$expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filepath')));
sort($actual);
$this->assertEqual($expected, $actual, 'Returned the correct values for the filename key.');
// "basename", for the basename of the file.
$expected = array('javascript-1.txt', 'javascript-2.script');
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filename')));
sort($actual);
$this->assertEqual($expected, $actual, 'Returned the correct values for the basename key.');
// "name" for the name of the file without an extension.
$expected = array('javascript-1', 'javascript-2');
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'name')));
sort($actual);
$this->assertEqual($expected, $actual, 'Returned the correct values for the name key.');
// Invalid option that should default back to "filename".
$expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'INVALID')));
sort($actual);
$this->assertEqual($expected, $actual, 'An invalid key defaulted back to the default.');
}
/**
* Check that the recurse option descends into subdirectories.
*/
function testOptionRecurse() {
$files = file_scan_directory($this->path . '/..', '/^javascript-/', array('recurse' => FALSE));
$this->assertTrue(empty($files), "Without recursion couldn't find javascript files.");
$files = file_scan_directory($this->path . '/..', '/^javascript-/', array('recurse' => TRUE));
$this->assertEqual(2, count($files), 'With recursion we found the expected javascript files.');
}
/**
* Check that the min_depth options lets us ignore files in the starting
* directory.
*/
function testOptionMinDepth() {
$files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 0));
$this->assertEqual(2, count($files), 'No minimum-depth gets files in current directory.');
$files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 1));
$this->assertTrue(empty($files), 'Minimum-depth of 1 successfully excludes files from current directory.');
}
}
@@ -1,149 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\PublicStream;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests stream wrapper functions.
*
* @group File
*/
class StreamWrapperTest extends FileTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* A stream wrapper scheme to register for the test.
*
* @var string
*/
protected $scheme = 'dummy';
/**
* A fully-qualified stream wrapper class name to register for the test.
*
* @var string
*/
protected $classname = 'Drupal\file_test\StreamWrapper\DummyStreamWrapper';
function setUp() {
// Add file_private_path setting.
$settings = Settings::getAll();
$request = Request::create('/');;
$site_path = DrupalKernel::findSitePath($request);
$settings['file_private_path'] = $site_path . '/private';
new Settings($settings + Settings::getAll());
parent::setUp();
}
/**
* Test the getClassName() function.
*/
function testGetClassName() {
// Check the dummy scheme.
$this->assertEqual($this->classname, \Drupal::service('stream_wrapper_manager')->getClass($this->scheme), 'Got correct class name for dummy scheme.');
// Check core's scheme.
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', \Drupal::service('stream_wrapper_manager')->getClass('public'), 'Got correct class name for public scheme.');
}
/**
* Test the getViaScheme() method.
*/
function testGetInstanceByScheme() {
$instance = \Drupal::service('stream_wrapper_manager')->getViaScheme($this->scheme);
$this->assertEqual($this->classname, get_class($instance), 'Got correct class type for dummy scheme.');
$instance = \Drupal::service('stream_wrapper_manager')->getViaScheme('public');
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', get_class($instance), 'Got correct class type for public scheme.');
}
/**
* Test the getViaUri() and getViaScheme() methods and target functions.
*/
function testUriFunctions() {
$config = $this->config('system.file');
$instance = \Drupal::service('stream_wrapper_manager')->getViaUri($this->scheme . '://foo');
$this->assertEqual($this->classname, get_class($instance), 'Got correct class type for dummy URI.');
$instance = \Drupal::service('stream_wrapper_manager')->getViaUri('public://foo');
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', get_class($instance), 'Got correct class type for public URI.');
// Test file_uri_target().
$this->assertEqual(file_uri_target('public://foo/bar.txt'), 'foo/bar.txt', 'Got a valid stream target from public://foo/bar.txt.');
$this->assertEqual(file_uri_target('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='), 'image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', t('Got a valid stream target from a data URI.'));
$this->assertFalse(file_uri_target('foo/bar.txt'), 'foo/bar.txt is not a valid stream.');
$this->assertFalse(file_uri_target('public://'), 'public:// has no target.');
$this->assertFalse(file_uri_target('data:'), 'data: has no target.');
// Test file_build_uri() and
// Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath().
$this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', 'Expected scheme was added.');
$this->assertEqual(\Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath(), PublicStream::basePath(), 'Expected default directory path was returned.');
$this->assertEqual(\Drupal::service('stream_wrapper_manager')->getViaScheme('temporary')->getDirectoryPath(), $config->get('path.temporary'), 'Expected temporary directory path was returned.');
$config->set('default_scheme', 'private')->save();
$this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', 'Got a valid URI from foo/bar.txt.');
// Test file_create_url()
// TemporaryStream::getExternalUrl() uses Url::fromRoute(), which needs
// route information to work.
$this->container->get('router.builder')->rebuild();
$this->assertTrue(strpos(file_create_url('temporary://test.txt'), 'system/temporary?file=test.txt'), 'Temporary external URL correctly built.');
$this->assertTrue(strpos(file_create_url('public://test.txt'), Settings::get('file_public_path') . '/test.txt'), 'Public external URL correctly built.');
$this->assertTrue(strpos(file_create_url('private://test.txt'), 'system/files/test.txt'), 'Private external URL correctly built.');
}
/**
* Test some file handle functions.
*/
function testFileFunctions() {
$filename = 'public://' . $this->randomMachineName();
file_put_contents($filename, str_repeat('d', 1000));
// Open for rw and place pointer at beginning of file so select will return.
$handle = fopen($filename, 'c+');
$this->assertTrue($handle, 'Able to open a file for appending, reading and writing.');
// Attempt to change options on the file stream: should all fail.
$this->assertFalse(@stream_set_blocking($handle, 0), 'Unable to set to non blocking using a local stream wrapper.');
$this->assertFalse(@stream_set_blocking($handle, 1), 'Unable to set to blocking using a local stream wrapper.');
$this->assertFalse(@stream_set_timeout($handle, 1), 'Unable to set read time out using a local stream wrapper.');
$this->assertEqual(-1 /*EOF*/, @stream_set_write_buffer($handle, 512), 'Unable to set write buffer using a local stream wrapper.');
// This will test stream_cast().
$read = array($handle);
$write = NULL;
$except = NULL;
$this->assertEqual(1, stream_select($read, $write, $except, 0), 'Able to cast a stream via stream_select.');
// This will test stream_truncate().
$this->assertEqual(1, ftruncate($handle, 0), 'Able to truncate a stream via ftruncate().');
fclose($handle);
$this->assertEqual(0, filesize($filename), 'Able to truncate a stream.');
// Cleanup.
unlink($filename);
}
/**
* Test the scheme functions.
*/
function testGetValidStreamScheme() {
$this->assertEqual('foo', file_uri_scheme('foo://pork//chops'), 'Got the correct scheme from foo://asdf');
$this->assertEqual('data', file_uri_scheme('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='), 'Got the correct scheme from a data URI.');
$this->assertFalse(file_uri_scheme('foo/bar.txt'), 'foo/bar.txt is not a valid stream.');
$this->assertTrue(file_stream_wrapper_valid_scheme(file_uri_scheme('public://asdf')), 'Got a valid stream scheme from public://asdf');
$this->assertFalse(file_stream_wrapper_valid_scheme(file_uri_scheme('foo://asdf')), 'Did not get a valid stream scheme from foo://asdf');
}
}
@@ -1,89 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\Core\Site\Settings;
use Drupal\Core\File\FileSystem;
/**
* Tests the unmanaged file copy function.
*
* @group File
*/
class UnmanagedCopyTest extends FileTestBase {
/**
* Copy a normal file.
*/
function testNormal() {
// Create a file for testing
$uri = $this->createUri();
// Copying to a new name.
$desired_filepath = 'public://' . $this->randomMachineName();
$new_filepath = file_unmanaged_copy($uri, $desired_filepath, FILE_EXISTS_ERROR);
$this->assertTrue($new_filepath, 'Copy was successful.');
$this->assertEqual($new_filepath, $desired_filepath, 'Returned expected filepath.');
$this->assertTrue(file_exists($uri), 'Original file remains.');
$this->assertTrue(file_exists($new_filepath), 'New file exists.');
$this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
// Copying with rename.
$desired_filepath = 'public://' . $this->randomMachineName();
$this->assertTrue(file_put_contents($desired_filepath, ' '), 'Created a file so a rename will have to happen.');
$newer_filepath = file_unmanaged_copy($uri, $desired_filepath, FILE_EXISTS_RENAME);
$this->assertTrue($newer_filepath, 'Copy was successful.');
$this->assertNotEqual($newer_filepath, $desired_filepath, 'Returned expected filepath.');
$this->assertTrue(file_exists($uri), 'Original file remains.');
$this->assertTrue(file_exists($newer_filepath), 'New file exists.');
$this->assertFilePermissions($newer_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
// TODO: test copying to a directory (rather than full directory/file path)
// TODO: test copying normal files using normal paths (rather than only streams)
}
/**
* Copy a non-existent file.
*/
function testNonExistent() {
// Copy non-existent file
$desired_filepath = $this->randomMachineName();
$this->assertFalse(file_exists($desired_filepath), "Randomly named file doesn't exists.");
$new_filepath = file_unmanaged_copy($desired_filepath, $this->randomMachineName());
$this->assertFalse($new_filepath, 'Copying a missing file fails.');
}
/**
* Copy a file onto itself.
*/
function testOverwriteSelf() {
// Create a file for testing
$uri = $this->createUri();
// Copy the file onto itself with renaming works.
$new_filepath = file_unmanaged_copy($uri, $uri, FILE_EXISTS_RENAME);
$this->assertTrue($new_filepath, 'Copying onto itself with renaming works.');
$this->assertNotEqual($new_filepath, $uri, 'Copied file has a new name.');
$this->assertTrue(file_exists($uri), 'Original file exists after copying onto itself.');
$this->assertTrue(file_exists($new_filepath), 'Copied file exists after copying onto itself.');
$this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
// Copy the file onto itself without renaming fails.
$new_filepath = file_unmanaged_copy($uri, $uri, FILE_EXISTS_ERROR);
$this->assertFalse($new_filepath, 'Copying onto itself without renaming fails.');
$this->assertTrue(file_exists($uri), 'File exists after copying onto itself.');
// Copy the file into same directory without renaming fails.
$new_filepath = file_unmanaged_copy($uri, drupal_dirname($uri), FILE_EXISTS_ERROR);
$this->assertFalse($new_filepath, 'Copying onto itself fails.');
$this->assertTrue(file_exists($uri), 'File exists after copying onto itself.');
// Copy the file into same directory with renaming works.
$new_filepath = file_unmanaged_copy($uri, drupal_dirname($uri), FILE_EXISTS_RENAME);
$this->assertTrue($new_filepath, 'Copying into same directory works.');
$this->assertNotEqual($new_filepath, $uri, 'Copied file has a new name.');
$this->assertTrue(file_exists($uri), 'Original file exists after copying onto itself.');
$this->assertTrue(file_exists($new_filepath), 'Copied file exists after copying onto itself.');
$this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
}
}
@@ -1,74 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the unmanaged file delete recursive function.
*
* @group File
*/
class UnmanagedDeleteRecursiveTest extends FileTestBase {
/**
* Delete a normal file.
*/
function testSingleFile() {
// Create a file for testing
$filepath = file_default_scheme() . '://' . $this->randomMachineName();
file_put_contents($filepath, '');
// Delete the file.
$this->assertTrue(file_unmanaged_delete_recursive($filepath), 'Function reported success.');
$this->assertFalse(file_exists($filepath), 'Test file has been deleted.');
}
/**
* Try deleting an empty directory.
*/
function testEmptyDirectory() {
// A directory to operate on.
$directory = $this->createDirectory();
// Delete the directory.
$this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
$this->assertFalse(file_exists($directory), 'Directory has been deleted.');
}
/**
* Try deleting a directory with some files.
*/
function testDirectory() {
// A directory to operate on.
$directory = $this->createDirectory();
$filepathA = $directory . '/A';
$filepathB = $directory . '/B';
file_put_contents($filepathA, '');
file_put_contents($filepathB, '');
// Delete the directory.
$this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
$this->assertFalse(file_exists($filepathA), 'Test file A has been deleted.');
$this->assertFalse(file_exists($filepathB), 'Test file B has been deleted.');
$this->assertFalse(file_exists($directory), 'Directory has been deleted.');
}
/**
* Try deleting subdirectories with some files.
*/
function testSubDirectory() {
// A directory to operate on.
$directory = $this->createDirectory();
$subdirectory = $this->createDirectory($directory . '/sub');
$filepathA = $directory . '/A';
$filepathB = $subdirectory . '/B';
file_put_contents($filepathA, '');
file_put_contents($filepathB, '');
// Delete the directory.
$this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
$this->assertFalse(file_exists($filepathA), 'Test file A has been deleted.');
$this->assertFalse(file_exists($filepathB), 'Test file B has been deleted.');
$this->assertFalse(file_exists($subdirectory), 'Subdirectory has been deleted.');
$this->assertFalse(file_exists($directory), 'Directory has been deleted.');
}
}
@@ -1,43 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the unmanaged file delete function.
*
* @group File
*/
class UnmanagedDeleteTest extends FileTestBase {
/**
* Delete a normal file.
*/
function testNormal() {
// Create a file for testing
$uri = $this->createUri();
// Delete a regular file
$this->assertTrue(file_unmanaged_delete($uri), 'Deleted worked.');
$this->assertFalse(file_exists($uri), 'Test file has actually been deleted.');
}
/**
* Try deleting a missing file.
*/
function testMissing() {
// Try to delete a non-existing file
$this->assertTrue(file_unmanaged_delete(file_default_scheme() . '/' . $this->randomMachineName()), 'Returns true when deleting a non-existent file.');
}
/**
* Try deleting a directory.
*/
function testDirectory() {
// A directory to operate on.
$directory = $this->createDirectory();
// Try to delete a directory
$this->assertFalse(file_unmanaged_delete($directory), 'Could not delete the delete directory.');
$this->assertTrue(file_exists($directory), 'Directory has not been deleted.');
}
}
@@ -1,73 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Drupal\Core\Site\Settings;
use Drupal\Core\File\FileSystem;
/**
* Tests the unmanaged file move function.
*
* @group File
*/
class UnmanagedMoveTest extends FileTestBase {
/**
* Move a normal file.
*/
function testNormal() {
// Create a file for testing
$uri = $this->createUri();
// Moving to a new name.
$desired_filepath = 'public://' . $this->randomMachineName();
$new_filepath = file_unmanaged_move($uri, $desired_filepath, FILE_EXISTS_ERROR);
$this->assertTrue($new_filepath, 'Move was successful.');
$this->assertEqual($new_filepath, $desired_filepath, 'Returned expected filepath.');
$this->assertTrue(file_exists($new_filepath), 'File exists at the new location.');
$this->assertFalse(file_exists($uri), 'No file remains at the old location.');
$this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
// Moving with rename.
$desired_filepath = 'public://' . $this->randomMachineName();
$this->assertTrue(file_exists($new_filepath), 'File exists before moving.');
$this->assertTrue(file_put_contents($desired_filepath, ' '), 'Created a file so a rename will have to happen.');
$newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
$this->assertTrue($newer_filepath, 'Move was successful.');
$this->assertNotEqual($newer_filepath, $desired_filepath, 'Returned expected filepath.');
$this->assertTrue(file_exists($newer_filepath), 'File exists at the new location.');
$this->assertFalse(file_exists($new_filepath), 'No file remains at the old location.');
$this->assertFilePermissions($newer_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
// TODO: test moving to a directory (rather than full directory/file path)
// TODO: test creating and moving normal files (rather than streams)
}
/**
* Try to move a missing file.
*/
function testMissing() {
// Move non-existent file.
$new_filepath = file_unmanaged_move($this->randomMachineName(), $this->randomMachineName());
$this->assertFalse($new_filepath, 'Moving a missing file fails.');
}
/**
* Try to move a file onto itself.
*/
function testOverwriteSelf() {
// Create a file for testing.
$uri = $this->createUri();
// Move the file onto itself without renaming shouldn't make changes.
$new_filepath = file_unmanaged_move($uri, $uri, FILE_EXISTS_REPLACE);
$this->assertFalse($new_filepath, 'Moving onto itself without renaming fails.');
$this->assertTrue(file_exists($uri), 'File exists after moving onto itself.');
// Move the file onto itself with renaming will result in a new filename.
$new_filepath = file_unmanaged_move($uri, $uri, FILE_EXISTS_RENAME);
$this->assertTrue($new_filepath, 'Moving onto itself with renaming works.');
$this->assertFalse(file_exists($uri), 'Original file has been removed.');
$this->assertTrue(file_exists($new_filepath), 'File exists after moving onto itself.');
}
}
@@ -1,32 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
/**
* Tests the file_unmanaged_save_data() function.
*
* @group File
*/
class UnmanagedSaveDataTest extends FileTestBase {
/**
* Test the file_unmanaged_save_data() function.
*/
function testFileSaveData() {
$contents = $this->randomMachineName(8);
$this->settingsSet('file_chmod_file', 0777);
// No filename.
$filepath = file_unmanaged_save_data($contents);
$this->assertTrue($filepath, 'Unnamed file saved correctly.');
$this->assertEqual(file_uri_scheme($filepath), file_default_scheme(), "File was placed in Drupal's files directory.");
$this->assertEqual($contents, file_get_contents($filepath), 'Contents of the file are correct.');
// Provide a filename.
$filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE);
$this->assertTrue($filepath, 'Unnamed file saved correctly.');
$this->assertEqual('asdf.txt', drupal_basename($filepath), 'File was named correctly.');
$this->assertEqual($contents, file_get_contents($filepath), 'Contents of the file are correct.');
$this->assertFilePermissions($filepath, 0777);
}
}
@@ -1,118 +0,0 @@
<?php
namespace Drupal\system\Tests\File;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests for file URL rewriting.
*
* @group File
*/
class UrlRewritingTest extends FileTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* Tests the rewriting of shipped file URLs by hook_file_url_alter().
*/
function testShippedFileURL() {
// Test generating a URL to a shipped file (i.e. a file that is part of
// Drupal core, a module or a theme, for example a JavaScript file).
// Test alteration of file URLs to use a CDN.
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertEqual(FILE_URL_TEST_CDN_1 . '/' . $filepath, $url, 'Correctly generated a CDN URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = file_create_url($filepath);
$this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $filepath, $url, 'Correctly generated a CDN URL for a shipped file.');
// Test alteration of file URLs to use root-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'root-relative');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertEqual(base_path() . '/' . $filepath, $url, 'Correctly generated a root-relative URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = file_create_url($filepath);
$this->assertEqual(base_path() . '/' . $filepath, $url, 'Correctly generated a root-relative URL for a shipped file.');
// Test alteration of file URLs to use protocol-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'protocol-relative');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertEqual('/' . base_path() . '/' . $filepath, $url, 'Correctly generated a protocol-relative URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = file_create_url($filepath);
$this->assertEqual('/' . base_path() . '/' . $filepath, $url, 'Correctly generated a protocol-relative URL for a shipped file.');
// Test alteration of file URLs with query strings and/or fragment.
\Drupal::state()->delete('file_test.hook_file_url_alter');
$filepath = 'core/misc/favicon.ico';
$url = file_create_url($filepath . '?foo');
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=', $url, 'Correctly generated URL. The query string is present.');
$url = file_create_url($filepath . '?foo=bar');
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar', $url, 'Correctly generated URL. The query string is present.');
$url = file_create_url($filepath . '#v1.2');
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '#v1.2', $url, 'Correctly generated URL. The fragment is present.');
$url = file_create_url($filepath . '?foo=bar#v1.2');
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar#v1.2', $url, 'Correctly generated URL. The query string amd fragment is present.');
}
/**
* Tests the rewriting of public managed file URLs by hook_file_url_alter().
*/
function testPublicManagedFileURL() {
// Test generating a URL to a managed file.
// Test alteration of file URLs to use a CDN.
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
$uri = $this->createUri();
$url = file_create_url($uri);
$public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
$this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $public_directory_path . '/' . drupal_basename($uri), $url, 'Correctly generated a CDN URL for a created file.');
// Test alteration of file URLs to use root-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'root-relative');
$uri = $this->createUri();
$url = file_create_url($uri);
$this->assertEqual(base_path() . '/' . $public_directory_path . '/' . drupal_basename($uri), $url, 'Correctly generated a root-relative URL for a created file.');
// Test alteration of file URLs to use a protocol-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'protocol-relative');
$uri = $this->createUri();
$url = file_create_url($uri);
$this->assertEqual('/' . base_path() . '/' . $public_directory_path . '/' . drupal_basename($uri), $url, 'Correctly generated a protocol-relative URL for a created file.');
}
/**
* Test file_url_transform_relative().
*/
function testRelativeFileURL() {
// Disable file_test.module's hook_file_url_alter() implementation.
\Drupal::state()->set('file_test.hook_file_url_alter', NULL);
// Create a mock Request for file_url_transform_relative().
$request = Request::create($GLOBALS['base_url']);
$this->container->get('request_stack')->push($request);
\Drupal::setContainer($this->container);
// Shipped file.
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertIdentical(base_path() . $filepath, file_url_transform_relative($url));
// Managed file.
$uri = $this->createUri();
$url = file_create_url($uri);
$public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
$this->assertIdentical(base_path() . $public_directory_path . '/' . rawurlencode(drupal_basename($uri)), file_url_transform_relative($url));
}
}
@@ -1,93 +0,0 @@
<?php
namespace Drupal\system\Tests\FileTransfer;
use Drupal\Core\FileTransfer\FileTransferException;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\simpletest\WebTestBase;
/**
* Tests that the jail is respected and that protocols using recursive file move
* operations work.
*
* @group FileTransfer
*/
class FileTransferTest extends WebTestBase {
protected $hostname = 'localhost';
protected $username = 'drupal';
protected $password = 'password';
protected $port = '42';
protected function setUp() {
parent::setUp();
$this->testConnection = TestFileTransfer::factory(\Drupal::root(), array('hostname' => $this->hostname, 'username' => $this->username, 'password' => $this->password, 'port' => $this->port));
}
function _getFakeModuleFiles() {
$files = array(
'fake.module',
'fake.info.yml',
'theme' => array(
'fake.html.twig'
),
'inc' => array(
'fake.inc'
)
);
return $files;
}
function _buildFakeModule() {
$location = 'temporary://fake';
if (is_dir($location)) {
$ret = 0;
$output = array();
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;
}
function _writeDirectory($base, $files = array()) {
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);
}
}
}
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');
}
}
@@ -1,23 +0,0 @@
<?php
namespace Drupal\system\Tests\FileTransfer;
/**
* Mock connection object for test case.
*/
class MockTestConnection {
protected $commandsRun = array();
public $connectionString;
function run($cmd) {
$this->commandsRun[] = $cmd;
}
function flushCommands() {
$out = $this->commandsRun;
$this->commandsRun = array();
return $out;
}
}
@@ -1,65 +0,0 @@
<?php
namespace Drupal\system\Tests\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;
function __construct($jail, $username, $password, $hostname = 'localhost', $port = 9999) {
parent::__construct($jail, $username, $password, $hostname, $port);
}
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/";
}
function copyFileJailed($source, $destination) {
$this->connection->run("copyFile $source $destination");
}
protected function removeDirectoryJailed($directory) {
$this->connection->run("rmdir $directory");
}
function createDirectoryJailed($directory) {
$this->connection->run("mkdir $directory");
}
function removeFileJailed($destination) {
if (!ftp_delete($this->connection, $item)) {
throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item));
}
}
function isDirectory($path) {
return $this->shouldIsDirectoryReturnTrue;
}
function isFile($path) {
return FALSE;
}
function chmodJailed($path, $mode, $recursive) {
return;
}
}
@@ -1,39 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Component\Utility\Xss;
use Drupal\simpletest\WebTestBase;
/**
* Tests hook_form_alter() and hook_form_FORM_ID_alter().
*
* @group Form
*/
class AlterTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'form_test'];
/**
* Tests execution order of hook_form_alter() and hook_form_FORM_ID_alter().
*/
public function testExecutionOrder() {
$this->drupalGet('form-test/alter');
// Ensure that the order is first by module, then for a given module, the
// id-specific one after the generic one.
$expected = [
'block_form_form_test_alter_form_alter() executed.',
'form_test_form_alter() executed.',
'form_test_form_form_test_alter_form_alter() executed.',
'system_form_form_test_alter_form_alter() executed.',
];
$content = preg_replace('/\s+/', ' ', Xss::filter($this->content, []));
$this->assert(strpos($content, implode(' ', $expected)) !== FALSE, 'Form alter hooks executed in the expected order.');
}
}
@@ -1,76 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\field\Entity\FieldConfig;
use Drupal\simpletest\WebTestBase;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests altering forms to be rebuilt so there are multiple steps.
*
* @group Form
*/
class ArbitraryRebuildTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['text', 'form_test'];
protected function setUp() {
parent::setUp();
// Auto-create a field for testing.
FieldStorageConfig::create([
'entity_type' => 'user',
'field_name' => 'test_multiple',
'type' => 'text',
'cardinality' => -1,
'translatable' => FALSE,
])->save();
FieldConfig::create([
'entity_type' => 'user',
'field_name' => 'test_multiple',
'bundle' => 'user',
'label' => 'Test a multiple valued field',
])->save();
entity_get_form_display('user', 'user', 'register')
->setComponent('test_multiple', [
'type' => 'text_textfield',
'weight' => 0,
])
->save();
}
/**
* Tests a basic rebuild with the user registration form.
*/
public function testUserRegistrationRebuild() {
$edit = [
'name' => 'foo',
'mail' => 'bar@example.com',
];
$this->drupalPostForm('user/register', $edit, 'Rebuild');
$this->assertText('Form rebuilt.');
$this->assertFieldByName('name', 'foo', 'Entered username has been kept.');
$this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.');
}
/**
* Tests a rebuild caused by a multiple value field.
*/
public function testUserRegistrationMultipleField() {
$edit = [
'name' => 'foo',
'mail' => 'bar@example.com',
];
$this->drupalPostForm('user/register', $edit, t('Add another item'));
$this->assertText('Test a multiple valued field', 'Form has been rebuilt.');
$this->assertFieldByName('name', 'foo', 'Entered username has been kept.');
$this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.');
}
}
@@ -1,89 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\simpletest\WebTestBase;
/**
* Tests form API checkbox handling of various combinations of #default_value
* and #return_value.
*
* @group Form
*/
class CheckboxTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
public function testFormCheckbox() {
// Ensure that the checked state is determined and rendered correctly for
// tricky combinations of default and return values.
foreach ([FALSE, NULL, TRUE, 0, '0', '', 1, '1', 'foobar', '1foobar'] as $default_value) {
// Only values that can be used for array indices are supported for
// #return_value, with the exception of integer 0, which is not supported.
// @see \Drupal\Core\Render\Element\Checkbox::processCheckbox().
foreach (['0', '', 1, '1', 'foobar', '1foobar'] as $return_value) {
$form_array = \Drupal::formBuilder()->getForm('\Drupal\form_test\Form\FormTestCheckboxTypeJugglingForm', $default_value, $return_value);
$form = \Drupal::service('renderer')->renderRoot($form_array);
if ($default_value === TRUE) {
$checked = TRUE;
}
elseif ($return_value === '0') {
$checked = ($default_value === '0');
}
elseif ($return_value === '') {
$checked = ($default_value === '');
}
elseif ($return_value === 1 || $return_value === '1') {
$checked = ($default_value === 1 || $default_value === '1');
}
elseif ($return_value === 'foobar') {
$checked = ($default_value === 'foobar');
}
elseif ($return_value === '1foobar') {
$checked = ($default_value === '1foobar');
}
$checked_in_html = strpos($form, 'checked') !== FALSE;
$message = format_string('#default_value is %default_value #return_value is %return_value.', ['%default_value' => var_export($default_value, TRUE), '%return_value' => var_export($return_value, TRUE)]);
$this->assertIdentical($checked, $checked_in_html, $message);
}
}
// Ensure that $form_state->getValues() is populated correctly for a
// checkboxes group that includes a 0-indexed array of options.
$results = json_decode($this->drupalPostForm('form-test/checkboxes-zero/1', [], 'Save'));
$this->assertIdentical($results->checkbox_off, [0, 0, 0], 'All three in checkbox_off are zeroes: off.');
$this->assertIdentical($results->checkbox_zero_default, ['0', 0, 0], 'The first choice is on in checkbox_zero_default');
$this->assertIdentical($results->checkbox_string_zero_default, ['0', 0, 0], 'The first choice is on in checkbox_string_zero_default');
$edit = ['checkbox_off[0]' => '0'];
$results = json_decode($this->drupalPostForm('form-test/checkboxes-zero/1', $edit, 'Save'));
$this->assertIdentical($results->checkbox_off, ['0', 0, 0], 'The first choice is on in checkbox_off but the rest is not');
// Ensure that each checkbox is rendered correctly for a checkboxes group
// that includes a 0-indexed array of options.
$this->drupalPostForm('form-test/checkboxes-zero/0', [], 'Save');
$checkboxes = $this->xpath('//input[@type="checkbox"]');
$this->assertIdentical(count($checkboxes), 9, 'Correct number of checkboxes found.');
foreach ($checkboxes as $checkbox) {
$checked = isset($checkbox['checked']);
$name = (string) $checkbox['name'];
$this->assertIdentical($checked, $name == 'checkbox_zero_default[0]' || $name == 'checkbox_string_zero_default[0]', format_string('Checkbox %name correctly checked', ['%name' => $name]));
}
$edit = ['checkbox_off[0]' => '0'];
$this->drupalPostForm('form-test/checkboxes-zero/0', $edit, 'Save');
$checkboxes = $this->xpath('//input[@type="checkbox"]');
$this->assertIdentical(count($checkboxes), 9, 'Correct number of checkboxes found.');
foreach ($checkboxes as $checkbox) {
$checked = isset($checkbox['checked']);
$name = (string) $checkbox['name'];
$this->assertIdentical($checked, $name == 'checkbox_off[0]' || $name == 'checkbox_zero_default[0]' || $name == 'checkbox_string_zero_default[0]', format_string('Checkbox %name correctly checked', ['%name' => $name]));
}
}
}
@@ -1,86 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Tests confirmation forms.
*
* @group Form
*/
class ConfirmFormTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
public function testConfirmForm() {
// Test the building of the form.
$this->drupalGet('form-test/confirm-form');
$site_name = $this->config('system.site')->get('name');
$this->assertTitle(t('ConfirmFormTestForm::getQuestion(). | @site-name', ['@site-name' => $site_name]), 'The question was found as the page title.');
$this->assertText(t('ConfirmFormTestForm::getDescription().'), 'The description was used.');
$this->assertFieldByXPath('//input[@id="edit-submit"]', t('ConfirmFormTestForm::getConfirmText().'), 'The confirm text was used.');
// Test cancelling the form.
$this->clickLink(t('ConfirmFormTestForm::getCancelText().'));
$this->assertUrl('form-test/autocomplete', [], "The form's cancel link was followed.");
// Test submitting the form.
$this->drupalPostForm('form-test/confirm-form', NULL, t('ConfirmFormTestForm::getConfirmText().'));
$this->assertText('The ConfirmFormTestForm::submitForm() method was used for this form.');
$this->assertUrl('', [], "The form's redirect was followed.");
// Test submitting the form with a destination.
$this->drupalPostForm('form-test/confirm-form', NULL, t('ConfirmFormTestForm::getConfirmText().'), ['query' => ['destination' => 'admin/config']]);
$this->assertUrl('admin/config', [], "The form's redirect was not followed, the destination query string was followed.");
// Test cancelling the form with a complex destination.
$this->drupalGet('form-test/confirm-form-array-path');
$this->clickLink(t('ConfirmFormArrayPathTestForm::getCancelText().'));
$this->assertUrl('form-test/confirm-form', ['query' => ['destination' => 'admin/config']], "The form's complex cancel link was followed.");
}
/**
* Tests that the confirm form does not use external destinations.
*/
public function testConfirmFormWithExternalDestination() {
$this->drupalGet('form-test/confirm-form');
$this->assertCancelLinkUrl(Url::fromRoute('form_test.route8'));
$this->drupalGet('form-test/confirm-form', ['query' => ['destination' => 'node']]);
$this->assertCancelLinkUrl(Url::fromUri('internal:/node'));
$this->drupalGet('form-test/confirm-form', ['query' => ['destination' => 'http://example.com']]);
$this->assertCancelLinkUrl(Url::fromRoute('form_test.route8'));
$this->drupalGet('form-test/confirm-form', ['query' => ['destination' => '<front>']]);
$this->assertCancelLinkUrl(Url::fromRoute('<front>'));
// Other invalid destinations, should fall back to the form default.
$this->drupalGet('form-test/confirm-form', ['query' => ['destination' => '/http://example.com']]);
$this->assertCancelLinkUrl(Url::fromRoute('form_test.route8'));
}
/**
* Asserts that a cancel link is present pointing to the provided URL.
*
* @param \Drupal\Core\Url $url
* The url to check for.
* @param string $message
* The assert message.
* @param string $group
* The assertion group.
*
* @return bool
* Result of the assertion.
*/
public function assertCancelLinkUrl(Url $url, $message = '', $group = 'Other') {
$links = $this->xpath('//a[@href=:url]', [':url' => $url->toString()]);
$message = ($message ? $message : SafeMarkup::format('Cancel link with URL %url found.', ['%url' => $url->toString()]));
return $this->assertTrue(isset($links[0]), $message, $group);
}
}
@@ -1,186 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\simpletest\WebTestBase;
/**
* Tests building and processing of core form elements.
*
* @group Form
*/
class ElementTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
/**
* Tests placeholder text for elements that support placeholders.
*/
public function testPlaceHolderText() {
$this->drupalGet('form-test/placeholder-text');
$expected = 'placeholder-text';
// Test to make sure non-textarea elements have the proper placeholder text.
foreach (['textfield', 'tel', 'url', 'password', 'email', 'number'] as $type) {
$element = $this->xpath('//input[@id=:id and @placeholder=:expected]', [
':id' => 'edit-' . $type,
':expected' => $expected,
]);
$this->assertTrue(!empty($element), format_string('Placeholder text placed in @type.', ['@type' => $type]));
}
// Test to make sure textarea has the proper placeholder text.
$element = $this->xpath('//textarea[@id=:id and @placeholder=:expected]', [
':id' => 'edit-textarea',
':expected' => $expected,
]);
$this->assertTrue(!empty($element), 'Placeholder text placed in textarea.');
}
/**
* Tests expansion of #options for #type checkboxes and radios.
*/
public function testOptions() {
$this->drupalGet('form-test/checkboxes-radios');
// Verify that all options appear in their defined order.
foreach (['checkbox', 'radio'] as $type) {
$elements = $this->xpath('//input[@type=:type]', [':type' => $type]);
$expected_values = ['0', 'foo', '1', 'bar', '>'];
foreach ($elements as $element) {
$expected = array_shift($expected_values);
$this->assertIdentical((string) $element['value'], $expected);
}
}
// Verify that the choices are admin filtered as expected.
$this->assertRaw("<em>Special Char</em>alert('checkboxes');");
$this->assertRaw("<em>Special Char</em>alert('radios');");
$this->assertRaw('<em>Bar - checkboxes</em>');
$this->assertRaw('<em>Bar - radios</em>');
// Enable customized option sub-elements.
$this->drupalGet('form-test/checkboxes-radios/customize');
// Verify that all options appear in their defined order, taking a custom
// #weight into account.
foreach (['checkbox', 'radio'] as $type) {
$elements = $this->xpath('//input[@type=:type]', [':type' => $type]);
$expected_values = ['0', 'foo', 'bar', '>', '1'];
foreach ($elements as $element) {
$expected = array_shift($expected_values);
$this->assertIdentical((string) $element['value'], $expected);
}
}
// Verify that custom #description properties are output.
foreach (['checkboxes', 'radios'] as $type) {
$elements = $this->xpath('//input[@id=:id]/following-sibling::div[@class=:class]', [
':id' => 'edit-' . $type . '-foo',
':class' => 'description',
]);
$this->assertTrue(count($elements), format_string('Custom %type option description found.', [
'%type' => $type,
]));
}
}
/**
* Tests wrapper ids for checkboxes and radios.
*/
public function testWrapperIds() {
$this->drupalGet('form-test/checkboxes-radios');
// Verify that wrapper id is different from element id.
foreach (['checkboxes', 'radios'] as $type) {
$element_ids = $this->xpath('//div[@id=:id]', [':id' => 'edit-' . $type]);
$wrapper_ids = $this->xpath('//fieldset[@id=:id]', [':id' => 'edit-' . $type . '--wrapper']);
$this->assertTrue(count($element_ids) == 1, format_string('A single element id found for type %type', ['%type' => $type]));
$this->assertTrue(count($wrapper_ids) == 1, format_string('A single wrapper id found for type %type', ['%type' => $type]));
}
}
/**
* Tests button classes.
*/
public function testButtonClasses() {
$this->drupalGet('form-test/button-class');
// Just contains(@class, "button") won't do because then
// "button--foo" would contain "button". Instead, check
// " button ". Make sure it matches in the beginning and the end too
// by adding a space before and after.
$this->assertEqual(2, count($this->xpath('//*[contains(concat(" ", @class, " "), " button ")]')));
$this->assertEqual(1, count($this->xpath('//*[contains(concat(" ", @class, " "), " button--foo ")]')));
$this->assertEqual(1, count($this->xpath('//*[contains(concat(" ", @class, " "), " button--danger ")]')));
}
/**
* Tests the #group property.
*/
public function testGroupElements() {
$this->drupalGet('form-test/group-details');
$elements = $this->xpath('//div[@class="details-wrapper"]//div[@class="details-wrapper"]//label');
$this->assertTrue(count($elements) == 1);
$this->drupalGet('form-test/group-container');
$elements = $this->xpath('//div[@id="edit-container"]//div[@class="details-wrapper"]//label');
$this->assertTrue(count($elements) == 1);
$this->drupalGet('form-test/group-fieldset');
$elements = $this->xpath('//fieldset[@id="edit-fieldset"]//div[@id="edit-meta"]//label');
$this->assertTrue(count($elements) == 1);
$this->drupalGet('form-test/group-vertical-tabs');
$elements = $this->xpath('//div[@data-vertical-tabs-panes]//details[@id="edit-meta"]//label');
$this->assertTrue(count($elements) == 1);
$elements = $this->xpath('//div[@data-vertical-tabs-panes]//details[@id="edit-meta-2"]//label');
$this->assertTrue(count($elements) == 1);
}
/**
* Tests the #required property on details and fieldset elements.
*/
public function testRequiredFieldsetsAndDetails() {
$this->drupalGet('form-test/group-details');
$this->assertFalse($this->cssSelect('summary.form-required'));
$this->drupalGet('form-test/group-details/1');
$this->assertTrue($this->cssSelect('summary.form-required'));
$this->drupalGet('form-test/group-fieldset');
$this->assertFalse($this->cssSelect('span.form-required'));
$this->drupalGet('form-test/group-fieldset/1');
$this->assertTrue($this->cssSelect('span.form-required'));
}
/**
* Tests a form with a autocomplete setting..
*/
public function testFormAutocomplete() {
$this->drupalGet('form-test/autocomplete');
$result = $this->xpath('//input[@id="edit-autocomplete-1" and contains(@data-autocomplete-path, "form-test/autocomplete-1")]');
$this->assertEqual(count($result), 0, 'Ensure that the user does not have access to the autocompletion');
$result = $this->xpath('//input[@id="edit-autocomplete-2" and contains(@data-autocomplete-path, "form-test/autocomplete-2/value")]');
$this->assertEqual(count($result), 0, 'Ensure that the user does not have access to the autocompletion');
$user = $this->drupalCreateUser(['access autocomplete test']);
$this->drupalLogin($user);
$this->drupalGet('form-test/autocomplete');
// Make sure that the autocomplete library is added.
$this->assertRaw('core/misc/autocomplete.js');
$result = $this->xpath('//input[@id="edit-autocomplete-1" and contains(@data-autocomplete-path, "form-test/autocomplete-1")]');
$this->assertEqual(count($result), 1, 'Ensure that the user does have access to the autocompletion');
$result = $this->xpath('//input[@id="edit-autocomplete-2" and contains(@data-autocomplete-path, "form-test/autocomplete-2/value")]');
$this->assertEqual(count($result), 1, 'Ensure that the user does have access to the autocompletion');
}
/**
* Tests form element error messages.
*/
public function testFormElementErrors() {
$this->drupalPostForm('form_test/details-form', [], 'Submit');
$this->assertText('I am an error on the details element.');
}
}
@@ -1,35 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\simpletest\WebTestBase;
/**
* Tests access control for form elements.
*
* @group Form
*/
class ElementsAccessTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('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.'));
}
}
@@ -1,150 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\simpletest\WebTestBase;
/**
* Tests form element labels, required markers and associated output.
*
* @group Form
*/
class ElementsLabelsTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
/**
* Test form elements, labels, title attributes and required marks output
* correctly and have the correct label option class if needed.
*/
public function testFormLabels() {
$this->drupalGet('form_test/form-labels');
// Check that the checkbox/radio processing is not interfering with
// basic placement.
$elements = $this->xpath('//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]');
$this->assertTrue(isset($elements[0]), 'Label follows field and label option class correct for regular checkboxes.');
// Make sure the label is rendered for checkboxes.
$elements = $this->xpath('//input[@id="edit-form-checkboxes-test-0"]/following-sibling::label[@for="edit-form-checkboxes-test-0" and @class="option"]');
$this->assertTrue(isset($elements[0]), 'Label 0 found checkbox.');
$elements = $this->xpath('//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]');
$this->assertTrue(isset($elements[0]), 'Label follows field and label option class correct for regular radios.');
// Make sure the label is rendered for radios.
$elements = $this->xpath('//input[@id="edit-form-radios-test-0"]/following-sibling::label[@for="edit-form-radios-test-0" and @class="option"]');
$this->assertTrue(isset($elements[0]), 'Label 0 found radios.');
// Exercise various defaults for checkboxes and modifications to ensure
// appropriate override and correct behavior.
$elements = $this->xpath('//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]');
$this->assertTrue(isset($elements[0]), 'Label follows field and label option class correct for a checkbox by default.');
// Exercise various defaults for textboxes and modifications to ensure
// appropriate override and correct behavior.
$elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required" and @class="js-form-required form-required"]/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]');
$this->assertTrue(isset($elements[0]), 'Label precedes textfield, with required marker inside label.');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required" and @class="js-form-required form-required"]');
$this->assertTrue(isset($elements[0]), 'Label tag with required marker precedes required textfield with no title.');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/preceding-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="visually-hidden"]');
$this->assertTrue(isset($elements[0]), 'Label preceding field and label class is visually-hidden.');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="js-form-required form-required"]');
$this->assertFalse(isset($elements[0]), 'No required marker on non-required field.');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]');
$this->assertTrue(isset($elements[0]), 'Label after field and label option class correct for text field.');
$elements = $this->xpath('//label[@for="edit-form-textfield-test-title-no-show"]');
$this->assertFalse(isset($elements[0]), 'No label tag when title set not to display.');
$elements = $this->xpath('//div[contains(@class, "js-form-item-form-textfield-test-title-invisible") and contains(@class, "form-no-label")]');
$this->assertTrue(isset($elements[0]), 'Field class is form-no-label when there is no label.');
// Check #field_prefix and #field_suffix placement.
$elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::div[@id="edit-form-radios-test"]');
$this->assertTrue(isset($elements[0]), 'Properly placed the #field_prefix element after the label and before the field.');
$elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::div[@id="edit-form-radios-test"]');
$this->assertTrue(isset($elements[0]), 'Properly places the #field_suffix element immediately after the form field.');
// Check #prefix and #suffix placement.
$elements = $this->xpath('//div[@id="form-test-textfield-title-prefix"]/following-sibling::div[contains(@class, \'js-form-item-form-textfield-test-title\')]');
$this->assertTrue(isset($elements[0]), 'Properly places the #prefix element before the form item.');
$elements = $this->xpath('//div[@id="form-test-textfield-title-suffix"]/preceding-sibling::div[contains(@class, \'js-form-item-form-textfield-test-title\')]');
$this->assertTrue(isset($elements[0]), 'Properly places the #suffix element before the form item.');
// Check title attribute for radios and checkboxes.
$elements = $this->xpath('//div[@id="edit-form-checkboxes-title-attribute"]');
$this->assertEqual($elements[0]['title'], 'Checkboxes test' . ' (' . t('Required') . ')', 'Title attribute found.');
$elements = $this->xpath('//div[@id="edit-form-radios-title-attribute"]');
$this->assertEqual($elements[0]['title'], 'Radios test' . ' (' . t('Required') . ')', 'Title attribute found.');
$elements = $this->xpath('//fieldset[@id="edit-form-checkboxes-title-invisible--wrapper"]/legend/span[contains(@class, "visually-hidden")]');
$this->assertTrue(!empty($elements), "Title/Label not displayed when 'visually-hidden' attribute is set in checkboxes.");
$elements = $this->xpath('//fieldset[@id="edit-form-radios-title-invisible--wrapper"]/legend/span[contains(@class, "visually-hidden")]');
$this->assertTrue(!empty($elements), "Title/Label not displayed when 'visually-hidden' attribute is set in radios.");
}
/**
* Tests different display options for form element descriptions.
*/
public function testFormDescriptions() {
$this->drupalGet('form_test/form-descriptions');
// Check #description placement with #description_display='after'.
$field_id = 'edit-form-textfield-test-description-after';
$description_id = $field_id . '--description';
$elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/following-sibling::div[@id="' . $description_id . '"]');
$this->assertTrue(isset($elements[0]), t('Properly places the #description element after the form item.'));
// Check #description placement with #description_display='before'.
$field_id = 'edit-form-textfield-test-description-before';
$description_id = $field_id . '--description';
$elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/preceding-sibling::div[@id="' . $description_id . '"]');
$this->assertTrue(isset($elements[0]), t('Properly places the #description element before the form item.'));
// Check if the class is 'visually-hidden' on the form element description
// for the option with #description_display='invisible' and also check that
// the description is placed after the form element.
$field_id = 'edit-form-textfield-test-description-invisible';
$description_id = $field_id . '--description';
$elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/following-sibling::div[contains(@class, "visually-hidden")]');
$this->assertTrue(isset($elements[0]), t('Properly renders the #description element visually-hidden.'));
}
/**
* Test forms in theme-less environments.
*/
public function testFormsInThemeLessEnvironments() {
$form = $this->getFormWithLimitedProperties();
$render_service = $this->container->get('renderer');
// This should not throw any notices.
$render_service->renderPlain($form);
}
/**
* Return a form with element with not all properties defined.
*/
protected function getFormWithLimitedProperties() {
$form = [];
$form['fieldset'] = [
'#type' => 'fieldset',
'#title' => 'Fieldset',
];
return $form;
}
}
@@ -1,89 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\WebTestBase;
use Drupal\Component\Serialization\Json;
/**
* Tests the vertical_tabs form element for expected behavior.
*
* @group Form
*/
class ElementsVerticalTabsTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
/**
* A user with permission to access vertical_tab_test_tabs.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* A normal user.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(['access vertical_tab_test tabs']);
$this->webUser = $this->drupalCreateUser();
$this->drupalLogin($this->adminUser);
}
/**
* Ensures that vertical-tabs.js is included before collapse.js.
*
* Otherwise, collapse.js adds "SHOW" or "HIDE" labels to the tabs.
*/
public function testJavaScriptOrdering() {
$this->drupalGet('form_test/vertical-tabs');
$position1 = strpos($this->content, 'core/misc/vertical-tabs.js');
$position2 = strpos($this->content, 'core/misc/collapse.js');
$this->assertTrue($position1 !== FALSE && $position2 !== FALSE && $position1 < $position2, 'vertical-tabs.js is included before collapse.js');
}
/**
* Ensures that vertical tab markup is not shown if user has no tab access.
*/
public function testWrapperNotShownWhenEmpty() {
// Test admin user can see vertical tabs and wrapper.
$this->drupalGet('form_test/vertical-tabs');
$wrapper = $this->xpath("//div[@data-vertical-tabs-panes]");
$this->assertTrue(isset($wrapper[0]), 'Vertical tab panes found.');
// Test wrapper markup not present for non-privileged web user.
$this->drupalLogin($this->webUser);
$this->drupalGet('form_test/vertical-tabs');
$wrapper = $this->xpath("//div[@data-vertical-tabs-panes]");
$this->assertFalse(isset($wrapper[0]), 'Vertical tab wrappers are not displayed to unprivileged users.');
}
/**
* Ensures that default vertical tab is correctly selected.
*/
public function testDefaultTab() {
$this->drupalGet('form_test/vertical-tabs');
$this->assertFieldByName('vertical_tabs__active_tab', 'edit-tab3', t('The default vertical tab is correctly selected.'));
}
/**
* Ensures that vertical tab form values are cleaned.
*/
public function testDefaultTabCleaned() {
$values = Json::decode($this->drupalPostForm('form_test/form-state-values-clean', [], t('Submit')));
$this->assertFalse(isset($values['vertical_tabs__active_tab']), SafeMarkup::format('%element was removed.', ['%element' => 'vertical_tabs__active_tab']));
}
}
@@ -1,49 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Component\Serialization\Json;
use Drupal\simpletest\WebTestBase;
/**
* Tests the form API email element.
*
* @group Form
*/
class EmailTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
protected $profile = 'testing';
/**
* Tests that #type 'email' fields are properly validated.
*/
public function testFormEmail() {
$edit = [];
$edit['email'] = 'invalid';
$edit['email_required'] = ' ';
$this->drupalPostForm('form-test/email', $edit, 'Submit');
$this->assertRaw(t('The email address %mail is not valid.', ['%mail' => 'invalid']));
$this->assertRaw(t('@name field is required.', ['@name' => 'Address']));
$edit = [];
$edit['email_required'] = ' foo.bar@example.com ';
$values = Json::decode($this->drupalPostForm('form-test/email', $edit, 'Submit'));
$this->assertIdentical($values['email'], '');
$this->assertEqual($values['email_required'], 'foo.bar@example.com');
$edit = [];
$edit['email'] = 'foo@example.com';
$edit['email_required'] = 'example@drupal.org';
$values = Json::decode($this->drupalPostForm('form-test/email', $edit, 'Submit'));
$this->assertEqual($values['email'], 'foo@example.com');
$this->assertEqual($values['email_required'], 'example@drupal.org');
}
}
@@ -1,103 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\User;
use Symfony\Component\HttpFoundation\Request;
/**
* Ensures that form actions can't be tricked into sending to external URLs.
*
* @group system
*/
class ExternalFormUrlTest extends KernelTestBase implements FormInterface {
/**
* {@inheritdoc}
*/
public static $modules = ['user', 'system'];
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'external_form_url_test';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['something'] = [
'#type' => 'textfield',
'#title' => 'What do you think?',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['key_value_expire', 'sequences']);
$this->installEntitySchema('user');
$test_user = User::create([
'name' => 'foobar',
'mail' => 'foobar@example.com',
]);
$test_user->save();
\Drupal::service('current_user')->setAccount($test_user);
}
/**
* Tests form behaviour.
*/
public function testActionUrlBehavior() {
// Create a new request which has a request uri with multiple leading
// slashes and make it the master request.
$request_stack = \Drupal::service('request_stack');
$original_request = $request_stack->pop();
$request = Request::create($original_request->getSchemeAndHttpHost() . '//example.org');
$request_stack->push($request);
$form = \Drupal::formBuilder()->getForm($this);
$markup = \Drupal::service('renderer')->renderRoot($form);
$this->setRawContent($markup);
$elements = $this->xpath('//form/@action');
$action = (string) $elements[0];
$this->assertEqual($original_request->getSchemeAndHttpHost() . '//example.org', $action);
// Create a new request which has a request uri with a single leading slash
// and make it the master request.
$request_stack = \Drupal::service('request_stack');
$original_request = $request_stack->pop();
$request = Request::create($original_request->getSchemeAndHttpHost() . '/example.org');
$request_stack->push($request);
$form = \Drupal::formBuilder()->getForm($this);
$markup = \Drupal::service('renderer')->renderRoot($form);
$this->setRawContent($markup);
$elements = $this->xpath('//form/@action');
$action = (string) $elements[0];
$this->assertEqual('/example.org', $action);
}
}
@@ -1,104 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Core\Form\FormState;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\Core\Session\UserSession;
use Drupal\simpletest\KernelTestBase;
/**
* Tests \Drupal::formBuilder()->setCache() and
* \Drupal::formBuilder()->getCache().
*
* @group Form
*/
class FormCacheTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'user');
/**
* @var string
*/
protected $formBuildId;
/**
* @var array
*/
protected $form;
/**
* @var array
*/
protected $formState;
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('key_value_expire'));
$this->formBuildId = $this->randomMachineName();
$this->form = array(
'#property' => $this->randomMachineName(),
);
$this->formState = new FormState();
$this->formState->set('example', $this->randomMachineName());
}
/**
* Tests the form cache with a logged-in user.
*/
function testCacheToken() {
\Drupal::currentUser()->setAccount(new UserSession(array('uid' => 1)));
\Drupal::formBuilder()->setCache($this->formBuildId, $this->form, $this->formState);
$cached_form_state = new FormState();
$cached_form = \Drupal::formBuilder()->getCache($this->formBuildId, $cached_form_state);
$this->assertEqual($this->form['#property'], $cached_form['#property']);
$this->assertTrue(!empty($cached_form['#cache_token']), 'Form has a cache token');
$this->assertEqual($this->formState->get('example'), $cached_form_state->get('example'));
// Test that the form cache isn't loaded when the session/token has changed.
// Change the private key. (We cannot change the session ID because this
// will break the parent site test runner batch.)
\Drupal::state()->set('system.private_key', 'invalid');
$cached_form_state = new FormState();
$cached_form = \Drupal::formBuilder()->getCache($this->formBuildId, $cached_form_state);
$this->assertFalse($cached_form, 'No form returned from cache');
$cached_form_state_example = $cached_form_state->get('example');
$this->assertTrue(empty($cached_form_state_example));
// Test that loading the cache with a different form_id fails.
$wrong_form_build_id = $this->randomMachineName(9);
$cached_form_state = new FormState();
$this->assertFalse(\Drupal::formBuilder()->getCache($wrong_form_build_id, $cached_form_state), 'No form returned from cache');
$cached_form_state_example = $cached_form_state->get('example');
$this->assertTrue(empty($cached_form_state_example), 'Cached form state was not loaded');
}
/**
* Tests the form cache without a logged-in user.
*/
function testNoCacheToken() {
// Switch to a anonymous user account.
$account_switcher = \Drupal::service('account_switcher');
$account_switcher->switchTo(new AnonymousUserSession());
$this->formState->set('example', $this->randomMachineName());
\Drupal::formBuilder()->setCache($this->formBuildId, $this->form, $this->formState);
$cached_form_state = new FormState();
$cached_form = \Drupal::formBuilder()->getCache($this->formBuildId, $cached_form_state);
$this->assertEqual($this->form['#property'], $cached_form['#property']);
$this->assertTrue(empty($cached_form['#cache_token']), 'Form has no cache token');
$this->assertEqual($this->formState->get('example'), $cached_form_state->get('example'));
// Restore user account.
$account_switcher->switchBack();
}
}
@@ -1,104 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\simpletest\KernelTestBase;
/**
* Tests automatically added form handlers.
*
* @group Form
*/
class FormDefaultHandlersTest extends KernelTestBase implements FormInterface {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['key_value_expire']);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'test_form_handlers';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['#validate'][] = '::customValidateForm';
$form['#submit'][] = '::customSubmitForm';
$form['submit'] = array('#type' => 'submit', '#value' => 'Save');
return $form;
}
/**
* {@inheritdoc}
*/
public function customValidateForm(array &$form, FormStateInterface $form_state) {
$test_handlers = $form_state->get('test_handlers');
$test_handlers['validate'][] = __FUNCTION__;
$form_state->set('test_handlers', $test_handlers);
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$test_handlers = $form_state->get('test_handlers');
$test_handlers['validate'][] = __FUNCTION__;
$form_state->set('test_handlers', $test_handlers);
}
/**
* {@inheritdoc}
*/
public function customSubmitForm(array &$form, FormStateInterface $form_state) {
$test_handlers = $form_state->get('test_handlers');
$test_handlers['submit'][] = __FUNCTION__;
$form_state->set('test_handlers', $test_handlers);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$test_handlers = $form_state->get('test_handlers');
$test_handlers['submit'][] = __FUNCTION__;
$form_state->set('test_handlers', $test_handlers);
}
/**
* Tests that default handlers are added even if custom are specified.
*/
function testDefaultAndCustomHandlers() {
$form_state = new FormState();
$form_builder = $this->container->get('form_builder');
$form_builder->submitForm($this, $form_state);
$handlers = $form_state->get('test_handlers');
$this->assertIdentical(count($handlers['validate']), 2);
$this->assertIdentical($handlers['validate'][0], 'customValidateForm');
$this->assertIdentical($handlers['validate'][1], 'validateForm');
$this->assertIdentical(count($handlers['submit']), 2);
$this->assertIdentical($handlers['submit'][0], 'customSubmitForm');
$this->assertIdentical($handlers['submit'][1], 'submitForm');
}
}
@@ -1,87 +0,0 @@
<?php
namespace Drupal\system\Tests\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 = array('form_test');
protected function setUp() {
parent::setUp();
$this->form = new FormTestObject($this->container->get('config.factory'));
$this->values = array(
'bananas' => array(
'#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()
*/
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, array('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, array('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, array('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);
}
}
@@ -1,114 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\simpletest\WebTestBase;
/**
* Tests form storage from cached pages.
*
* @group Form
*/
class FormStoragePageCacheTest extends WebTestBase {
/**
* @var array
*/
public static $modules = ['form_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$config = $this->config('system.performance');
$config->set('cache.page.max_age', 300);
$config->save();
}
/**
* Return the build id of the current form.
*/
protected function getFormBuildId() {
$build_id_fields = $this->xpath('//input[@name="form_build_id"]');
$this->assertEqual(count($build_id_fields), 1, 'One form build id field on the page');
return (string) $build_id_fields[0]['value'];
}
/**
* Build-id is regenerated when validating cached form.
*/
public function testValidateFormStorageOnCachedPage() {
$this->drupalGet('form-test/form-storage-page-cache');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_initial = $this->getFormBuildId();
// Trigger validation error by submitting an empty title.
$edit = ['title' => ''];
$this->drupalPostForm(NULL, $edit, 'Save');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_first_validation = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_first_validation, 'Build id changes when form validation fails');
// Trigger validation error by again submitting an empty title.
$edit = ['title' => ''];
$this->drupalPostForm(NULL, $edit, 'Save');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_second_validation = $this->getFormBuildId();
$this->assertEqual($build_id_first_validation, $build_id_second_validation, 'Build id remains the same when form validation fails subsequently');
// Repeat the test sequence but this time with a page loaded from the cache.
$this->drupalGet('form-test/form-storage-page-cache');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_from_cache_initial = $this->getFormBuildId();
$this->assertEqual($build_id_initial, $build_id_from_cache_initial, 'Build id is the same as on the first request');
// Trigger validation error by submitting an empty title.
$edit = ['title' => ''];
$this->drupalPostForm(NULL, $edit, 'Save');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_from_cache_first_validation = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_from_cache_first_validation, 'Build id changes when form validation fails');
$this->assertNotEqual($build_id_first_validation, $build_id_from_cache_first_validation, 'Build id from first user is not reused');
// Trigger validation error by again submitting an empty title.
$edit = ['title' => ''];
$this->drupalPostForm(NULL, $edit, 'Save');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_from_cache_second_validation = $this->getFormBuildId();
$this->assertEqual($build_id_from_cache_first_validation, $build_id_from_cache_second_validation, 'Build id remains the same when form validation fails subsequently');
}
/**
* Build-id is regenerated when rebuilding cached form.
*/
public function testRebuildFormStorageOnCachedPage() {
$this->drupalGet('form-test/form-storage-page-cache');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_initial = $this->getFormBuildId();
// Trigger rebuild, should regenerate build id. When a submit handler
// triggers a rebuild, the form is built twice in the same POST request,
// and during the second build, there is an old build ID, but because the
// form is not cached during the initial GET request, it is different from
// that initial build ID.
$edit = ['title' => 'something'];
$this->drupalPostForm(NULL, $edit, 'Rebuild');
$this->assertNoText('No old build id', 'There is no old build id on the page.');
$this->assertNoText($build_id_initial, 'The old build id is not the initial build id.');
$build_id_first_rebuild = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_first_rebuild, 'Build id changes on first rebuild.');
// Trigger subsequent rebuild, should regenerate the build id again.
$edit = ['title' => 'something'];
$this->drupalPostForm(NULL, $edit, 'Rebuild');
$this->assertText($build_id_first_rebuild, 'First build id as old build id on the page');
$build_id_second_rebuild = $this->getFormBuildId();
$this->assertNotEqual($build_id_first_rebuild, $build_id_second_rebuild, 'Build id changes on second rebuild.');
}
}
@@ -1,749 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormState;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\form_test\Form\FormTestDisabledElementsForm;
use Drupal\simpletest\WebTestBase;
use Drupal\user\RoleInterface;
use Drupal\filter\Entity\FilterFormat;
/**
* Tests various form element validation mechanisms.
*
* @group Form
*/
class FormTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['filter', 'form_test', 'file', 'datetime'];
protected function setUp() {
parent::setUp();
$filtered_html_format = FilterFormat::create([
'format' => 'filtered_html',
'name' => 'Filtered HTML',
]);
$filtered_html_format->save();
$filtered_html_permission = $filtered_html_format->getPermissionName();
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, [$filtered_html_permission]);
}
/**
* Check several empty values for required forms elements.
*
* Carriage returns, tabs, spaces, and unchecked checkbox elements are not
* valid content for a required field.
*
* If the form field is found in $form_state->getErrors() then the test pass.
*/
public function testRequiredFields() {
// Originates from https://www.drupal.org/node/117748.
// Sets of empty strings and arrays.
$empty_strings = ['""' => "", '"\n"' => "\n", '" "' => " ", '"\t"' => "\t", '" \n\t "' => " \n\t ", '"\n\n\n\n\n"' => "\n\n\n\n\n"];
$empty_arrays = ['array()' => []];
$empty_checkbox = [NULL];
$elements['textfield']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'textfield'];
$elements['textfield']['empty_values'] = $empty_strings;
$elements['telephone']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'tel'];
$elements['telephone']['empty_values'] = $empty_strings;
$elements['url']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'url'];
$elements['url']['empty_values'] = $empty_strings;
$elements['search']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'search'];
$elements['search']['empty_values'] = $empty_strings;
$elements['password']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'password'];
$elements['password']['empty_values'] = $empty_strings;
$elements['password_confirm']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'password_confirm'];
// Provide empty values for both password fields.
foreach ($empty_strings as $key => $value) {
$elements['password_confirm']['empty_values'][$key] = ['pass1' => $value, 'pass2' => $value];
}
$elements['textarea']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'textarea'];
$elements['textarea']['empty_values'] = $empty_strings;
$elements['radios']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'radios', '#options' => ['' => t('None'), $this->randomMachineName(), $this->randomMachineName(), $this->randomMachineName()]];
$elements['radios']['empty_values'] = $empty_arrays;
$elements['checkbox']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'checkbox', '#required' => TRUE];
$elements['checkbox']['empty_values'] = $empty_checkbox;
$elements['checkboxes']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'checkboxes', '#options' => [$this->randomMachineName(), $this->randomMachineName(), $this->randomMachineName()]];
$elements['checkboxes']['empty_values'] = $empty_arrays;
$elements['select']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'select', '#options' => ['' => t('None'), $this->randomMachineName(), $this->randomMachineName(), $this->randomMachineName()]];
$elements['select']['empty_values'] = $empty_strings;
$elements['file']['element'] = ['#title' => $this->randomMachineName(), '#type' => 'file'];
$elements['file']['empty_values'] = $empty_strings;
// Regular expression to find the expected marker on required elements.
$required_marker_preg = '@<.*?class=".*?js-form-required.*form-required.*?">@';
// Go through all the elements and all the empty values for them.
foreach ($elements as $type => $data) {
foreach ($data['empty_values'] as $key => $empty) {
foreach ([TRUE, FALSE] as $required) {
$form_id = $this->randomMachineName();
$form = [];
$form_state = new FormState();
$form['op'] = ['#type' => 'submit', '#value' => t('Submit')];
$element = $data['element']['#title'];
$form[$element] = $data['element'];
$form[$element]['#required'] = $required;
$user_input[$element] = $empty;
$user_input['form_id'] = $form_id;
$form_state->setUserInput($user_input);
$form_state->setFormObject(new StubForm($form_id, $form));
$form_state->setMethod('POST');
// The form token CSRF protection should not interfere with this test,
// so we bypass it by setting the token to FALSE.
$form['#token'] = FALSE;
\Drupal::formBuilder()->prepareForm($form_id, $form, $form_state);
\Drupal::formBuilder()->processForm($form_id, $form, $form_state);
$errors = $form_state->getErrors();
// Form elements of type 'radios' throw all sorts of PHP notices
// when you try to render them like this, so we ignore those for
// testing the required marker.
// @todo Fix this work-around (https://www.drupal.org/node/588438).
$form_output = ($type == 'radios') ? '' : \Drupal::service('renderer')->renderRoot($form);
if ($required) {
// Make sure we have a form error for this element.
$this->assertTrue(isset($errors[$element]), "Check empty($key) '$type' field '$element'");
if (!empty($form_output)) {
// Make sure the form element is marked as required.
$this->assertTrue(preg_match($required_marker_preg, $form_output), "Required '$type' field is marked as required");
}
}
else {
if (!empty($form_output)) {
// Make sure the form element is *not* marked as required.
$this->assertFalse(preg_match($required_marker_preg, $form_output), "Optional '$type' field is not marked as required");
}
if ($type == 'select') {
// Select elements are going to have validation errors with empty
// input, since those are illegal choices. Just make sure the
// error is not "field is required".
$this->assertTrue((empty($errors[$element]) || strpos('field is required', (string) $errors[$element]) === FALSE), "Optional '$type' field '$element' is not treated as a required element");
}
else {
// Make sure there is *no* form error for this element.
$this->assertTrue(empty($errors[$element]), "Optional '$type' field '$element' has no errors with empty input");
}
}
}
}
}
// Clear the expected form error messages so they don't appear as exceptions.
drupal_get_messages();
}
/**
* Tests validation for required checkbox, select, and radio elements.
*
* Submits a test form containing several types of form elements. The form
* is submitted twice, first without values for required fields and then
* with values. Each submission is checked for relevant error messages.
*
* @see \Drupal\form_test\Form\FormTestValidateRequiredForm
*/
public function testRequiredCheckboxesRadio() {
$form = \Drupal::formBuilder()->getForm('\Drupal\form_test\Form\FormTestValidateRequiredForm');
// Attempt to submit the form with no required fields set.
$edit = [];
$this->drupalPostForm('form-test/validate-required', $edit, 'Submit');
// The only error messages that should appear are the relevant 'required'
// messages for each field.
$expected = [];
foreach (['textfield', 'checkboxes', 'select', 'radios'] as $key) {
if (isset($form[$key]['#required_error'])) {
$expected[] = $form[$key]['#required_error'];
}
elseif (isset($form[$key]['#form_test_required_error'])) {
$expected[] = $form[$key]['#form_test_required_error'];
}
else {
$expected[] = t('@name field is required.', ['@name' => $form[$key]['#title']]);
}
}
// Check the page for error messages.
$errors = $this->xpath('//div[contains(@class, "error")]//li');
foreach ($errors as $error) {
$expected_key = array_search($error[0], $expected);
// If the error message is not one of the expected messages, fail.
if ($expected_key === FALSE) {
$this->fail(format_string("Unexpected error message: @error", ['@error' => $error[0]]));
}
// Remove the expected message from the list once it is found.
else {
unset($expected[$expected_key]);
}
}
// Fail if any expected messages were not found.
foreach ($expected as $not_found) {
$this->fail(format_string("Found error message: @error", ['@error' => $not_found]));
}
// Verify that input elements are still empty.
$this->assertFieldByName('textfield', '');
$this->assertNoFieldChecked('edit-checkboxes-foo');
$this->assertNoFieldChecked('edit-checkboxes-bar');
$this->assertOptionSelected('edit-select', '');
$this->assertNoFieldChecked('edit-radios-foo');
$this->assertNoFieldChecked('edit-radios-bar');
$this->assertNoFieldChecked('edit-radios-optional-foo');
$this->assertNoFieldChecked('edit-radios-optional-bar');
$this->assertNoFieldChecked('edit-radios-optional-default-value-false-foo');
$this->assertNoFieldChecked('edit-radios-optional-default-value-false-bar');
// Submit again with required fields set and verify that there are no
// error messages.
$edit = [
'textfield' => $this->randomString(),
'checkboxes[foo]' => TRUE,
'select' => 'foo',
'radios' => 'bar',
];
$this->drupalPostForm(NULL, $edit, 'Submit');
$this->assertNoFieldByXpath('//div[contains(@class, "error")]', FALSE, 'No error message is displayed when all required fields are filled.');
$this->assertRaw("The form_test_validate_required_form form was submitted successfully.", 'Validation form submitted successfully.');
}
/**
* Tests that input is retained for safe elements even with an invalid token.
*
* Submits a test form containing several types of form elements.
*/
public function testInputWithInvalidToken() {
// We need to be logged in to have CSRF tokens.
$account = $this->createUser();
$this->drupalLogin($account);
// Submit again with required fields set but an invalid form token and
// verify that all the values are retained.
$edit = [
'textfield' => $this->randomString(),
'checkboxes[bar]' => TRUE,
'select' => 'bar',
'radios' => 'foo',
'form_token' => 'invalid token',
];
$this->drupalPostForm(Url::fromRoute('form_test.validate_required'), $edit, 'Submit');
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
// Verify that input elements retained the posted values.
$this->assertFieldByName('textfield', $edit['textfield']);
$this->assertNoFieldChecked('edit-checkboxes-foo');
$this->assertFieldChecked('edit-checkboxes-bar');
$this->assertOptionSelected('edit-select', 'bar');
$this->assertFieldChecked('edit-radios-foo');
// Check another form that has a textarea input.
$edit = [
'textfield' => $this->randomString(),
'textarea' => $this->randomString() . "\n",
'form_token' => 'invalid token',
];
$this->drupalPostForm(Url::fromRoute('form_test.required'), $edit, 'Submit');
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
$this->assertFieldByName('textfield', $edit['textfield']);
$this->assertFieldByName('textarea', $edit['textarea']);
// Check another form that has a number input.
$edit = [
'integer_step' => mt_rand(1, 100),
'form_token' => 'invalid token',
];
$this->drupalPostForm(Url::fromRoute('form_test.number'), $edit, 'Submit');
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
$this->assertFieldByName('integer_step', $edit['integer_step']);
// Check a form with a Url field
$edit = [
'url' => $this->randomString(),
'form_token' => 'invalid token',
];
$this->drupalPostForm(Url::fromRoute('form_test.url'), $edit, 'Submit');
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
$this->assertFieldByName('url', $edit['url']);
}
/**
* CSRF tokens for GET forms should not be added by default.
*/
public function testGetFormsCsrfToken() {
// We need to be logged in to have CSRF tokens.
$account = $this->createUser();
$this->drupalLogin($account);
$this->drupalGet(Url::fromRoute('form_test.get_form'));
$this->assertNoRaw('form_token');
}
/**
* Tests validation for required textfield element without title.
*
* Submits a test form containing a textfield form element without title.
* The form is submitted twice, first without value for the required field
* and then with value. Each submission is checked for relevant error
* messages.
*
* @see \Drupal\form_test\Form\FormTestValidateRequiredNoTitleForm
*/
public function testRequiredTextfieldNoTitle() {
// Attempt to submit the form with no required field set.
$edit = [];
$this->drupalPostForm('form-test/validate-required-no-title', $edit, 'Submit');
$this->assertNoRaw("The form_test_validate_required_form_no_title form was submitted successfully.", 'Validation form submitted successfully.');
// Check the page for the error class on the textfield.
$this->assertFieldByXPath('//input[contains(@class, "error")]', FALSE, 'Error input form element class found.');
// Check the page for the aria-invalid attribute on the textfield.
$this->assertFieldByXPath('//input[contains(@aria-invalid, "true")]', FALSE, 'Aria invalid attribute found.');
// Submit again with required fields set and verify that there are no
// error messages.
$edit = [
'textfield' => $this->randomString(),
];
$this->drupalPostForm(NULL, $edit, 'Submit');
$this->assertNoFieldByXpath('//input[contains(@class, "error")]', FALSE, 'No error input form element class found.');
$this->assertRaw("The form_test_validate_required_form_no_title form was submitted successfully.", 'Validation form submitted successfully.');
}
/**
* Test default value handling for checkboxes.
*
* @see _form_test_checkbox()
*/
public function testCheckboxProcessing() {
// First, try to submit without the required checkbox.
$edit = [];
$this->drupalPostForm('form-test/checkbox', $edit, t('Submit'));
$this->assertRaw(t('@name field is required.', ['@name' => 'required_checkbox']), 'A required checkbox is actually mandatory');
// Now try to submit the form correctly.
$values = Json::decode($this->drupalPostForm(NULL, ['required_checkbox' => 1], t('Submit')));
$expected_values = [
'disabled_checkbox_on' => 'disabled_checkbox_on',
'disabled_checkbox_off' => '',
'checkbox_on' => 'checkbox_on',
'checkbox_off' => '',
'zero_checkbox_on' => '0',
'zero_checkbox_off' => '',
];
foreach ($expected_values as $widget => $expected_value) {
$this->assertEqual($values[$widget], $expected_value, format_string('Checkbox %widget returns expected value (expected: %expected, got: %value)', [
'%widget' => var_export($widget, TRUE),
'%expected' => var_export($expected_value, TRUE),
'%value' => var_export($values[$widget], TRUE),
]));
}
}
/**
* Tests validation of #type 'select' elements.
*/
public function testSelect() {
$form = \Drupal::formBuilder()->getForm('Drupal\form_test\Form\FormTestSelectForm');
$this->drupalGet('form-test/select');
// Verify that the options are escaped as expected.
$this->assertEscaped('<strong>four</strong>');
$this->assertNoRaw('<strong>four</strong>');
// Posting without any values should throw validation errors.
$this->drupalPostForm(NULL, [], 'Submit');
$no_errors = [
'select',
'select_required',
'select_optional',
'empty_value',
'empty_value_one',
'no_default_optional',
'no_default_empty_option_optional',
'no_default_empty_value_optional',
'multiple',
'multiple_no_default',
];
foreach ($no_errors as $key) {
$this->assertNoText(t('@name field is required.', ['@name' => $form[$key]['#title']]));
}
$expected_errors = [
'no_default',
'no_default_empty_option',
'no_default_empty_value',
'no_default_empty_value_one',
'multiple_no_default_required',
];
foreach ($expected_errors as $key) {
$this->assertText(t('@name field is required.', ['@name' => $form[$key]['#title']]));
}
// Post values for required fields.
$edit = [
'no_default' => 'three',
'no_default_empty_option' => 'three',
'no_default_empty_value' => 'three',
'no_default_empty_value_one' => 'three',
'multiple_no_default_required[]' => 'three',
];
$this->drupalPostForm(NULL, $edit, 'Submit');
$values = Json::decode($this->getRawContent());
// Verify expected values.
$expected = [
'select' => 'one',
'empty_value' => 'one',
'empty_value_one' => 'one',
'no_default' => 'three',
'no_default_optional' => 'one',
'no_default_optional_empty_value' => '',
'no_default_empty_option' => 'three',
'no_default_empty_option_optional' => '',
'no_default_empty_value' => 'three',
'no_default_empty_value_one' => 'three',
'no_default_empty_value_optional' => 0,
'multiple' => ['two' => 'two'],
'multiple_no_default' => [],
'multiple_no_default_required' => ['three' => 'three'],
];
foreach ($expected as $key => $value) {
$this->assertIdentical($values[$key], $value, format_string('@name: @actual is equal to @expected.', [
'@name' => $key,
'@actual' => var_export($values[$key], TRUE),
'@expected' => var_export($value, TRUE),
]));
}
}
/**
* Tests a select element when #options is not set.
*/
public function testEmptySelect() {
$this->drupalGet('form-test/empty-select');
$this->assertFieldByXPath("//select[1]", NULL, 'Select element found.');
$this->assertNoFieldByXPath("//select[1]/option", NULL, 'No option element found.');
}
/**
* Tests validation of #type 'number' and 'range' elements.
*/
public function testNumber() {
$form = \Drupal::formBuilder()->getForm('\Drupal\form_test\Form\FormTestNumberForm');
// Array with all the error messages to be checked.
$error_messages = [
'no_number' => '%name must be a number.',
'too_low' => '%name must be higher than or equal to %min.',
'too_high' => '%name must be lower than or equal to %max.',
'step_mismatch' => '%name is not a valid number.',
];
// The expected errors.
$expected = [
'integer_no_number' => 'no_number',
'integer_no_step' => 0,
'integer_no_step_step_error' => 'step_mismatch',
'integer_step' => 0,
'integer_step_error' => 'step_mismatch',
'integer_step_min' => 0,
'integer_step_min_error' => 'too_low',
'integer_step_max' => 0,
'integer_step_max_error' => 'too_high',
'integer_step_min_border' => 0,
'integer_step_max_border' => 0,
'integer_step_based_on_min' => 0,
'integer_step_based_on_min_error' => 'step_mismatch',
'float_small_step' => 0,
'float_step_no_error' => 0,
'float_step_error' => 'step_mismatch',
'float_step_hard_no_error' => 0,
'float_step_hard_error' => 'step_mismatch',
'float_step_any_no_error' => 0,
];
// First test the number element type, then range.
foreach (['form-test/number', 'form-test/number/range'] as $path) {
// Post form and show errors.
$this->drupalPostForm($path, [], 'Submit');
foreach ($expected as $element => $error) {
// Create placeholder array.
$placeholders = [
'%name' => $form[$element]['#title'],
'%min' => isset($form[$element]['#min']) ? $form[$element]['#min'] : '0',
'%max' => isset($form[$element]['#max']) ? $form[$element]['#max'] : '0',
];
foreach ($error_messages as $id => $message) {
// Check if the error exists on the page, if the current message ID is
// expected. Otherwise ensure that the error message is not present.
if ($id === $error) {
$this->assertRaw(format_string($message, $placeholders));
}
else {
$this->assertNoRaw(format_string($message, $placeholders));
}
}
}
}
}
/**
* Tests default value handling of #type 'range' elements.
*/
public function testRange() {
$values = json_decode($this->drupalPostForm('form-test/range', [], 'Submit'));
$this->assertEqual($values->with_default_value, 18);
$this->assertEqual($values->float, 10.5);
$this->assertEqual($values->integer, 6);
$this->assertEqual($values->offset, 6.9);
$this->drupalPostForm('form-test/range/invalid', [], 'Submit');
$this->assertFieldByXPath('//input[@type="range" and contains(@class, "error")]', NULL, 'Range element has the error class.');
}
/**
* Tests validation of #type 'color' elements.
*/
public function testColorValidation() {
// Keys are inputs, values are expected results.
$values = [
'' => '#000000',
'#000' => '#000000',
'AAA' => '#aaaaaa',
'#af0DEE' => '#af0dee',
'#99ccBc' => '#99ccbc',
'#aabbcc' => '#aabbcc',
'123456' => '#123456',
];
// Tests that valid values are properly normalized.
foreach ($values as $input => $expected) {
$edit = [
'color' => $input,
];
$result = json_decode($this->drupalPostForm('form-test/color', $edit, 'Submit'));
$this->assertEqual($result->color, $expected);
}
// Tests invalid values are rejected.
$values = ['#0008', '#1234', '#fffffg', '#abcdef22', '17', '#uaa'];
foreach ($values as $input) {
$edit = [
'color' => $input,
];
$this->drupalPostForm('form-test/color', $edit, 'Submit');
$this->assertRaw(t('%name must be a valid color.', ['%name' => 'Color']));
}
}
/**
* Test handling of disabled elements.
*
* @see _form_test_disabled_elements()
*/
public function testDisabledElements() {
// Get the raw form in its original state.
$form_state = new FormState();
$form = (new FormTestDisabledElementsForm())->buildForm([], $form_state);
// Build a submission that tries to hijack the form by submitting input for
// elements that are disabled.
$edit = [];
foreach (Element::children($form) as $key) {
if (isset($form[$key]['#test_hijack_value'])) {
if (is_array($form[$key]['#test_hijack_value'])) {
foreach ($form[$key]['#test_hijack_value'] as $subkey => $value) {
$edit[$key . '[' . $subkey . ']'] = $value;
}
}
else {
$edit[$key] = $form[$key]['#test_hijack_value'];
}
}
}
// Submit the form with no input, as the browser does for disabled elements,
// and fetch the $form_state->getValues() that is passed to the submit handler.
$this->drupalPostForm('form-test/disabled-elements', [], t('Submit'));
$returned_values['normal'] = Json::decode($this->content);
// Do the same with input, as could happen if JavaScript un-disables an
// element. drupalPostForm() emulates a browser by not submitting input for
// disabled elements, so we need to un-disable those elements first.
$this->drupalGet('form-test/disabled-elements');
$disabled_elements = [];
foreach ($this->xpath('//*[@disabled]') as $element) {
$disabled_elements[] = (string) $element['name'];
unset($element['disabled']);
}
// All the elements should be marked as disabled, including the ones below
// the disabled container.
$actual_count = count($disabled_elements);
$expected_count = 42;
$this->assertEqual($actual_count, $expected_count, SafeMarkup::format('Found @actual elements with disabled property (expected @expected).', [
'@actual' => count($disabled_elements),
'@expected' => $expected_count,
]));
$this->drupalPostForm(NULL, $edit, t('Submit'));
$returned_values['hijacked'] = Json::decode($this->content);
// Ensure that the returned values match the form's default values in both
// cases.
foreach ($returned_values as $values) {
$this->assertFormValuesDefault($values, $form);
}
}
/**
* Assert that the values submitted to a form matches the default values of the elements.
*/
public function assertFormValuesDefault($values, $form) {
foreach (Element::children($form) as $key) {
if (isset($form[$key]['#default_value'])) {
if (isset($form[$key]['#expected_value'])) {
$expected_value = $form[$key]['#expected_value'];
}
else {
$expected_value = $form[$key]['#default_value'];
}
if ($key == 'checkboxes_multiple') {
// Checkboxes values are not filtered out.
$values[$key] = array_filter($values[$key]);
}
$this->assertIdentical($expected_value, $values[$key], format_string('Default value for %type: expected %expected, returned %returned.', ['%type' => $key, '%expected' => var_export($expected_value, TRUE), '%returned' => var_export($values[$key], TRUE)]));
}
// Recurse children.
$this->assertFormValuesDefault($values, $form[$key]);
}
}
/**
* Verify markup for disabled form elements.
*
* @see _form_test_disabled_elements()
*/
public function testDisabledMarkup() {
$this->drupalGet('form-test/disabled-elements');
$form = \Drupal::formBuilder()->getForm('\Drupal\form_test\Form\FormTestDisabledElementsForm');
$type_map = [
'textarea' => 'textarea',
'select' => 'select',
'weight' => 'select',
'datetime' => 'datetime',
];
foreach ($form as $name => $item) {
// Skip special #types.
if (!isset($item['#type']) || in_array($item['#type'], ['hidden', 'text_format'])) {
continue;
}
// Setup XPath and CSS class depending on #type.
if (in_array($item['#type'], ['button', 'submit'])) {
$path = "//!type[contains(@class, :div-class) and @value=:value]";
$class = 'is-disabled';
}
elseif (in_array($item['#type'], ['image_button'])) {
$path = "//!type[contains(@class, :div-class) and @value=:value]";
$class = 'is-disabled';
}
else {
// starts-with() required for checkboxes.
$path = "//div[contains(@class, :div-class)]/descendant::!type[starts-with(@name, :name)]";
$class = 'form-disabled';
}
// Replace DOM element name in $path according to #type.
$type = 'input';
if (isset($type_map[$item['#type']])) {
$type = $type_map[$item['#type']];
}
$path = strtr($path, ['!type' => $type]);
// Verify that the element exists.
$element = $this->xpath($path, [
':name' => Html::escape($name),
':div-class' => $class,
':value' => isset($item['#value']) ? $item['#value'] : '',
]);
$this->assertTrue(isset($element[0]), format_string('Disabled form element class found for #type %type.', ['%type' => $item['#type']]));
}
// Verify special element #type text-format.
$element = $this->xpath('//div[contains(@class, :div-class)]/descendant::textarea[@name=:name]', [
':name' => 'text_format[value]',
':div-class' => 'form-disabled',
]);
$this->assertTrue(isset($element[0]), format_string('Disabled form element class found for #type %type.', ['%type' => 'text_format[value]']));
$element = $this->xpath('//div[contains(@class, :div-class)]/descendant::select[@name=:name]', [
':name' => 'text_format[format]',
':div-class' => 'form-disabled',
]);
$this->assertTrue(isset($element[0]), format_string('Disabled form element class found for #type %type.', ['%type' => 'text_format[format]']));
}
/**
* Test Form API protections against input forgery.
*
* @see _form_test_input_forgery()
*/
public function testInputForgery() {
$this->drupalGet('form-test/input-forgery');
$checkbox = $this->xpath('//input[@name="checkboxes[two]"]');
$checkbox[0]['value'] = 'FORGERY';
$this->drupalPostForm(NULL, ['checkboxes[one]' => TRUE, 'checkboxes[two]' => TRUE], t('Submit'));
$this->assertText('An illegal choice has been detected.', 'Input forgery was detected.');
}
/**
* Tests required attribute.
*/
public function testRequiredAttribute() {
$this->drupalGet('form-test/required-attribute');
$expected = 'required';
// Test to make sure the elements have the proper required attribute.
foreach (['textfield', 'password'] as $type) {
$element = $this->xpath('//input[@id=:id and @required=:expected]', [
':id' => 'edit-' . $type,
':expected' => $expected,
]);
$this->assertTrue(!empty($element), format_string('The @type has the proper required attribute.', ['@type' => $type]));
}
// Test to make sure textarea has the proper required attribute.
$element = $this->xpath('//textarea[@id=:id and @required=:expected]', [
':id' => 'edit-textarea',
':expected' => $expected,
]);
$this->assertTrue(!empty($element), 'The textarea has the proper required attribute.');
}
}
@@ -1,117 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Language\LanguageInterface;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\simpletest\WebTestBase;
/**
* Tests that the language select form element prints and submits the right
* options.
*
* @group Form
*/
class LanguageSelectElementTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test', 'language'];
/**
* Tests that the options printed by the language select element are correct.
*/
public function testLanguageSelectElementOptions() {
// Add some languages.
ConfigurableLanguage::create([
'id' => 'aaa',
'label' => $this->randomMachineName(),
])->save();
ConfigurableLanguage::create([
'id' => 'bbb',
'label' => $this->randomMachineName(),
])->save();
\Drupal::languageManager()->reset();
$this->drupalGet('form-test/language_select');
// Check that the language fields were rendered on the page.
$ids = [
'edit-languages-all' => LanguageInterface::STATE_ALL,
'edit-languages-configurable' => LanguageInterface::STATE_CONFIGURABLE,
'edit-languages-locked' => LanguageInterface::STATE_LOCKED,
'edit-languages-config-and-locked' => LanguageInterface::STATE_CONFIGURABLE | LanguageInterface::STATE_LOCKED
];
foreach ($ids as $id => $flags) {
$this->assertField($id, format_string('The @id field was found on the page.', ['@id' => $id]));
$options = [];
/* @var $language_manager \Drupal\Core\Language\LanguageManagerInterface */
$language_manager = $this->container->get('language_manager');
foreach ($language_manager->getLanguages($flags) as $langcode => $language) {
$options[$langcode] = $language->isLocked() ? t('- @name -', ['@name' => $language->getName()]) : $language->getName();
}
$this->_testLanguageSelectElementOptions($id, $options);
}
// Test that the #options were not altered by #languages.
$this->assertField('edit-language-custom-options', format_string('The @id field was found on the page.', ['@id' => 'edit-language-custom-options']));
$this->_testLanguageSelectElementOptions('edit-language-custom-options', ['opt1' => 'First option', 'opt2' => 'Second option', 'opt3' => 'Third option']);
}
/**
* Tests the case when the language select elements should not be printed.
*
* This happens when the language module is disabled.
*/
public function testHiddenLanguageSelectElement() {
// Disable the language module, so that the language select field will not
// be rendered.
$this->container->get('module_installer')->uninstall(['language']);
$this->drupalGet('form-test/language_select');
// Check that the language fields were rendered on the page.
$ids = ['edit-languages-all', 'edit-languages-configurable', 'edit-languages-locked', 'edit-languages-config-and-locked'];
foreach ($ids as $id) {
$this->assertNoField($id, format_string('The @id field was not found on the page.', ['@id' => $id]));
}
// Check that the submitted values were the default values of the language
// field elements.
$edit = [];
$this->drupalPostForm(NULL, $edit, t('Submit'));
$values = Json::decode($this->getRawContent());
$this->assertEqual($values['languages_all'], 'xx');
$this->assertEqual($values['languages_configurable'], 'en');
$this->assertEqual($values['languages_locked'], LanguageInterface::LANGCODE_NOT_SPECIFIED);
$this->assertEqual($values['languages_config_and_locked'], 'dummy_value');
$this->assertEqual($values['language_custom_options'], 'opt2');
}
/**
* Helper function to check the options of a language select form element.
*
* @param string $id
* The id of the language select element to check.
*
* @param array $options
* An array with options to compare with.
*/
protected function _testLanguageSelectElementOptions($id, $options) {
// Check that the options in the language field are exactly the same,
// including the order, as the languages sent as a parameter.
$elements = $this->xpath("//select[@id='" . $id . "']");
$count = 0;
foreach ($elements[0]->option as $option) {
$count++;
$option_title = current($options);
$this->assertEqual((string) $option, $option_title);
next($options);
}
$this->assertEqual($count, count($options), format_string('The number of languages and the number of options shown by the language element are the same: @languages languages, @number options', ['@languages' => count($options), '@number' => $count]));
}
}
@@ -1,54 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\simpletest\WebTestBase;
/**
* Tests \Drupal\system\Form\ModulesListForm.
*
* @group Form
*/
class ModulesListFormWebTest extends WebTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('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(
array('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');
}
}
@@ -1,119 +0,0 @@
<?php
namespace Drupal\system\Tests\Form;
use Drupal\Core\Form\FormState;
use Drupal\simpletest\WebTestBase;
/**
* Tests the programmatic form submission behavior.
*
* @group Form
*/
class ProgrammaticTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['form_test'];
/**
* Test the programmatic form submission workflow.
*/
public function testSubmissionWorkflow() {
// Backup the current batch status and reset it to avoid conflicts while
// processing the dummy form submit handler.
$current_batch = $batch =& batch_get();
$batch = [];
// Test that a programmatic form submission is rejected when a required
// textfield is omitted and correctly processed when it is provided.
$this->submitForm([], FALSE);
$this->submitForm(['textfield' => 'test 1'], TRUE);
$this->submitForm([], FALSE);
$this->submitForm(['textfield' => 'test 2'], TRUE);
// Test that a programmatic form submission can turn on and off checkboxes
// which are, by default, checked.
$this->submitForm(['textfield' => 'dummy value', 'checkboxes' => [1 => 1, 2 => 2]], TRUE);
$this->submitForm(['textfield' => 'dummy value', 'checkboxes' => [1 => 1, 2 => NULL]], TRUE);
$this->submitForm(['textfield' => 'dummy value', 'checkboxes' => [1 => NULL, 2 => 2]], TRUE);
$this->submitForm(['textfield' => 'dummy value', 'checkboxes' => [1 => NULL, 2 => NULL]], TRUE);
// Test that a programmatic form submission can correctly click a button
// that limits validation errors based on user input. Since we do not
// submit any values for "textfield" here and the textfield is required, we
// only expect form validation to pass when validation is limited to a
// different field.
$this->submitForm(['op' => 'Submit with limited validation', 'field_to_validate' => 'all'], FALSE);
$this->submitForm(['op' => 'Submit with limited validation', 'field_to_validate' => 'textfield'], FALSE);
$this->submitForm(['op' => 'Submit with limited validation', 'field_to_validate' => 'field_to_validate'], TRUE);
// Restore the current batch status.
$batch = $current_batch;
}
/**
* Helper function used to programmatically submit the form defined in
* form_test.module with the given values.
*
* @param $values
* An array of field values to be submitted.
* @param $valid_input
* A boolean indicating whether or not the form submission is expected to
* be valid.
*/
private function submitForm($values, $valid_input) {
// Programmatically submit the given values.
$form_state = (new FormState())->setValues($values);
\Drupal::formBuilder()->submitForm('\Drupal\form_test\Form\FormTestProgrammaticForm', $form_state);
// Check that the form returns an error when expected, and vice versa.
$errors = $form_state->getErrors();
$valid_form = empty($errors);
$args = [
'%values' => print_r($values, TRUE),
'%errors' => $valid_form ? t('None') : implode(' ', $errors),
];
$this->assertTrue($valid_input == $valid_form, format_string('Input values: %values<br />Validation handler errors: %errors', $args));
// We check submitted values only if we have a valid input.
if ($valid_input) {
// Fetching the values that were set in the submission handler.
$stored_values = $form_state->get('programmatic_form_submit');
foreach ($values as $key => $value) {
$this->assertEqual($stored_values[$key], $value, format_string('Submission handler correctly executed: %stored_key is %stored_value', ['%stored_key' => $key, '%stored_value' => print_r($value, TRUE)]));
}
}
}
/**
* Test the programmed_bypass_access_check flag.
*/
public function testProgrammaticAccessBypass() {
$form_state = (new FormState())->setValues([
'textfield' => 'dummy value',
'field_restricted' => 'dummy value'
]);
// Programmatically submit the form with a value for the restricted field.
// Since programmed_bypass_access_check is set to TRUE by default, the
// field is accessible and can be set.
\Drupal::formBuilder()->submitForm('\Drupal\form_test\Form\FormTestProgrammaticForm', $form_state);
$values = $form_state->get('programmatic_form_submit');
$this->assertEqual($values['field_restricted'], 'dummy value', 'The value for the restricted field is stored correctly.');
// Programmatically submit the form with a value for the restricted field
// with programmed_bypass_access_check set to FALSE. Since access
// restrictions apply, the restricted field is inaccessible, and the value
// should not be stored.
$form_state->setProgrammedBypassAccessCheck(FALSE);
\Drupal::formBuilder()->submitForm('\Drupal\form_test\Form\FormTestProgrammaticForm', $form_state);
$values = $form_state->get('programmatic_form_submit');
$this->assertNotEqual($values['field_restricted'], 'dummy value', 'The value for the restricted field is not stored.');
}
}

Some files were not shown because too many files have changed in this diff Show More