updated core
This commit is contained in:
@@ -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 /> </div>', 'Initial progress message appears correctly.');
|
||||
$this->assertNoRaw('&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('&', '&', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2'))) . '&' . $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('&', '&', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2'))) . '&' . $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: <script>', 't replaces and escapes string.');
|
||||
$text = t('Placeholder text: %value', array('%value' => '<script>'));
|
||||
$this->assertEqual($text, 'Placeholder text: <em class="placeholder"><script></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&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);
|
||||
}
|
||||
|
||||
}
|
||||
-515
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
-20
@@ -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';
|
||||
}
|
||||
|
||||
}
|
||||
-42
@@ -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
Reference in New Issue
Block a user