updated core to 8.6.1 via composer

This commit is contained in:
2018-09-12 13:58:26 +02:00
parent a9a219f2ed
commit ea56b9fba3
4443 changed files with 112098 additions and 40708 deletions
@@ -335,11 +335,11 @@ class DbUpdateController extends ControllerBase {
// Warn the user if any updates were incompatible.
if ($incompatible_updates_exist) {
drupal_set_message($this->t('Some of the pending updates cannot be applied because their dependencies were not met.'), 'warning');
$this->messenger()->addWarning($this->t('Some of the pending updates cannot be applied because their dependencies were not met.'));
}
if (empty($count)) {
drupal_set_message($this->t('No pending updates.'));
$this->messenger()->addStatus($this->t('No pending updates.'));
unset($build);
$build['links'] = [
'#theme' => 'links',
@@ -524,7 +524,7 @@ class DbUpdateController extends ControllerBase {
$build['status_report'] = [
'#type' => 'status_report',
'#requirements' => $requirements,
'#suffix' => $this->t('Check the messages and <a href=":url">try again</a>.', [':url' => $try_again_url])
'#suffix' => $this->t('Check the messages and <a href=":url">try again</a>.', [':url' => $try_again_url]),
];
$build['#title'] = $this->t('Requirements problem');
@@ -4,7 +4,6 @@ namespace Drupal\system\Controller;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Tags;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityAutocompleteMatcher;
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
@@ -81,7 +80,7 @@ class EntityAutocompleteController extends ControllerBase {
// Get the typed string from the URL, if it exists.
if ($input = $request->query->get('q')) {
$typed_string = Tags::explode($input);
$typed_string = Unicode::strtolower(array_pop($typed_string));
$typed_string = mb_strtolower(array_pop($typed_string));
// Selection settings are passed in as a hashed key of a serialized array
// stored in the key/value store.
@@ -100,7 +100,7 @@ class SystemController extends ControllerBase {
public function overview($link_id) {
// Check for status report errors.
if ($this->systemManager->checkRequirements() && $this->currentUser()->hasPermission('administer site configuration')) {
drupal_set_message($this->t('One or more problems were detected with your Drupal installation. Check the <a href=":status">status report</a> for more information.', [':status' => $this->url('system.status')]), 'error');
$this->messenger()->addError($this->t('One or more problems were detected with your Drupal installation. Check the <a href=":status">status report</a> for more information.', [':status' => $this->url('system.status')]));
}
// Load all menu links below it.
$parameters = new MenuTreeParameters();
@@ -187,7 +187,7 @@ class SystemController extends ControllerBase {
uasort($themes, 'system_sort_modules_by_info_name');
$theme_default = $config->get('default');
$theme_groups = ['installed' => [], 'uninstalled' => []];
$theme_groups = ['installed' => [], 'uninstalled' => []];
$admin_theme = $config->get('admin');
$admin_theme_options = [];
@@ -71,15 +71,15 @@ class ThemeController extends ControllerBase {
if (!empty($themes[$theme])) {
// Do not uninstall the default or admin theme.
if ($theme === $config->get('default') || $theme === $config->get('admin')) {
drupal_set_message($this->t('%theme is the default theme and cannot be uninstalled.', ['%theme' => $themes[$theme]->info['name']]), 'error');
$this->messenger()->addError($this->t('%theme is the default theme and cannot be uninstalled.', ['%theme' => $themes[$theme]->info['name']]));
}
else {
$this->themeHandler->uninstall([$theme]);
drupal_set_message($this->t('The %theme theme has been uninstalled.', ['%theme' => $themes[$theme]->info['name']]));
$this->messenger()->addStatus($this->t('The %theme theme has been uninstalled.', ['%theme' => $themes[$theme]->info['name']]));
}
}
else {
drupal_set_message($this->t('The %theme theme was not found.', ['%theme' => $theme]), 'error');
$this->messenger()->addError($this->t('The %theme theme was not found.', ['%theme' => $theme]));
}
return $this->redirect('system.themes_page');
@@ -108,15 +108,15 @@ class ThemeController extends ControllerBase {
try {
if ($this->themeHandler->install([$theme])) {
$themes = $this->themeHandler->listInfo();
drupal_set_message($this->t('The %theme theme has been installed.', ['%theme' => $themes[$theme]->info['name']]));
$this->messenger()->addStatus($this->t('The %theme theme has been installed.', ['%theme' => $themes[$theme]->info['name']]));
}
else {
drupal_set_message($this->t('The %theme theme was not found.', ['%theme' => $theme]), 'error');
$this->messenger()->addError($this->t('The %theme theme was not found.', ['%theme' => $theme]));
}
}
catch (PreExistingConfigException $e) {
$config_objects = $e->flattenConfigObjects($e->getConfigObjects());
drupal_set_message(
$this->messenger()->addError(
$this->formatPlural(
count($config_objects),
'Unable to install @extension, %config_names already exists in active configuration.',
@@ -124,12 +124,11 @@ class ThemeController extends ControllerBase {
[
'%config_names' => implode(', ', $config_objects),
'@extension' => $theme,
]),
'error'
])
);
}
catch (UnmetDependenciesException $e) {
drupal_set_message($e->getTranslatedMessage($this->getStringTranslation(), $theme), 'error');
$this->messenger()->addError($e->getTranslatedMessage($this->getStringTranslation(), $theme));
}
return $this->redirect('system.themes_page');
@@ -171,17 +170,18 @@ class ThemeController extends ControllerBase {
// theme.
$admin_theme = $config->get('admin');
if ($admin_theme != 0 && $admin_theme != $theme) {
drupal_set_message($this->t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', [
'%admin_theme' => $themes[$admin_theme]->info['name'],
'%selected_theme' => $themes[$theme]->info['name'],
]));
$this->messenger()
->addStatus($this->t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', [
'%admin_theme' => $themes[$admin_theme]->info['name'],
'%selected_theme' => $themes[$theme]->info['name'],
]));
}
else {
drupal_set_message($this->t('%theme is now the default theme.', ['%theme' => $themes[$theme]->info['name']]));
$this->messenger()->addStatus($this->t('%theme is now the default theme.', ['%theme' => $themes[$theme]->info['name']]));
}
}
else {
drupal_set_message($this->t('The %theme theme was not found.', ['%theme' => $theme]), 'error');
$this->messenger()->addError($this->t('The %theme theme was not found.', ['%theme' => $theme]));
}
return $this->redirect('system.themes_page');
+2 -3
View File
@@ -36,7 +36,6 @@ class CronController extends ControllerBase {
return new static($container->get('cron'));
}
/**
* Run Cron once.
*
@@ -58,10 +57,10 @@ class CronController extends ControllerBase {
*/
public function runManually() {
if ($this->cron->run()) {
drupal_set_message($this->t('Cron ran successfully.'));
$this->messenger()->addStatus($this->t('Cron ran successfully.'));
}
else {
drupal_set_message($this->t('Cron run failed.'), 'error');
$this->messenger()->addError($this->t('Cron run failed.'));
}
return $this->redirect('system.status');
@@ -15,6 +15,13 @@ use Drupal\Component\Plugin\ConfigurablePluginInterface;
* @ConfigEntityType(
* id = "action",
* label = @Translation("Action"),
* label_collection = @Translation("Actions"),
* label_singular = @Translation("action"),
* label_plural = @Translation("actions"),
* label_count = @PluralTranslation(
* singular = "@count action",
* plural = "@count actions",
* ),
* admin_permission = "administer actions",
* entity_keys = {
* "id" = "id",
+47
View File
@@ -3,6 +3,7 @@
namespace Drupal\system\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\system\MenuInterface;
/**
@@ -11,6 +12,13 @@ use Drupal\system\MenuInterface;
* @ConfigEntityType(
* id = "menu",
* label = @Translation("Menu"),
* label_collection = @Translation("Menus"),
* label_singular = @Translation("menu"),
* label_plural = @Translation("menus"),
* label_count = @PluralTranslation(
* singular = "@count menu",
* plural = "@count menus",
* ),
* handlers = {
* "access" = "Drupal\system\MenuAccessControlHandler"
* },
@@ -71,4 +79,43 @@ class Menu extends ConfigEntityBase implements MenuInterface {
return (bool) $this->locked;
}
/**
* {@inheritdoc}
*/
public static function preDelete(EntityStorageInterface $storage, array $entities) {
parent::preDelete($storage, $entities);
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
foreach ($entities as $menu) {
// Delete all links from the menu.
$menu_link_manager->deleteLinksInMenu($menu->id());
}
}
/**
* {@inheritdoc}
*/
public function save() {
$return = parent::save();
\Drupal::cache('menu')->invalidateAll();
// Invalidate the block cache to update menu-based derivatives.
if (\Drupal::moduleHandler()->moduleExists('block')) {
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
}
return $return;
}
/**
* {@inheritdoc}
*/
public function delete() {
parent::delete();
\Drupal::cache('menu')->invalidateAll();
// Invalidate the block cache to update menu-based derivatives.
if (\Drupal::moduleHandler()->moduleExists('block')) {
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
}
}
}
+3 -3
View File
@@ -155,7 +155,7 @@ class CronForm extends FormBase {
$this->config('system.cron')
->set('logging', $form_state->getValue('logging'))
->save();
drupal_set_message(t('The configuration options have been saved.'));
$this->messenger()->addStatus(t('The configuration options have been saved.'));
}
/**
@@ -163,10 +163,10 @@ class CronForm extends FormBase {
*/
public function runCron(array &$form, FormStateInterface $form_state) {
if ($this->cron->run()) {
drupal_set_message($this->t('Cron ran successfully.'));
$this->messenger()->addStatus($this->t('Cron ran successfully.'));
}
else {
drupal_set_message($this->t('Cron run failed.'), 'error');
$this->messenger()->addError($this->t('Cron run failed.'));
}
}
@@ -129,7 +129,7 @@ abstract class DateFormatFormBase extends EntityForm {
$pattern = trim($form_state->getValue('date_format_pattern'));
foreach ($this->dateFormatStorage->loadMultiple() as $format) {
if ($format->getPattern() == $pattern && ($format->id() == $this->entity->id())) {
drupal_set_message(t('The existing format/name combination has not been altered.'));
$this->messenger()->addStatus($this->t('The existing format/name combination has not been altered.'));
continue;
}
}
@@ -149,10 +149,10 @@ abstract class DateFormatFormBase extends EntityForm {
public function save(array $form, FormStateInterface $form_state) {
$status = $this->entity->save();
if ($status == SAVED_UPDATED) {
drupal_set_message(t('Custom date format updated.'));
$this->messenger()->addStatus($this->t('Custom date format updated.'));
}
else {
drupal_set_message(t('Custom date format added.'));
$this->messenger()->addStatus($this->t('Custom date format added.'));
}
$form_state->setRedirectUrl($this->entity->urlInfo('collection'));
}
@@ -174,29 +174,27 @@ class ModulesListConfirmForm extends ConfirmFormBase {
}
catch (PreExistingConfigException $e) {
$config_objects = $e->flattenConfigObjects($e->getConfigObjects());
drupal_set_message(
$this->messenger()->addError(
$this->formatPlural(
count($config_objects),
'Unable to install @extension, %config_names already exists in active configuration.',
'Unable to install @extension, %config_names already exist in active configuration.',
[
'%config_names' => implode(', ', $config_objects),
'@extension' => $this->modules['install'][$e->getExtension()]
]),
'error'
'@extension' => $this->modules['install'][$e->getExtension()],
])
);
return;
}
catch (UnmetDependenciesException $e) {
drupal_set_message(
$e->getTranslatedMessage($this->getStringTranslation(), $this->modules['install'][$e->getExtension()]),
'error'
$this->messenger()->addError(
$e->getTranslatedMessage($this->getStringTranslation(), $this->modules['install'][$e->getExtension()])
);
return;
}
$module_names = array_values($this->modules['install']);
drupal_set_message($this->formatPlural(count($module_names), 'Module %name has been enabled.', '@count modules have been enabled: %names.', [
$this->messenger()->addStatus($this->formatPlural(count($module_names), 'Module %name has been enabled.', '@count modules have been enabled: %names.', [
'%name' => $module_names[0],
'%names' => implode(', ', $module_names),
]));
@@ -27,7 +27,7 @@ class ModulesListExperimentalConfirmForm extends ModulesListConfirmForm {
* {@inheritdoc}
*/
protected function buildMessageList() {
drupal_set_message($this->t('<a href=":url">Experimental modules</a> are provided for testing purposes only. Use at your own risk.', [':url' => 'https://www.drupal.org/core/experimental']), 'warning');
$this->messenger()->addWarning($this->t('<a href=":url">Experimental modules</a> are provided for testing purposes only. Use at your own risk.', [':url' => 'https://www.drupal.org/core/experimental']));
$items = parent::buildMessageList();
// Add the list of experimental modules after any other messages.
@@ -450,30 +450,28 @@ class ModulesListForm extends FormBase {
try {
$this->moduleInstaller->install(array_keys($modules['install']));
$module_names = array_values($modules['install']);
drupal_set_message($this->formatPlural(count($module_names), 'Module %name has been enabled.', '@count modules have been enabled: %names.', [
$this->messenger()->addStatus($this->formatPlural(count($module_names), 'Module %name has been enabled.', '@count modules have been enabled: %names.', [
'%name' => $module_names[0],
'%names' => implode(', ', $module_names),
]));
}
catch (PreExistingConfigException $e) {
$config_objects = $e->flattenConfigObjects($e->getConfigObjects());
drupal_set_message(
$this->messenger()->addError(
$this->formatPlural(
count($config_objects),
'Unable to install @extension, %config_names already exists in active configuration.',
'Unable to install @extension, %config_names already exist in active configuration.',
[
'%config_names' => implode(', ', $config_objects),
'@extension' => $modules['install'][$e->getExtension()]
]),
'error'
'@extension' => $modules['install'][$e->getExtension()],
])
);
return;
}
catch (UnmetDependenciesException $e) {
drupal_set_message(
$e->getTranslatedMessage($this->getStringTranslation(), $modules['install'][$e->getExtension()]),
'error'
$this->messenger()->addError(
$e->getTranslatedMessage($this->getStringTranslation(), $modules['install'][$e->getExtension()])
);
return;
}
@@ -131,7 +131,7 @@ class ModulesUninstallConfirmForm extends ConfirmFormBase {
// Prevent this page from showing when the module list is empty.
if (empty($this->modules)) {
drupal_set_message($this->t('The selected modules could not be uninstalled, either due to a website problem or due to the uninstall confirmation form timing out. Please try again.'), 'error');
$this->messenger()->addError($this->t('The selected modules could not be uninstalled, either due to a website problem or due to the uninstall confirmation form timing out. Please try again.'));
return $this->redirect('system.modules_uninstall');
}
@@ -161,7 +161,7 @@ class ModulesUninstallConfirmForm extends ConfirmFormBase {
// Uninstall the modules.
$this->moduleInstaller->uninstall($this->modules);
drupal_set_message($this->t('The selected modules have been uninstalled.'));
$this->messenger()->addStatus($this->t('The selected modules have been uninstalled.'));
$form_state->setRedirectUrl($this->getCancelUrl());
}
@@ -116,8 +116,6 @@ class ModulesUninstallForm extends FormBase {
return $form;
}
$profile = drupal_get_profile();
// Sort all modules by their name.
uasort($uninstallable, 'system_sort_modules_by_info_name');
$validation_reasons = $this->moduleInstaller->validateUninstall(array_keys($uninstallable));
@@ -142,10 +140,9 @@ class ModulesUninstallForm extends FormBase {
$form['uninstall'][$module->getName()]['#disabled'] = TRUE;
}
// All modules which depend on this one must be uninstalled first, before
// we can allow this module to be uninstalled. (The installation profile
// is excluded from this list.)
// we can allow this module to be uninstalled.
foreach (array_keys($module->required_by) as $dependent) {
if ($dependent != $profile && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED) {
if (drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED) {
$name = isset($modules[$dependent]->info['name']) ? $modules[$dependent]->info['name'] : $dependent;
$form['modules'][$module->getName()]['#required_by'][] = $name;
$form['uninstall'][$module->getName()]['#disabled'] = TRUE;
@@ -3,10 +3,12 @@
namespace Drupal\system\Form;
use Drupal\Core\Asset\AssetCollectionOptimizerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -37,6 +39,13 @@ class PerformanceForm extends ConfigFormBase {
*/
protected $jsCollectionOptimizer;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a PerformanceForm object.
*
@@ -48,13 +57,16 @@ class PerformanceForm extends ConfigFormBase {
* The CSS asset collection optimizer service.
* @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $js_collection_optimizer
* The JavaScript asset collection optimizer service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(ConfigFactoryInterface $config_factory, DateFormatterInterface $date_formatter, AssetCollectionOptimizerInterface $css_collection_optimizer, AssetCollectionOptimizerInterface $js_collection_optimizer) {
public function __construct(ConfigFactoryInterface $config_factory, DateFormatterInterface $date_formatter, AssetCollectionOptimizerInterface $css_collection_optimizer, AssetCollectionOptimizerInterface $js_collection_optimizer, ModuleHandlerInterface $module_handler) {
parent::__construct($config_factory);
$this->dateFormatter = $date_formatter;
$this->cssCollectionOptimizer = $css_collection_optimizer;
$this->jsCollectionOptimizer = $js_collection_optimizer;
$this->moduleHandler = $module_handler;
}
/**
@@ -65,7 +77,8 @@ class PerformanceForm extends ConfigFormBase {
$container->get('config.factory'),
$container->get('date.formatter'),
$container->get('asset.css.collection_optimizer'),
$container->get('asset.js.collection_optimizer')
$container->get('asset.js.collection_optimizer'),
$container->get('module_handler')
);
}
@@ -107,7 +120,6 @@ class PerformanceForm extends ConfigFormBase {
'#type' => 'details',
'#title' => t('Caching'),
'#open' => TRUE,
'#description' => $this->t('Note: Drupal provides an internal page cache module that is recommended for small to medium-sized websites.'),
];
// Identical options to the ones for block caching.
// @see \Drupal\Core\Block\BlockBase::buildConfigurationForm()
@@ -116,10 +128,14 @@ class PerformanceForm extends ConfigFormBase {
$period[0] = '<' . t('no caching') . '>';
$form['caching']['page_cache_maximum_age'] = [
'#type' => 'select',
'#title' => t('Page cache maximum age'),
'#title' => t('Browser and proxy cache maximum age'),
'#default_value' => $config->get('cache.page.max_age'),
'#options' => $period,
'#description' => t('The maximum time a page can be cached by browsers and proxies. This is used as the value for max-age in Cache-Control headers.'),
'#description' => t('This is used as the value for max-age in Cache-Control headers.'),
];
$form['caching']['internal_page_cache'] = [
'#markup' => $this->t('Drupal provides an <a href=":module_enable">Internal Page Cache module</a> that is recommended for small to medium-sized websites.', [':module_enable' => Url::fromRoute('system.modules_list')->toString()]),
'#access' => !$this->moduleHandler->moduleExists('page_cache'),
];
$directory = 'public://';
@@ -174,7 +190,7 @@ class PerformanceForm extends ConfigFormBase {
*/
public function submitCacheClear(array &$form, FormStateInterface $form_state) {
drupal_flush_all_caches();
drupal_set_message(t('Caches cleared.'));
$this->messenger()->addStatus($this->t('Caches cleared.'));
}
}
@@ -159,7 +159,7 @@ class PrepareModulesEntityUninstallForm extends ConfirmFormBase {
'@entity_type_singular' => $entity_type->getSingularLabel(),
'@entity_type_plural' => $entity_type->getPluralLabel(),
]
)
),
];
}
@@ -250,7 +250,7 @@ class PrepareModulesEntityUninstallForm extends ConfirmFormBase {
*/
public static function moduleBatchFinished($success, $results, $operations) {
$entity_type_plural = \Drupal::entityTypeManager()->getDefinition($results['entity_type_id'])->getPluralLabel();
drupal_set_message(t('All @entity_type_plural have been deleted.', ['@entity_type_plural' => $entity_type_plural]));
\Drupal::messenger()->addStatus(t('All @entity_type_plural have been deleted.', ['@entity_type_plural' => $entity_type_plural]));
return new RedirectResponse(Url::fromRoute('system.modules_uninstall')->setAbsolute()->toString());
}
@@ -125,7 +125,7 @@ class RegionalForm extends ConfigFormBase {
'#type' => 'checkbox',
'#title' => t('Remind users at login if their time zone is not set'),
'#default_value' => $system_date->get('timezone.user.warn'),
'#description' => t('Only applied if users may set their own time zone.')
'#description' => t('Only applied if users may set their own time zone.'),
];
$form['timezone']['configurable_timezones_wrapper']['user_default_timezone'] = [
@@ -137,7 +137,7 @@ class RegionalForm extends ConfigFormBase {
DRUPAL_USER_TIMEZONE_EMPTY => t('Empty time zone'),
DRUPAL_USER_TIMEZONE_SELECT => t('Users may set their own time zone at registration'),
],
'#description' => t('Only applied if users may set their own time zone.')
'#description' => t('Only applied if users may set their own time zone.'),
];
return parent::buildForm($form, $form_state);
@@ -35,7 +35,7 @@ class RssFeedsForm extends ConfigFormBase {
'#type' => 'textarea',
'#title' => t('Feed description'),
'#default_value' => $rss_config->get('channel.description'),
'#description' => t('Description of your site, included in each feed.')
'#description' => t('Description of your site, included in each feed.'),
];
$options = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30];
$form['feed_default_items'] = [
@@ -43,7 +43,7 @@ class RssFeedsForm extends ConfigFormBase {
'#title' => t('Number of items in each feed'),
'#default_value' => $rss_config->get('items.limit'),
'#options' => array_combine($options, $options),
'#description' => t('Default number of items to include in each feed.')
'#description' => t('Default number of items to include in each feed.'),
];
$form['feed_view_mode'] = [
'#type' => 'select',
@@ -54,7 +54,7 @@ class RssFeedsForm extends ConfigFormBase {
'teaser' => t('Titles plus teaser'),
'fulltext' => t('Full text'),
],
'#description' => t('Global setting for the default display of content items in each feed.')
'#description' => t('Global setting for the default display of content items in each feed.'),
];
return parent::buildForm($form, $form_state);
@@ -56,6 +56,7 @@ class SiteMaintenanceModeForm extends ConfigFormBase {
$container->get('user.permissions')
);
}
/**
* {@inheritdoc}
*/
@@ -136,11 +136,11 @@ class ThemeSettingsForm extends ConfigFormBase {
$form['var'] = [
'#type' => 'hidden',
'#value' => $var
'#value' => $var,
];
$form['config_key'] = [
'#type' => 'hidden',
'#value' => $config_key
'#value' => $config_key,
];
// Toggle settings
@@ -377,22 +377,23 @@ class ThemeSettingsForm extends ConfigFormBase {
parent::validateForm($form, $form_state);
if ($this->moduleHandler->moduleExists('file')) {
$validators = ['file_validate_is_image' => []];
// Check for a new uploaded logo.
$file = _file_save_upload_from_form($form['logo']['settings']['logo_upload'], $form_state, 0);
if ($file) {
// Put the temporary file in form_values so we can save it on submit.
$form_state->setValue('logo_upload', $file);
if (isset($form['logo'])) {
$file = _file_save_upload_from_form($form['logo']['settings']['logo_upload'], $form_state, 0);
if ($file) {
// Put the temporary file in form_values so we can save it on submit.
$form_state->setValue('logo_upload', $file);
}
}
$validators = ['file_validate_extensions' => ['ico png gif jpg jpeg apng svg']];
// Check for a new uploaded favicon.
$file = _file_save_upload_from_form($form['favicon']['settings']['favicon_upload'], $form_state, 0);
if ($file) {
// Put the temporary file in form_values so we can save it on submit.
$form_state->setValue('favicon_upload', $file);
if (isset($form['favicon'])) {
$file = _file_save_upload_from_form($form['favicon']['settings']['favicon_upload'], $form_state, 0);
if ($file) {
// Put the temporary file in form_values so we can save it on submit.
$form_state->setValue('favicon_upload', $file);
}
}
// When intending to use the default logo, unset the logo_path.
@@ -3,7 +3,6 @@
namespace Drupal\system;
use Drupal\Component\Transliteration\TransliterationInterface;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -73,7 +72,7 @@ class MachineNameController implements ContainerInjectionInterface {
$transliterated = $this->transliteration->transliterate($text, $langcode, '_');
if ($lowercase) {
$transliterated = Unicode::strtolower($transliterated);
$transliterated = mb_strtolower($transliterated);
}
if (isset($replace_pattern) && isset($replace)) {
@@ -9,7 +9,7 @@ use Drupal\Core\Cache\Cache;
/**
* Provides a block to display the messages.
*
* @see drupal_set_message()
* @see @see \Drupal\Core\Messenger\MessengerInterface
*
* @Block(
* id = "system_messages_block",
@@ -2,7 +2,6 @@
namespace Drupal\system\Plugin\Condition;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Path\AliasManagerInterface;
@@ -139,7 +138,7 @@ class RequestPath extends ConditionPluginBase implements ContainerFactoryPluginI
public function evaluate() {
// Convert path to lowercase. This allows comparison of the same path
// with different case. Ex: /Page, /page, /PAGE.
$pages = Unicode::strtolower($this->configuration['pages']);
$pages = mb_strtolower($this->configuration['pages']);
if (!$pages) {
return TRUE;
}
@@ -149,7 +148,7 @@ class RequestPath extends ConditionPluginBase implements ContainerFactoryPluginI
$path = $this->currentPath->getPath($request);
// Do not trim a trailing slash if that is the complete path.
$path = $path === '/' ? $path : rtrim($path, '/');
$path_alias = Unicode::strtolower($this->aliasManager->getAliasByPath($path));
$path_alias = mb_strtolower($this->aliasManager->getAliasByPath($path));
return $this->pathMatcher->matchPath($path_alias, $pages) || (($path != $path_alias) && $this->pathMatcher->matchPath($path, $pages));
}
@@ -3,7 +3,6 @@
namespace Drupal\system\Plugin\ImageToolkit;
use Drupal\Component\Utility\Color;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\ImageToolkit\ImageToolkitBase;
@@ -395,7 +394,7 @@ class GDToolkit extends ImageToolkitBase {
foreach (static::supportedTypes() as $image_type) {
// @todo Automatically fetch possible extensions for each mime type.
// @see https://www.drupal.org/node/2311679
$extension = Unicode::strtolower(image_type_to_extension($image_type, FALSE));
$extension = mb_strtolower(image_type_to_extension($image_type, FALSE));
$extensions[] = $extension;
// Add some known similar extensions.
if ($extension === 'jpeg') {
@@ -20,6 +20,16 @@ class ScaleAndCrop extends GDImageToolkitOperationBase {
*/
protected function arguments() {
return [
'x' => [
'description' => 'The horizontal offset for the start of the crop, in pixels',
'required' => FALSE,
'default' => NULL,
],
'y' => [
'description' => 'The vertical offset for the start the crop, in pixels',
'required' => FALSE,
'default' => NULL,
],
'width' => [
'description' => 'The target width, in pixels',
],
@@ -38,8 +48,12 @@ class ScaleAndCrop extends GDImageToolkitOperationBase {
$scaleFactor = max($arguments['width'] / $actualWidth, $arguments['height'] / $actualHeight);
$arguments['x'] = (int) round(($actualWidth * $scaleFactor - $arguments['width']) / 2);
$arguments['y'] = (int) round(($actualHeight * $scaleFactor - $arguments['height']) / 2);
$arguments['x'] = isset($arguments['x']) ?
(int) round($arguments['x']) :
(int) round(($actualWidth * $scaleFactor - $arguments['width']) / 2);
$arguments['y'] = isset($arguments['y']) ?
(int) round($arguments['y']) :
(int) round(($actualHeight * $scaleFactor - $arguments['height']) / 2);
$arguments['resize'] = [
'width' => (int) round($actualWidth * $scaleFactor),
'height' => (int) round($actualHeight * $scaleFactor),
@@ -14,6 +14,7 @@ use Drupal\migrate\Row;
* )
*/
class TimeZone extends ProcessPluginBase {
/**
* {@inheritdoc}
*/
@@ -0,0 +1,66 @@
<?php
namespace Drupal\system\Plugin\migrate\source;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
/**
* Gets system data for a legacy extension.
*
* @MigrateSource(
* id = "extension",
* source_module = "system"
* )
*/
class Extension extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this->select('system', 's')
->fields('s');
if (isset($this->configuration['name'])) {
$query->condition('name', (array) $this->configuration['name'], 'IN');
}
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
$fields = [
'filename' => $this->t('Filename'),
'name' => $this->t('Name'),
'type' => $this->t('Type'),
'owner' => $this->t('Owner'),
'status' => $this->t('Status'),
'throttle' => $this->t('Throttle'),
'bootstrap' => $this->t('Bootstrap'),
'schema_version' => $this->t('Schema version'),
'weight' => $this->t('Weight'),
'info' => $this->t('Information array'),
];
return $fields;
}
/**
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
$row->setSourceProperty('info', unserialize($row->getSourceProperty('info')));
return parent::prepareRow($row);
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['name']['type'] = 'string';
return $ids;
}
}
@@ -72,6 +72,9 @@ class SystemConfigSubscriber implements EventSubscriberInterface {
* The config import event.
*/
public function onConfigImporterValidateSiteUUID(ConfigImporterEvent $event) {
if (!$event->getConfigImporter()->getStorageComparer()->getSourceStorage()->exists('system.site')) {
$event->getConfigImporter()->logError($this->t('This import does not contain system.site configuration, so has been rejected.'));
}
if (!$event->getConfigImporter()->getStorageComparer()->validateSiteUuid()) {
$event->getConfigImporter()->logError($this->t('Site UUID in source storage does not match the target storage.'));
}
+2 -1
View File
@@ -152,7 +152,8 @@ class SystemManager {
* hidden, so we supply the contents of the block.
*
* @return array
* A render array suitable for drupal_render.
* A render array suitable for
* \Drupal\Core\Render\RendererInterface::render().
*/
public function getBlockContents() {
// We hard-code the menu name here since otherwise a link in the tools menu
@@ -1,77 +0,0 @@
<?php
namespace Drupal\system\Tests\Ajax;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Url;
/**
* Tests the usage of form caching for AJAX forms.
*
* @group Ajax
*/
class AjaxFormCacheTest extends AjaxTestBase {
/**
* Tests the usage of form cache for AJAX forms.
*/
public function testFormCacheUsage() {
/** @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $key_value_expirable */
$key_value_expirable = \Drupal::service('keyvalue.expirable')->get('form');
$this->drupalLogin($this->rootUser);
// Ensure that the cache is empty.
$this->assertEqual(0, count($key_value_expirable->getAll()));
// Visit an AJAX form that is not cached, 3 times.
$uncached_form_url = Url::fromRoute('ajax_forms_test.commands_form');
$this->drupalGet($uncached_form_url);
$this->drupalGet($uncached_form_url);
$this->drupalGet($uncached_form_url);
// The number of cache entries should not have changed.
$this->assertEqual(0, count($key_value_expirable->getAll()));
}
/**
* Tests AJAX forms in blocks.
*/
public function testBlockForms() {
$this->container->get('module_installer')->install(['block', 'search']);
$this->rebuildContainer();
$this->container->get('router.builder')->rebuild();
$this->drupalLogin($this->rootUser);
$this->drupalPlaceBlock('search_form_block', ['weight' => -5]);
$this->drupalPlaceBlock('ajax_forms_test_block');
$this->drupalGet('');
$this->drupalPostAjaxForm(NULL, ['test1' => 'option1'], 'test1');
$this->assertOptionSelectedWithDrupalSelector('edit-test1', 'option1');
$this->assertOptionWithDrupalSelector('edit-test1', 'option3');
$this->drupalPostForm(NULL, ['test1' => 'option1'], 'Submit');
$this->assertText('Submission successful.');
}
/**
* Tests AJAX forms on pages with a query string.
*/
public function testQueryString() {
$this->container->get('module_installer')->install(['block']);
$this->drupalLogin($this->rootUser);
$this->drupalPlaceBlock('ajax_forms_test_block');
$url = Url::fromRoute('entity.user.canonical', ['user' => $this->rootUser->id()], ['query' => ['foo' => 'bar']]);
$this->drupalGet($url);
$this->drupalPostAjaxForm(NULL, ['test1' => 'option1'], 'test1');
$url->setOption('query', [
'foo' => 'bar',
FormBuilderInterface::AJAX_FORM_REQUEST => 1,
MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax',
]);
$this->assertUrl($url);
}
}
@@ -1,32 +0,0 @@
<?php
namespace Drupal\system\Tests\Ajax;
/**
* Tests that form elements in groups work correctly with AJAX.
*
* @group Ajax
*/
class AjaxInGroupTest extends AjaxTestBase {
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser(['access content']));
}
/**
* Submits forms with select and checkbox elements via Ajax.
*/
public function testSimpleAjaxFormValue() {
$this->drupalGet('/ajax_forms_test_get_form');
$this->assertText('Test group');
$this->assertText('AJAX checkbox in a group');
$this->drupalPostAjaxForm(NULL, ['checkbox_in_group' => TRUE], 'checkbox_in_group');
$this->assertText('Test group');
$this->assertText('AJAX checkbox in a group');
$this->assertText('AJAX checkbox in a nested group');
$this->assertText('Another AJAX checkbox in a nested group');
}
}
@@ -29,6 +29,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
* @group Ajax
*/
class CommandsTest extends AjaxTestBase {
/**
* Tests the various Ajax Commands.
*/
@@ -114,7 +114,7 @@ class DialogTest extends AjaxTestBase {
], [], 'ajax-test/dialog-contents', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_dialog']], [], NULL, [
'submit' => [
'dialogOptions[target]' => 'ajax-test-dialog-wrapper-1',
]
],
]);
$this->assertEqual($normal_expected_response, $ajax_result[3], 'Normal dialog JSON response matches.');
@@ -126,7 +126,7 @@ class DialogTest extends AjaxTestBase {
'textfield' => 'test',
], [], 'ajax-test/dialog-contents', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_dialog']], [], NULL, [
// Don't send a target.
'submit' => []
'submit' => [],
]);
// Make sure the selector ID starts with the right string.
$this->assert(strpos($ajax_result[3]['selector'], $no_target_expected_response['selector']) === 0, 'Selector starts with right string.');
@@ -8,6 +8,7 @@ namespace Drupal\system\Tests\Ajax;
* @group Ajax
*/
class ElementValidationTest extends AjaxTestBase {
/**
* Tries to post an Ajax change to a form that has a validated element.
*
@@ -10,6 +10,7 @@ use Drupal\Core\Ajax\DataCommand;
* @group Ajax
*/
class FormValuesTest extends AjaxTestBase {
protected function setUp() {
parent::setUp();
@@ -16,6 +16,7 @@ use Drupal\Core\Asset\AttachedAssets;
* @group Ajax
*/
class FrameworkTest extends AjaxTestBase {
/**
* Verifies the Ajax rendering of a command in the settings.
*/
@@ -1,27 +0,0 @@
<?php
namespace Drupal\system\Tests\Bootstrap;
use Drupal\Core\DependencyInjection\Container;
/**
* Container base class which triggers an error.
*/
class ErrorContainer extends Container {
/**
* {@inheritdoc}
*/
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) {
if ($id === 'http_kernel') {
// Enforce a recoverable error.
$callable = function (ErrorContainer $container) {
};
$callable(1);
}
else {
return parent::get($id, $invalidBehavior);
}
}
}
@@ -1,24 +0,0 @@
<?php
namespace Drupal\system\Tests\Bootstrap;
use Drupal\Core\DependencyInjection\Container;
/**
* Base container which throws an exception.
*/
class ExceptionContainer extends Container {
/**
* {@inheritdoc}
*/
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) {
if ($id === 'http_kernel') {
throw new \Exception('Thrown exception during Container::get');
}
else {
return parent::get($id, $invalidBehavior);
}
}
}
@@ -4,7 +4,7 @@ namespace Drupal\system\Tests\Cache;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
/**
* Provides helper methods for page cache tags tests.
@@ -47,7 +47,7 @@ abstract class PageCacheTagsTestBase extends WebTestBase {
*/
protected function verifyPageCache(Url $url, $hit_or_miss, $tags = FALSE) {
$this->drupalGet($url);
$message = SafeMarkup::format('Page cache @hit_or_miss for %path.', ['@hit_or_miss' => $hit_or_miss, '%path' => $url->toString()]);
$message = new FormattableMarkup('Page cache @hit_or_miss for %path.', ['@hit_or_miss' => $hit_or_miss, '%path' => $url->toString()]);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), $hit_or_miss, $message);
if ($hit_or_miss === 'HIT' && is_array($tags)) {
@@ -1,95 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Make sure that attaching feeds works correctly with various constructs.
*
* @group Common
*/
class AddFeedTest extends WebTestBase {
/**
* Tests attaching feeds with paths, URLs, and titles.
*/
public function testBasicFeedAddNoTitle() {
$path = $this->randomMachineName(12);
$external_url = 'http://' . $this->randomMachineName(12) . '/' . $this->randomMachineName(12);
$fully_qualified_local_url = Url::fromUri('base:' . $this->randomMachineName(12), ['absolute' => TRUE])->toString();
$path_for_title = $this->randomMachineName(12);
$external_for_title = 'http://' . $this->randomMachineName(12) . '/' . $this->randomMachineName(12);
$fully_qualified_for_title = Url::fromUri('base:' . $this->randomMachineName(12), ['absolute' => TRUE])->toString();
$urls = [
'path without title' => [
'url' => Url::fromUri('base:' . $path, ['absolute' => TRUE])->toString(),
'title' => '',
],
'external URL without title' => [
'url' => $external_url,
'title' => '',
],
'local URL without title' => [
'url' => $fully_qualified_local_url,
'title' => '',
],
'path with title' => [
'url' => Url::fromUri('base:' . $path_for_title, ['absolute' => TRUE])->toString(),
'title' => $this->randomMachineName(12),
],
'external URL with title' => [
'url' => $external_for_title,
'title' => $this->randomMachineName(12),
],
'local URL with title' => [
'url' => $fully_qualified_for_title,
'title' => $this->randomMachineName(12),
],
];
$build = [];
foreach ($urls as $feed_info) {
$build['#attached']['feed'][] = [$feed_info['url'], $feed_info['title']];
}
// Use the bare HTML page renderer to render our links.
$renderer = $this->container->get('bare_html_page_renderer');
$response = $renderer->renderBarePage($build, '', 'maintenance_page');
// Glean the content from the response object.
$this->setRawContent($response->getContent());
// Assert that the content contains the RSS links we specified.
foreach ($urls as $description => $feed_info) {
$this->assertPattern($this->urlToRSSLinkPattern($feed_info['url'], $feed_info['title']), format_string('Found correct feed header for %description', ['%description' => $description]));
}
}
/**
* Creates a pattern representing the RSS feed in the page.
*/
public function urlToRSSLinkPattern($url, $title = '') {
// Escape any regular expression characters in the URL ('?' is the worst).
$url = preg_replace('/([+?.*])/', '[$0]', $url);
$generated_pattern = '%<link +href="' . $url . '" +rel="alternate" +title="' . $title . '" +type="application/rss.xml" */>%';
return $generated_pattern;
}
/**
* Checks that special characters are correctly escaped.
*
* @see https://www.drupal.org/node/1211668
*/
public function testFeedIconEscaping() {
$variables = [
'#theme' => 'feed_icon',
'#url' => 'node',
'#title' => '<>&"\'',
];
$text = \Drupal::service('renderer')->renderRoot($variables);
$this->assertEqual(trim(strip_tags($text)), 'Subscribe to &lt;&gt;&amp;&quot;&#039;', 'feed_icon template escapes reserved HTML characters.');
}
}
@@ -1,72 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\WebTestBase;
/**
* Tests alteration of arguments passed to \Drupal::moduleHandler->alter().
*
* @group Common
*/
class AlterTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'common_test'];
/**
* Tests if the theme has been altered.
*/
public function testDrupalAlter() {
// This test depends on Bartik, so make sure that it is always the current
// active theme.
\Drupal::service('theme_handler')->install(['bartik']);
\Drupal::theme()->setActiveTheme(\Drupal::service('theme.initialization')->initTheme('bartik'));
$array = ['foo' => 'bar'];
$entity = new \stdClass();
$entity->foo = 'bar';
// Verify alteration of a single argument.
$array_copy = $array;
$array_expected = ['foo' => 'Drupal theme'];
\Drupal::moduleHandler()->alter('drupal_alter', $array_copy);
\Drupal::theme()->alter('drupal_alter', $array_copy);
$this->assertEqual($array_copy, $array_expected, 'Single array was altered.');
$entity_copy = clone $entity;
$entity_expected = clone $entity;
$entity_expected->foo = 'Drupal theme';
\Drupal::moduleHandler()->alter('drupal_alter', $entity_copy);
\Drupal::theme()->alter('drupal_alter', $entity_copy);
$this->assertEqual($entity_copy, $entity_expected, 'Single object was altered.');
// Verify alteration of multiple arguments.
$array_copy = $array;
$array_expected = ['foo' => 'Drupal theme'];
$entity_copy = clone $entity;
$entity_expected = clone $entity;
$entity_expected->foo = 'Drupal theme';
$array2_copy = $array;
$array2_expected = ['foo' => 'Drupal theme'];
\Drupal::moduleHandler()->alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
\Drupal::theme()->alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
$this->assertEqual($array_copy, $array_expected, 'First argument to \Drupal::moduleHandler->alter() was altered.');
$this->assertEqual($entity_copy, $entity_expected, 'Second argument to \Drupal::moduleHandler->alter() was altered.');
$this->assertEqual($array2_copy, $array2_expected, 'Third argument to \Drupal::moduleHandler->alter() was altered.');
// Verify alteration order when passing an array of types to \Drupal::moduleHandler->alter().
// common_test_module_implements_alter() places 'block' implementation after
// other modules.
$array_copy = $array;
$array_expected = ['foo' => 'Drupal block theme'];
\Drupal::moduleHandler()->alter(['drupal_alter', 'drupal_alter_foo'], $array_copy);
\Drupal::theme()->alter(['drupal_alter', 'drupal_alter_foo'], $array_copy);
$this->assertEqual($array_copy, $array_expected, 'hook_TYPE_alter() implementations ran in correct order.');
}
}
@@ -1,110 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Verifies that bubbleable metadata of early rendering is not lost.
*
* @group Common
*/
class EarlyRenderingControllerTest extends WebTestBase {
/**
* {@inheritdoc}
*/
protected $dumpHeaders = TRUE;
/**
* {@inheritdoc}
*/
public static $modules = ['system', 'early_rendering_controller_test'];
/**
* Tests theme preprocess functions being able to attach assets.
*/
public function testEarlyRendering() {
// Render array: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.render_array'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.render_array.early'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertCacheTag('foo');
// AjaxResponse: non-early & early.
// @todo Add cache tags assertion when AjaxResponse is made cacheable in
// https://www.drupal.org/node/956186.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.ajax_response'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.ajax_response.early'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
// Basic Response object: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response.early'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertNoCacheTag('foo');
// Response object with attachments: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\AttachmentsTestResponse.');
// Cacheable Response object: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\CacheableTestResponse.');
// Basic domain object: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object'));
$this->assertResponse(200);
$this->assertRaw('TestDomainObject');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object.early'));
$this->assertResponse(200);
$this->assertRaw('TestDomainObject');
$this->assertNoCacheTag('foo');
// Basic domain object with attachments: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object-with-attachments'));
$this->assertResponse(200);
$this->assertRaw('AttachmentsTestDomainObject');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object-with-attachments.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\AttachmentsTestDomainObject.');
// Cacheable Response object: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-domain-object'));
$this->assertResponse(200);
$this->assertRaw('CacheableTestDomainObject');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-domain-object.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\CacheableTestDomainObject.');
// The exceptions are expected. Do not interpret them as a test failure.
// Not using File API; a potential error must trigger a PHP warning.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
}
}
@@ -1,80 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\simpletest\WebTestBase;
/**
* Tests the format_date() function.
*
* @group Common
*/
class FormatDateTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language'];
/**
* Arbitrary langcode for a custom language.
*/
const LANGCODE = 'xx';
protected function setUp() {
parent::setUp('language');
$this->config('system.date')
->set('timezone.user.configurable', 1)
->save();
$formats = $this->container->get('entity.manager')
->getStorage('date_format')
->loadMultiple(['long', 'medium', 'short']);
$formats['long']->setPattern('l, j. F Y - G:i')->save();
$formats['medium']->setPattern('j. F Y - G:i')->save();
$formats['short']->setPattern('Y M j - g:ia')->save();
$this->refreshVariables();
$this->settingsSet('locale_custom_strings_' . self::LANGCODE, [
'' => ['Sunday' => 'domingo'],
'Long month name' => ['March' => 'marzo'],
]);
ConfigurableLanguage::createFromLangcode(static::LANGCODE)->save();
$this->resetAll();
}
/**
* Tests admin-defined formats in format_date().
*/
public function testAdminDefinedFormatDate() {
// Create and log in an admin user.
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
// Add new date format.
$edit = [
'id' => 'example_style',
'label' => 'Example Style',
'date_format_pattern' => 'j M y',
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
// Add a second date format with a different case than the first.
$edit = [
'id' => 'example_style_uppercase',
'label' => 'Example Style Uppercase',
'date_format_pattern' => 'j M Y',
];
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertText(t('Custom date format added.'));
$timestamp = strtotime('2007-03-10T00:00:00+00:00');
$this->assertIdentical(format_date($timestamp, 'example_style', '', 'America/Los_Angeles'), '9 Mar 07');
$this->assertIdentical(format_date($timestamp, 'example_style_uppercase', '', 'America/Los_Angeles'), '9 Mar 2007');
$this->assertIdentical(format_date($timestamp, 'undefined_style'), format_date($timestamp, 'fallback'), 'Test format_date() defaulting to `fallback` when $type not found.');
}
}
@@ -1,67 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\WebTestBase;
use Drupal\node\NodeInterface;
/**
* Tests that anonymous users are not served any JavaScript in the Standard
* installation profile.
*
* @group Common
*/
class NoJavaScriptAnonymousTest extends WebTestBase {
protected $profile = 'standard';
protected function setUp() {
parent::setUp();
// Grant the anonymous user the permission to look at user profiles.
user_role_grant_permissions('anonymous', ['access user profiles']);
}
/**
* Tests that anonymous users are not served any JavaScript.
*/
public function testNoJavaScript() {
// Create a node that is listed on the frontpage.
$this->drupalCreateNode([
'promote' => NodeInterface::PROMOTED,
]);
$user = $this->drupalCreateUser();
// Test frontpage.
$this->drupalGet('');
$this->assertNoJavaScriptExceptHtml5Shiv();
// Test node page.
$this->drupalGet('node/1');
$this->assertNoJavaScriptExceptHtml5Shiv();
// Test user profile page.
$this->drupalGet('user/' . $user->id());
$this->assertNoJavaScriptExceptHtml5Shiv();
}
/**
* Passes if no JavaScript is found on the page except the HTML5 shiv.
*
* The HTML5 shiv is necessary for e.g. the <article> tag which Drupal 8 uses
* to work in older browsers like Internet Explorer 8.
*/
protected function assertNoJavaScriptExceptHtml5Shiv() {
// Ensure drupalSettings is not set.
$settings = $this->getDrupalSettings();
$this->assertTrue(empty($settings), 'drupalSettings is not set.');
// Ensure the HTML5 shiv exists.
$this->assertRaw('html5shiv/html5shiv.min.js', 'HTML5 shiv JavaScript exists.');
// Ensure no other JavaScript file exists on the page, while ignoring the
// HTML5 shiv.
$this->assertNoPattern('/(?<!html5shiv\.min)\.js/', "No other JavaScript exists.");
}
}
@@ -1,177 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Component\Serialization\Json;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Performs integration tests on drupal_render().
*
* @group Common
*/
class RenderWebTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['common_test'];
/**
* Asserts the cache context for the wrapper format is always present.
*/
public function testWrapperFormatCacheContext() {
$this->drupalGet('common-test/type-link-active-class');
$this->assertIdentical(0, strpos($this->getRawContent(), "<!DOCTYPE html>\n<html"));
$this->assertIdentical('text/html; charset=UTF-8', $this->drupalGetHeader('Content-Type'));
$this->assertTitle('Test active link class | Drupal');
$this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
$this->drupalGet('common-test/type-link-active-class', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'json']]);
$this->assertIdentical('application/json', $this->drupalGetHeader('Content-Type'));
$json = Json::decode($this->getRawContent());
$this->assertEqual(['content', 'title'], array_keys($json));
$this->assertIdentical('Test active link class', $json['title']);
$this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
}
/**
* Tests rendering form elements without passing through
* \Drupal::formBuilder()->doBuildForm().
*/
public function testDrupalRenderFormElements() {
// Define a series of form elements.
$element = [
'#type' => 'button',
'#value' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//input[@type=:type]', [':type' => 'submit']);
$element = [
'#type' => 'textfield',
'#title' => $this->randomMachineName(),
'#value' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//input[@type=:type]', [':type' => 'text']);
$element = [
'#type' => 'password',
'#title' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//input[@type=:type]', [':type' => 'password']);
$element = [
'#type' => 'textarea',
'#title' => $this->randomMachineName(),
'#value' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//textarea');
$element = [
'#type' => 'radio',
'#title' => $this->randomMachineName(),
'#value' => FALSE,
];
$this->assertRenderedElement($element, '//input[@type=:type]', [':type' => 'radio']);
$element = [
'#type' => 'checkbox',
'#title' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//input[@type=:type]', [':type' => 'checkbox']);
$element = [
'#type' => 'select',
'#title' => $this->randomMachineName(),
'#options' => [
0 => $this->randomMachineName(),
1 => $this->randomMachineName(),
],
];
$this->assertRenderedElement($element, '//select');
$element = [
'#type' => 'file',
'#title' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//input[@type=:type]', [':type' => 'file']);
$element = [
'#type' => 'item',
'#title' => $this->randomMachineName(),
'#markup' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//div[contains(@class, :class) and contains(., :markup)]/label[contains(., :label)]', [
':class' => 'js-form-type-item',
':markup' => $element['#markup'],
':label' => $element['#title'],
]);
$element = [
'#type' => 'hidden',
'#title' => $this->randomMachineName(),
'#value' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//input[@type=:type]', [':type' => 'hidden']);
$element = [
'#type' => 'link',
'#title' => $this->randomMachineName(),
'#url' => Url::fromRoute('common_test.destination'),
'#options' => [
'absolute' => TRUE,
],
];
$this->assertRenderedElement($element, '//a[@href=:href and contains(., :title)]', [
':href' => URL::fromRoute('common_test.destination')->setAbsolute()->toString(),
':title' => $element['#title'],
]);
$element = [
'#type' => 'details',
'#open' => TRUE,
'#title' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//details/summary[contains(., :title)]', [
':title' => $element['#title'],
]);
$element = [
'#type' => 'details',
'#open' => TRUE,
'#title' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//details');
$element['item'] = [
'#type' => 'item',
'#title' => $this->randomMachineName(),
'#markup' => $this->randomMachineName(),
];
$this->assertRenderedElement($element, '//details/div/div[contains(@class, :class) and contains(., :markup)]', [
':class' => 'js-form-type-item',
':markup' => $element['item']['#markup'],
]);
}
/**
* Tests that elements are rendered properly.
*/
protected function assertRenderedElement(array $element, $xpath, array $xpath_args = []) {
$original_element = $element;
$this->setRawContent(drupal_render_root($element));
$this->verbose('<hr />' . $this->getRawContent());
// @see \Drupal\simpletest\WebTestBase::xpath()
$xpath = $this->buildXPathQuery($xpath, $xpath_args);
$element += ['#value' => NULL];
$this->assertFieldByXPath($xpath, $element['#value'], format_string('#type @type was properly rendered.', [
'@type' => var_export($element['#type'], TRUE),
]));
}
}
@@ -1,94 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\simpletest\WebTestBase;
/**
* Tests SimpleTest error and exception collector.
*
* @group Common
*/
class SimpleTestErrorCollectorTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system_test', 'error_test'];
/**
* Errors triggered during the test.
*
* Errors are intercepted by the overridden implementation
* of Drupal\simpletest\WebTestBase::error() below.
*
* @var Array
*/
protected $collectedErrors = [];
/**
* Tests that simpletest collects errors from the tested site.
*/
public function testErrorCollect() {
$this->collectedErrors = [];
$this->drupalGet('error-test/generate-warnings-with-report');
$this->assertEqual(count($this->collectedErrors), 3, 'Three errors were collected');
if (count($this->collectedErrors) == 3) {
$this->assertError($this->collectedErrors[0], 'Notice', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Undefined variable: bananas');
$this->assertError($this->collectedErrors[1], 'Warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Division by zero');
$this->assertError($this->collectedErrors[2], 'User warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Drupal &amp; awesome');
}
else {
// Give back the errors to the log report.
foreach ($this->collectedErrors as $error) {
parent::error($error['message'], $error['group'], $error['caller']);
}
}
}
/**
* Stores errors into an array.
*
* This test class is trying to verify that simpletest correctly sees errors
* and warnings. However, it can't generate errors and warnings that
* propagate up to the testing framework itself, or these tests would always
* fail. So, this special copy of error() doesn't propagate the errors up
* the class hierarchy. It just stuffs them into a protected collectedErrors
* array for various assertions to inspect.
*/
protected function error($message = '', $group = 'Other', array $caller = NULL) {
// Due to a WTF elsewhere, simpletest treats debug() and verbose()
// messages as if they were an 'error'. But, we don't want to collect
// those here. This function just wants to collect the real errors (PHP
// notices, PHP fatal errors, etc.), and let all the 'errors' from the
// 'User notice' group bubble up to the parent classes to be handled (and
// eventually displayed) as normal.
if ($group == 'User notice') {
parent::error($message, $group, $caller);
}
// Everything else should be collected but not propagated.
else {
$this->collectedErrors[] = [
'message' => $message,
'group' => $group,
'caller' => $caller
];
}
}
/**
* Asserts that a collected error matches what we are expecting.
*/
public function assertError($error, $group, $function, $file, $message = NULL) {
$this->assertEqual($error['group'], $group, format_string("Group was %group", ['%group' => $group]));
$this->assertEqual($error['caller']['function'], $function, format_string("Function was %function", ['%function' => $function]));
$this->assertEqual(drupal_basename($error['caller']['file']), $file, format_string("File was %file", ['%file' => $file]));
if (isset($message)) {
$this->assertEqual($error['message'], $message, format_string("Message was %message", ['%message' => $message]));
}
}
}
@@ -1,320 +0,0 @@
<?php
namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Language\Language;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Confirm that \Drupal\Core\Url,
* \Drupal\Component\Utility\UrlHelper::filterQueryParameters(),
* \Drupal\Component\Utility\UrlHelper::buildQuery(), and
* \Drupal\Core\Utility\LinkGeneratorInterface::generate()
* work correctly with various input.
*
* @group Common
*/
class UrlTest extends WebTestBase {
public static $modules = ['common_test', 'url_alter_test'];
/**
* Confirms that invalid URLs are filtered in link generating functions.
*/
public function testLinkXSS() {
// Test \Drupal::l().
$text = $this->randomMachineName();
$path = "<SCRIPT>alert('XSS')</SCRIPT>";
$encoded_path = "3CSCRIPT%3Ealert%28%27XSS%27%29%3C/SCRIPT%3E";
$link = \Drupal::l($text, Url::fromUserInput('/' . $path));
$this->assertTrue(strpos($link, $encoded_path) !== FALSE && strpos($link, $path) === FALSE, format_string('XSS attack @path was filtered by \Drupal\Core\Utility\LinkGeneratorInterface::generate().', ['@path' => $path]));
// Test \Drupal\Core\Url.
$link = Url::fromUri('base:' . $path)->toString();
$this->assertTrue(strpos($link, $encoded_path) !== FALSE && strpos($link, $path) === FALSE, format_string('XSS attack @path was filtered by #theme', ['@path' => $path]));
}
/**
* Tests that #type=link bubbles outbound route/path processors' metadata.
*/
public function testLinkBubbleableMetadata() {
$cases = [
['Regular link', 'internal:/user', [], ['contexts' => [], 'tags' => [], 'max-age' => Cache::PERMANENT], []],
['Regular link, absolute', 'internal:/user', ['absolute' => TRUE], ['contexts' => ['url.site'], 'tags' => [], 'max-age' => Cache::PERMANENT], []],
['Route processor link', 'route:system.run_cron', [], ['contexts' => ['session'], 'tags' => [], 'max-age' => Cache::PERMANENT], ['placeholders' => []]],
['Route processor link, absolute', 'route:system.run_cron', ['absolute' => TRUE], ['contexts' => ['url.site', 'session'], 'tags' => [], 'max-age' => Cache::PERMANENT], ['placeholders' => []]],
['Path processor link', 'internal:/user/1', [], ['contexts' => [], 'tags' => ['user:1'], 'max-age' => Cache::PERMANENT], []],
['Path processor link, absolute', 'internal:/user/1', ['absolute' => TRUE], ['contexts' => ['url.site'], 'tags' => ['user:1'], 'max-age' => Cache::PERMANENT], []],
];
foreach ($cases as $case) {
list($title, $uri, $options, $expected_cacheability, $expected_attachments) = $case;
$expected_cacheability['contexts'] = Cache::mergeContexts($expected_cacheability['contexts'], ['languages:language_interface', 'theme', 'user.permissions']);
$link = [
'#type' => 'link',
'#title' => $title,
'#options' => $options,
'#url' => Url::fromUri($uri),
];
\Drupal::service('renderer')->renderRoot($link);
$this->pass($title);
$this->assertEqual($expected_cacheability, $link['#cache']);
$this->assertEqual($expected_attachments, $link['#attached']);
}
}
/**
* Tests that default and custom attributes are handled correctly on links.
*/
public function testLinkAttributes() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = $this->container->get('renderer');
// Test that hreflang is added when a link has a known language.
$language = new Language(['id' => 'fr', 'name' => 'French']);
$hreflang_link = [
'#type' => 'link',
'#options' => [
'language' => $language,
],
'#url' => Url::fromUri('https://www.drupal.org'),
'#title' => 'bar',
];
$langcode = $language->getId();
// Test that the default hreflang handling for links does not override a
// hreflang attribute explicitly set in the render array.
$hreflang_override_link = $hreflang_link;
$hreflang_override_link['#options']['attributes']['hreflang'] = 'foo';
$rendered = $renderer->renderRoot($hreflang_link);
$this->assertTrue($this->hasAttribute('hreflang', $rendered, $langcode), format_string('hreflang attribute with value @langcode is present on a rendered link when langcode is provided in the render array.', ['@langcode' => $langcode]));
$rendered = $renderer->renderRoot($hreflang_override_link);
$this->assertTrue($this->hasAttribute('hreflang', $rendered, 'foo'), format_string('hreflang attribute with value @hreflang is present on a rendered link when @hreflang is provided in the render array.', ['@hreflang' => 'foo']));
// Test the active class in links produced by
// \Drupal\Core\Utility\LinkGeneratorInterface::generate() and #type 'link'.
$options_no_query = [];
$options_query = [
'query' => [
'foo' => 'bar',
'one' => 'two',
],
];
$options_query_reverse = [
'query' => [
'one' => 'two',
'foo' => 'bar',
],
];
// Test #type link.
$path = 'common-test/type-link-active-class';
$this->drupalGet($path, $options_no_query);
$links = $this->xpath('//a[@href = :href and contains(@class, :class)]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_no_query)->toString(), ':class' => 'is-active']);
$this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page is marked active.');
$links = $this->xpath('//a[@href = :href and not(contains(@class, :class))]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_query)->toString(), ':class' => 'is-active']);
$this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page with a query string when the current page has no query string is not marked active.');
$this->drupalGet($path, $options_query);
$links = $this->xpath('//a[@href = :href and contains(@class, :class)]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_query)->toString(), ':class' => 'is-active']);
$this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page with a query string that matches the current query string is marked active.');
$links = $this->xpath('//a[@href = :href and contains(@class, :class)]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_query_reverse)->toString(), ':class' => 'is-active']);
$this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page with a query string that has matching parameters to the current query string but in a different order is marked active.');
$links = $this->xpath('//a[@href = :href and not(contains(@class, :class))]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_no_query)->toString(), ':class' => 'is-active']);
$this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page without a query string when the current page has a query string is not marked active.');
// Test adding a custom class in links produced by
// \Drupal\Core\Utility\LinkGeneratorInterface::generate() and #type 'link'.
// Test the link generator.
$class_l = $this->randomMachineName();
$link_l = \Drupal::l($this->randomMachineName(), new Url('<current>', [], ['attributes' => ['class' => [$class_l]]]));
$this->assertTrue($this->hasAttribute('class', $link_l, $class_l), format_string('Custom class @class is present on link when requested by l()', ['@class' => $class_l]));
// Test #type.
$class_theme = $this->randomMachineName();
$type_link = [
'#type' => 'link',
'#title' => $this->randomMachineName(),
'#url' => Url::fromRoute('<current>'),
'#options' => [
'attributes' => [
'class' => [$class_theme],
],
],
];
$link_theme = $renderer->renderRoot($type_link);
$this->assertTrue($this->hasAttribute('class', $link_theme, $class_theme), format_string('Custom class @class is present on link when requested by #type', ['@class' => $class_theme]));
}
/**
* Tests that link functions support render arrays as 'text'.
*/
public function testLinkRenderArrayText() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = $this->container->get('renderer');
// Build a link with the link generator for reference.
$l = \Drupal::l('foo', Url::fromUri('https://www.drupal.org'));
// Test a renderable array passed to the link generator.
$renderer->executeInRenderContext(new RenderContext(), function () use ($renderer, $l) {
$renderable_text = ['#markup' => 'foo'];
$l_renderable_text = \Drupal::l($renderable_text, Url::fromUri('https://www.drupal.org'));
$this->assertEqual($l_renderable_text, $l);
});
// Test a themed link with plain text 'text'.
$type_link_plain_array = [
'#type' => 'link',
'#title' => 'foo',
'#url' => Url::fromUri('https://www.drupal.org'),
];
$type_link_plain = $renderer->renderRoot($type_link_plain_array);
$this->assertEqual($type_link_plain, $l);
// Build a themed link with renderable 'text'.
$type_link_nested_array = [
'#type' => 'link',
'#title' => ['#markup' => 'foo'],
'#url' => Url::fromUri('https://www.drupal.org'),
];
$type_link_nested = $renderer->renderRoot($type_link_nested_array);
$this->assertEqual($type_link_nested, $l);
}
/**
* Checks for class existence in link.
*
* @param $link
* URL to search.
* @param $class
* Element class to search for.
*
* @return bool
* TRUE if the class is found, FALSE otherwise.
*/
private function hasAttribute($attribute, $link, $class) {
return preg_match('|' . $attribute . '="([^\"\s]+\s+)*' . $class . '|', $link);
}
/**
* Tests UrlHelper::filterQueryParameters().
*/
public function testDrupalGetQueryParameters() {
$original = [
'a' => 1,
'b' => [
'd' => 4,
'e' => [
'f' => 5,
],
],
'c' => 3,
];
// First-level exclusion.
$result = $original;
unset($result['b']);
$this->assertEqual(UrlHelper::filterQueryParameters($original, ['b']), $result, "'b' was removed.");
// Second-level exclusion.
$result = $original;
unset($result['b']['d']);
$this->assertEqual(UrlHelper::filterQueryParameters($original, ['b[d]']), $result, "'b[d]' was removed.");
// Third-level exclusion.
$result = $original;
unset($result['b']['e']['f']);
$this->assertEqual(UrlHelper::filterQueryParameters($original, ['b[e][f]']), $result, "'b[e][f]' was removed.");
// Multiple exclusions.
$result = $original;
unset($result['a'], $result['b']['e'], $result['c']);
$this->assertEqual(UrlHelper::filterQueryParameters($original, ['a', 'b[e]', 'c']), $result, "'a', 'b[e]', 'c' were removed.");
}
/**
* Tests UrlHelper::parse().
*/
public function testDrupalParseUrl() {
// Relative, absolute, and external URLs, without/with explicit script path,
// without/with Drupal path.
foreach (['', '/', 'https://www.drupal.org/'] as $absolute) {
foreach (['', 'index.php/'] as $script) {
foreach (['', 'foo/bar'] as $path) {
$url = $absolute . $script . $path . '?foo=bar&bar=baz&baz#foo';
$expected = [
'path' => $absolute . $script . $path,
'query' => ['foo' => 'bar', 'bar' => 'baz', 'baz' => ''],
'fragment' => 'foo',
];
$this->assertEqual(UrlHelper::parse($url), $expected, 'URL parsed correctly.');
}
}
}
// Relative URL that is known to confuse parse_url().
$url = 'foo/bar:1';
$result = [
'path' => 'foo/bar:1',
'query' => [],
'fragment' => '',
];
$this->assertEqual(UrlHelper::parse($url), $result, 'Relative URL parsed correctly.');
// Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
$url = 'https://www.drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
$this->assertTrue(UrlHelper::isExternal($url), 'Correctly identified an external URL.');
// Test that UrlHelper::parse() does not allow spoofing a URL to force a malicious redirect.
$parts = UrlHelper::parse('forged:http://cwe.mitre.org/data/definitions/601.html');
$this->assertFalse(UrlHelper::isValid($parts['path'], TRUE), '\Drupal\Component\Utility\UrlHelper::isValid() correctly parsed a forged URL.');
}
/**
* Tests external URL handling.
*/
public function testExternalUrls() {
$test_url = 'https://www.drupal.org/';
// Verify external URL can contain a fragment.
$url = $test_url . '#drupal';
$result = Url::fromUri($url)->toString();
$this->assertEqual($url, $result, 'External URL with fragment works without a fragment in $options.');
// Verify fragment can be overridden in an external URL.
$url = $test_url . '#drupal';
$fragment = $this->randomMachineName(10);
$result = Url::fromUri($url, ['fragment' => $fragment])->toString();
$this->assertEqual($test_url . '#' . $fragment, $result, 'External URL fragment is overridden with a custom fragment in $options.');
// Verify external URL can contain a query string.
$url = $test_url . '?drupal=awesome';
$result = Url::fromUri($url)->toString();
$this->assertEqual($url, $result);
// Verify external URL can be extended with a query string.
$url = $test_url;
$query = [$this->randomMachineName(5) => $this->randomMachineName(5)];
$result = Url::fromUri($url, ['query' => $query])->toString();
$this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, 'External URL can be extended with a query string in $options.');
// Verify query string can be extended in an external URL.
$url = $test_url . '?drupal=awesome';
$query = [$this->randomMachineName(5) => $this->randomMachineName(5)];
$result = Url::fromUri($url, ['query' => $query])->toString();
$this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result);
}
}
@@ -14,11 +14,15 @@ use Drupal\system\Tests\Cache\PageCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
@trigger_error(__NAMESPACE__ . '\EntityCacheTagsTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\system\Functional\Entity\EntityCacheTagsTestBase. See https://www.drupal.org/node/2946549.', E_USER_DEPRECATED);
/**
* Provides helper methods for Entity cache tags tests.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use \Drupal\Tests\system\Functional\Entity\EntityCacheTagsTestBase instead.
*
* @see https://www.drupal.org/node/2946549
*/
abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
@@ -5,8 +5,15 @@ namespace Drupal\system\Tests\Entity;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\entity_test\FieldStorageDefinition;
@trigger_error(__NAMESPACE__ . '\EntityDefinitionTestTrait is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait. See https://www.drupal.org/node/2946549.', E_USER_DEPRECATED);
/**
* Provides some test methods used to update existing entity definitions.
*
* @deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0.
* Use \Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait.
*
* @see https://www.drupal.org/node/2946549
*/
trait EntityDefinitionTestTrait {
@@ -1,168 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\simpletest\WebTestBase;
/**
* Tests the entity form.
*
* @group Entity
*/
class EntityFormTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['entity_test', 'language'];
protected function setUp() {
parent::setUp();
$web_user = $this->drupalCreateUser(['administer entity_test content', 'view test entity']);
$this->drupalLogin($web_user);
// Add a language.
ConfigurableLanguage::createFromLangcode('ro')->save();
}
/**
* Tests basic form CRUD functionality.
*/
public function testFormCRUD() {
// All entity variations have to have the same results.
foreach (entity_test_entity_types() as $entity_type) {
$this->doTestFormCRUD($entity_type);
}
}
/**
* Tests basic multilingual form CRUD functionality.
*/
public function testMultilingualFormCRUD() {
// All entity variations have to have the same results.
foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) {
$this->doTestMultilingualFormCRUD($entity_type);
}
}
/**
* Tests hook_entity_form_display_alter().
*
* @see entity_test_entity_form_display_alter()
*/
public function testEntityFormDisplayAlter() {
$this->drupalGet('entity_test/add');
$altered_field = $this->xpath('//input[@name="field_test_text[0][value]" and @size="42"]');
$this->assertTrue(count($altered_field) === 1, 'The altered field has the correct size value.');
}
/**
* Executes the form CRUD tests for the given entity type.
*
* @param string $entity_type
* The entity type to run the tests with.
*/
protected function doTestFormCRUD($entity_type) {
$name1 = $this->randomMachineName(8);
$name2 = $this->randomMachineName(10);
$edit = [
'name[0][value]' => $name1,
'field_test_text[0][value]' => $this->randomMachineName(16),
];
$this->drupalPostForm($entity_type . '/add', $edit, t('Save'));
$entity = $this->loadEntityByName($entity_type, $name1);
$this->assertTrue($entity, format_string('%entity_type: Entity found in the database.', ['%entity_type' => $entity_type]));
$edit['name[0][value]'] = $name2;
$this->drupalPostForm($entity_type . '/manage/' . $entity->id() . '/edit', $edit, t('Save'));
$entity = $this->loadEntityByName($entity_type, $name1);
$this->assertFalse($entity, format_string('%entity_type: The entity has been modified.', ['%entity_type' => $entity_type]));
$entity = $this->loadEntityByName($entity_type, $name2);
$this->assertTrue($entity, format_string('%entity_type: Modified entity found in the database.', ['%entity_type' => $entity_type]));
$this->assertNotEqual($entity->name->value, $name1, format_string('%entity_type: The entity name has been modified.', ['%entity_type' => $entity_type]));
$this->drupalGet($entity_type . '/manage/' . $entity->id() . '/edit');
$this->clickLink(t('Delete'));
$this->drupalPostForm(NULL, [], t('Delete'));
$entity = $this->loadEntityByName($entity_type, $name2);
$this->assertFalse($entity, format_string('%entity_type: Entity not found in the database.', ['%entity_type' => $entity_type]));
}
/**
* Executes the multilingual form CRUD tests for the given entity type ID.
*
* @param string $entity_type_id
* The ID of entity type to run the tests with.
*/
protected function doTestMultilingualFormCRUD($entity_type_id) {
$name1 = $this->randomMachineName(8);
$name1_ro = $this->randomMachineName(9);
$name2_ro = $this->randomMachineName(11);
$edit = [
'name[0][value]' => $name1,
'field_test_text[0][value]' => $this->randomMachineName(16),
];
$this->drupalPostForm($entity_type_id . '/add', $edit, t('Save'));
$entity = $this->loadEntityByName($entity_type_id, $name1);
$this->assertTrue($entity, format_string('%entity_type: Entity found in the database.', ['%entity_type' => $entity_type_id]));
// Add a translation to the newly created entity without using the Content
// translation module.
$entity->addTranslation('ro', ['name' => $name1_ro])->save();
$translated_entity = $this->loadEntityByName($entity_type_id, $name1)->getTranslation('ro');
$this->assertEqual($translated_entity->name->value, $name1_ro, format_string('%entity_type: The translation has been added.', ['%entity_type' => $entity_type_id]));
$edit['name[0][value]'] = $name2_ro;
$this->drupalPostForm('ro/' . $entity_type_id . '/manage/' . $entity->id() . '/edit', $edit, t('Save'));
$translated_entity = $this->loadEntityByName($entity_type_id, $name1)->getTranslation('ro');
$this->assertTrue($translated_entity, format_string('%entity_type: Modified translation found in the database.', ['%entity_type' => $entity_type_id]));
$this->assertEqual($translated_entity->name->value, $name2_ro, format_string('%entity_type: The name of the translation has been modified.', ['%entity_type' => $entity_type_id]));
$this->drupalGet('ro/' . $entity_type_id . '/manage/' . $entity->id() . '/edit');
$this->clickLink(t('Delete'));
$this->drupalPostForm(NULL, [], t('Delete Romanian translation'));
$entity = $this->loadEntityByName($entity_type_id, $name1);
$this->assertNotNull($entity, format_string('%entity_type: The original entity still exists.', ['%entity_type' => $entity_type_id]));
$this->assertFalse($entity->hasTranslation('ro'), format_string('%entity_type: Entity translation does not exist anymore.', ['%entity_type' => $entity_type_id]));
}
/**
* Loads a test entity by name always resetting the storage cache.
*/
protected function loadEntityByName($entity_type, $name) {
// Always load the entity from the database to ensure that changes are
// correctly picked up.
$entity_storage = $this->container->get('entity.manager')->getStorage($entity_type);
$entity_storage->resetCache();
$entities = $entity_storage->loadByProperties(['name' => $name]);
return $entities ? current($entities) : NULL;
}
/**
* Checks that validation handlers works as expected.
*/
public function testValidationHandlers() {
/** @var \Drupal\Core\State\StateInterface $state */
$state = $this->container->get('state');
// Check that from-level validation handlers can be defined and can alter
// the form array.
$state->set('entity_test.form.validate.test', 'form-level');
$this->drupalPostForm('entity_test/add', [], 'Save');
$this->assertTrue($state->get('entity_test.form.validate.result'), 'Form-level validation handlers behave correctly.');
// Check that defining a button-level validation handler causes an exception
// to be thrown.
$state->set('entity_test.form.validate.test', 'button-level');
$this->drupalPostForm('entity_test/add', [], 'Save');
$this->assertEqual($state->get('entity_test.form.save.exception'), 'Drupal\Core\Entity\EntityStorageException: Entity validation was skipped.', 'Button-level validation handlers behave correctly.');
}
}
@@ -1,118 +0,0 @@
<?php
namespace Drupal\system\Tests\Entity;
use Drupal\Core\Language\LanguageInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\simpletest\WebTestBase;
/**
* Tests entity translation form.
*
* @group Entity
*/
class EntityTranslationFormTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['entity_test', 'language', 'node'];
protected $langcodes;
protected function setUp() {
parent::setUp();
// Enable translations for the test entity type.
\Drupal::state()->set('entity_test.translation', TRUE);
// Create test languages.
$this->langcodes = [];
for ($i = 0; $i < 2; ++$i) {
$language = ConfigurableLanguage::create([
'id' => 'l' . $i,
'label' => $this->randomString(),
]);
$this->langcodes[$i] = $language->id();
$language->save();
}
}
/**
* Tests entity form language.
*/
public function testEntityFormLanguage() {
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
$web_user = $this->drupalCreateUser(['create page content', 'edit own page content', 'administer content types']);
$this->drupalLogin($web_user);
// Create a node with language LanguageInterface::LANGCODE_NOT_SPECIFIED.
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName(8);
$edit['body[0][value]'] = $this->randomMachineName(16);
$this->drupalGet('node/add/page');
$form_langcode = \Drupal::state()->get('entity_test.form_langcode');
$this->drupalPostForm(NULL, $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertTrue($node->language()->getId() == $form_langcode, 'Form language is the same as the entity language.');
// Edit the node and test the form language.
$this->drupalGet($this->langcodes[0] . '/node/' . $node->id() . '/edit');
$form_langcode = \Drupal::state()->get('entity_test.form_langcode');
$this->assertTrue($node->language()->getId() == $form_langcode, 'Form language is the same as the entity language.');
// Explicitly set form langcode.
$langcode = $this->langcodes[0];
$form_state_additions['langcode'] = $langcode;
\Drupal::service('entity.form_builder')->getForm($node, 'default', $form_state_additions);
$form_langcode = \Drupal::state()->get('entity_test.form_langcode');
$this->assertTrue($langcode == $form_langcode, 'Form language is the same as the language parameter.');
// Enable language selector.
$this->drupalGet('admin/structure/types/manage/page');
$edit = ['language_configuration[language_alterable]' => TRUE, 'language_configuration[langcode]' => LanguageInterface::LANGCODE_NOT_SPECIFIED];
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', ['%type' => 'Basic page']), 'Basic page content type has been updated.');
// Create a node with language.
$edit = [];
$langcode = $this->langcodes[0];
$edit['title[0][value]'] = $this->randomMachineName(8);
$edit['body[0][value]'] = $this->randomMachineName(16);
$edit['langcode[0][value]'] = $langcode;
$this->drupalPostForm('node/add/page', $edit, t('Save'));
$this->assertText(t('Basic page @title has been created.', ['@title' => $edit['title[0][value]']]), 'Basic page created.');
// Verify that the creation message contains a link to a node.
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'node/']);
$this->assert(isset($view_link), 'The message area contains a link to a node');
// Check to make sure the node was created.
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertTrue($node, 'Node found in database.');
// Make body translatable.
$field_storage = FieldStorageConfig::loadByName('node', 'body');
$field_storage->setTranslatable(TRUE);
$field_storage->save();
$field_storage = FieldStorageConfig::loadByName('node', 'body');
$this->assertTrue($field_storage->isTranslatable(), 'Field body is translatable.');
// Create a body translation and check the form language.
$langcode2 = $this->langcodes[1];
$translation = $node->addTranslation($langcode2);
$translation->title->value = $this->randomString();
$translation->body->value = $this->randomMachineName(16);
$translation->setOwnerId($web_user->id());
$node->save();
$this->drupalGet($langcode2 . '/node/' . $node->id() . '/edit');
$form_langcode = \Drupal::state()->get('entity_test.form_langcode');
$this->assertTrue($langcode2 == $form_langcode, "Node edit form language is $langcode2.");
}
}
@@ -2,6 +2,8 @@
namespace Drupal\system\Tests\Entity;
@trigger_error(__FILE__ . ' is deprecated in Drupal 8.1.0 and will be removed before Drupal 9.0.0. Use \Drupal\KernelTests\Core\Entity\EntityKernelTestBase instead.', E_USER_DEPRECATED);
use Drupal\simpletest\KernelTestBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\user\Entity\Role;
@@ -10,7 +12,7 @@ use Drupal\user\Entity\User;
/**
* Defines an abstract test base for entity unit tests.
*
* @deprecated in Drupal 8.1.x, will be removed before Drupal 8.2.x. Use
* @deprecated in Drupal 8.1.0, will be removed before Drupal 9.0.0. Use
* \Drupal\KernelTests\Core\Entity\EntityKernelTestBase instead.
*/
abstract class EntityUnitTestBase extends KernelTestBase {
@@ -7,8 +7,15 @@ use Drupal\Core\Language\LanguageInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
@trigger_error(__NAMESPACE__ . '\EntityWithUriCacheTagsTestBase is deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\system\Functional\Entity\EntityWithUriCacheTagsTestBase. See https://www.drupal.org/node/2946549.', E_USER_DEPRECATED);
/**
* Provides helper methods for Entity cache tags tests; for entities with URIs.
*
* @deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0.
* Use \Drupal\Tests\system\Functional\Entity\EntityWithUriCacheTagsTestBase.
*
* @see https://www.drupal.org/node/2946549
*/
abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
@@ -163,7 +163,6 @@ class ElementsTableSelectTest extends WebTestBase {
$this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, 'Do not display a "Select all" checkbox when #multiple is FALSE, even when #js_select is TRUE.');
}
/**
* Test the whether the option checker gives an error on invalid tableselect values for checkboxes.
*/
@@ -187,7 +186,6 @@ class ElementsTableSelectTest extends WebTestBase {
}
/**
* Test the whether the option checker gives an error on invalid tableselect values for radios.
*/
@@ -211,7 +209,6 @@ class ElementsTableSelectTest extends WebTestBase {
$this->assertTrue(isset($errors['tableselect']), 'Option checker disallows invalid values for radio buttons.');
}
/**
* Helper function for the option check test to submit a form while collecting errors.
*
@@ -248,7 +245,7 @@ class ElementsTableSelectTest extends WebTestBase {
$errors = $form_state->getErrors();
// Clear errors and messages.
drupal_get_messages();
\Drupal::messenger()->deleteAll();
$form_state->clearErrors();
// Return the processed form together with form_state and errors
@@ -5,7 +5,7 @@ namespace Drupal\system\Tests\Image;
@trigger_error(__FILE__ . ' is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Use Drupal\FunctionalTests\Image\ToolkitTestBase instead. See https://www.drupal.org/node/2862641.', E_USER_DEPRECATED);
use Drupal\simpletest\WebTestBase;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
/**
* Base class for image manipulation testing.
@@ -106,10 +106,10 @@ abstract class ToolkitTestBase extends WebTestBase {
// Determine if there were any expected that were not called.
$uncalled = array_diff($expected, $actual);
if (count($uncalled)) {
$this->assertTrue(FALSE, SafeMarkup::format('Expected operations %expected to be called but %uncalled was not called.', ['%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled)]));
$this->assertTrue(FALSE, new FormattableMarkup('Expected operations %expected to be called but %uncalled was not called.', ['%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled)]));
}
else {
$this->assertTrue(TRUE, SafeMarkup::format('All the expected operations were called: %expected', ['%expected' => implode(', ', $expected)]));
$this->assertTrue(TRUE, new FormattableMarkup('All the expected operations were called: %expected', ['%expected' => implode(', ', $expected)]));
}
// Determine if there were any unexpected calls.
@@ -117,7 +117,7 @@ abstract class ToolkitTestBase extends WebTestBase {
// count it as an error.
$unexpected = array_diff($actual, $expected);
if (count($unexpected) && (!in_array('apply', $expected) || count(array_intersect($unexpected, $operations)) !== count($unexpected))) {
$this->assertTrue(FALSE, SafeMarkup::format('Unexpected operations were called: %unexpected.', ['%unexpected' => implode(', ', $unexpected)]));
$this->assertTrue(FALSE, new FormattableMarkup('Unexpected operations were called: %unexpected.', ['%unexpected' => implode(', ', $unexpected)]));
}
else {
$this->assertTrue(TRUE, 'No unexpected operations were called.');
@@ -2,6 +2,8 @@
namespace Drupal\system\Tests\Installer;
@trigger_error(__NAMESPACE__ . '\ConfigAfterInstallerTestBase is deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\FunctionalTests\Installer\ConfigAfterInstallerTestBase.', E_USER_DEPRECATED);
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageInterface;
@@ -10,6 +12,9 @@ use Drupal\simpletest\InstallerTestBase;
/**
* Provides a class for install profiles to check their installed config.
*
* @deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0.
* Use \Drupal\FunctionalTests\Installer\ConfigAfterInstallerTestBase.
*/
abstract class ConfigAfterInstallerTestBase extends InstallerTestBase {
@@ -1,131 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Database\Database;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
use Drupal\simpletest\InstallerTestBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests distribution profile support with existing settings.
*
* @group Installer
*/
class DistributionProfileExistingSettingsTest extends InstallerTestBase {
/**
* The distribution profile info.
*
* @var array
*/
protected $info;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->info = [
'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY,
'name' => 'Distribution profile',
'distribution' => [
'name' => 'My Distribution',
'install' => [
'theme' => 'bartik',
],
],
];
// File API functions are not available yet.
$path = $this->siteDirectory . '/profiles/mydistro';
mkdir($path, 0777, TRUE);
file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info));
// Pre-configure hash salt.
// Any string is valid, so simply use the class name of this test.
$this->settings['settings']['hash_salt'] = (object) [
'value' => __CLASS__,
'required' => TRUE,
];
// Pre-configure database credentials.
$connection_info = Database::getConnectionInfo();
unset($connection_info['default']['pdo']);
unset($connection_info['default']['init_commands']);
$this->settings['databases']['default'] = (object) [
'value' => $connection_info,
'required' => TRUE,
];
// Use the kernel to find the site path because the site.path service should
// not be available at this point in the install process.
$site_path = DrupalKernel::findSitePath(Request::createFromGlobals());
// Pre-configure config directories.
$this->settings['config_directories'] = [
CONFIG_SYNC_DIRECTORY => (object) [
'value' => $site_path . '/files/config_staging',
'required' => TRUE,
],
];
mkdir($this->settings['config_directories'][CONFIG_SYNC_DIRECTORY]->value, 0777, TRUE);
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// Make settings file not writable.
$filename = $this->siteDirectory . '/settings.php';
// Make the settings file read-only.
// Not using File API; a potential error must trigger a PHP warning.
chmod($filename, 0444);
// Verify that the distribution name appears.
$this->assertRaw($this->info['distribution']['name']);
// Verify that the requested theme is used.
$this->assertRaw($this->info['distribution']['install']['theme']);
// Verify that the "Choose profile" step does not appear.
$this->assertNoText('profile');
parent::setUpLanguage();
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// This step is skipped, because there is a distribution profile.
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// This step should not appear, since settings.php is fully configured
// already.
}
/**
* Confirms that the installation succeeded.
*/
public function testInstalled() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Confirm that we are logged-in after installation.
$this->assertText($this->rootUser->getUsername());
// Confirm that Drupal recognizes this distribution as the current profile.
$this->assertEqual(\Drupal::installProfile(), 'mydistro');
$this->assertNull(Settings::get('install_profile'), 'The install profile has not been written to settings.php.');
$this->assertEqual($this->config('core.extension')->get('profile'), 'mydistro', 'The install profile has been written to core.extension configuration.');
$this->rebuildContainer();
$this->pass('Container can be rebuilt even though distribution is not written to settings.php.');
$this->assertEqual(\Drupal::installProfile(), 'mydistro');
}
}
@@ -1,81 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Serialization\Yaml;
use Drupal\Core\Site\Settings;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests distribution profile support.
*
* @group Installer
*/
class DistributionProfileTest extends InstallerTestBase {
/**
* The distribution profile info.
*
* @var array
*/
protected $info;
protected function setUp() {
$this->info = [
'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY,
'name' => 'Distribution profile',
'distribution' => [
'name' => 'My Distribution',
'install' => [
'theme' => 'bartik',
],
],
];
// File API functions are not available yet.
$path = $this->siteDirectory . '/profiles/mydistro';
mkdir($path, 0777, TRUE);
file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info));
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// Verify that the distribution name appears.
$this->assertRaw($this->info['distribution']['name']);
// Verify that the distribution name is used in the site title.
$this->assertTitle('Choose language | ' . $this->info['distribution']['name']);
// Verify that the requested theme is used.
$this->assertRaw($this->info['distribution']['install']['theme']);
// Verify that the "Choose profile" step does not appear.
$this->assertNoText('profile');
parent::setUpLanguage();
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// This step is skipped, because there is a distribution profile.
}
/**
* Confirms that the installation succeeded.
*/
public function testInstalled() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Confirm that we are logged-in after installation.
$this->assertText($this->rootUser->getUsername());
// Confirm that Drupal recognizes this distribution as the current profile.
$this->assertEqual(\Drupal::installProfile(), 'mydistro');
$this->assertEqual(Settings::get('install_profile'), 'mydistro', 'The install profile has been written to settings.php.');
$this->assertEqual($this->config('core.extension')->get('profile'), 'mydistro', 'The install profile has been written to core.extension configuration.');
}
}
@@ -1,142 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Serialization\Yaml;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests distribution profile support with a 'langcode' query string.
*
* @group Installer
*
* @see \Drupal\system\Tests\Installer\DistributionProfileTranslationTest
*/
class DistributionProfileTranslationQueryTest extends InstallerTestBase {
/**
* {@inheritdoc}
*/
protected $langcode = 'de';
/**
* The distribution profile info.
*
* @var array
*/
protected $info;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->info = [
'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY,
'name' => 'Distribution profile',
'distribution' => [
'name' => 'My Distribution',
'langcode' => $this->langcode,
'install' => [
'theme' => 'bartik',
],
],
];
// File API functions are not available yet.
$path = $this->siteDirectory . '/profiles/mydistro';
mkdir($path, 0777, TRUE);
file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info));
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function visitInstaller() {
// Place a custom local translation in the translations directory.
mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de'));
file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.fr.po', $this->getPo('fr'));
// Pass a different language code than the one set in the distribution
// profile. This distribution language should still be used.
// The unrouted URL assembler does not exist at this point, so we build the
// URL ourselves.
$this->drupalGet($GLOBALS['base_url'] . '/core/install.php' . '?langcode=fr');
}
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// This step is skipped, because the distribution profile uses a fixed
// language.
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// This step is skipped, because there is a distribution profile.
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// The language should have been automatically detected, all following
// screens should be translated already.
$elements = $this->xpath('//input[@type="submit"]/@value');
$this->assertEqual((string) current($elements), 'Save and continue de');
$this->translations['Save and continue'] = 'Save and continue de';
// Check the language direction.
$direction = (string) current($this->xpath('/html/@dir'));
$this->assertEqual($direction, 'ltr');
// Verify that the distribution name appears.
$this->assertRaw($this->info['distribution']['name']);
// Verify that the requested theme is used.
$this->assertRaw($this->info['distribution']['install']['theme']);
// Verify that the "Choose profile" step does not appear.
$this->assertNoText('profile');
parent::setUpSettings();
}
/**
* Confirms that the installation succeeded.
*/
public function testInstalled() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Confirm that we are logged-in after installation.
$this->assertText($this->rootUser->getDisplayName());
// Verify German was configured but not English.
$this->drupalGet('admin/config/regional/language');
$this->assertText('German');
$this->assertNoText('English');
}
/**
* Returns the string for the test .po file.
*
* @param string $langcode
* The language code.
* @return string
* Contents for the test .po file.
*/
protected function getPo($langcode) {
return <<<ENDPO
msgid ""
msgstr ""
msgid "Save and continue"
msgstr "Save and continue $langcode"
ENDPO;
}
}
@@ -1,138 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Serialization\Yaml;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests distribution profile support.
*
* @group Installer
*
* @see \Drupal\system\Tests\Installer\DistributionProfileTest
*/
class DistributionProfileTranslationTest extends InstallerTestBase {
/**
* {@inheritdoc}
*/
protected $langcode = 'de';
/**
* The distribution profile info.
*
* @var array
*/
protected $info;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->info = [
'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY,
'name' => 'Distribution profile',
'distribution' => [
'name' => 'My Distribution',
'langcode' => $this->langcode,
'install' => [
'theme' => 'bartik',
],
],
];
// File API functions are not available yet.
$path = $this->siteDirectory . '/profiles/mydistro';
mkdir($path, 0777, TRUE);
file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info));
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function visitInstaller() {
// Place a custom local translation in the translations directory.
mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de'));
parent::visitInstaller();
}
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// This step is skipped, because the distribution profile uses a fixed
// language.
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// This step is skipped, because there is a distribution profile.
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// The language should have been automatically detected, all following
// screens should be translated already.
$elements = $this->xpath('//input[@type="submit"]/@value');
$this->assertEqual((string) current($elements), 'Save and continue de');
$this->translations['Save and continue'] = 'Save and continue de';
// Check the language direction.
$direction = (string) current($this->xpath('/html/@dir'));
$this->assertEqual($direction, 'ltr');
// Verify that the distribution name appears.
$this->assertRaw($this->info['distribution']['name']);
// Verify that the requested theme is used.
$this->assertRaw($this->info['distribution']['install']['theme']);
// Verify that the "Choose profile" step does not appear.
$this->assertNoText('profile');
parent::setUpSettings();
}
/**
* Confirms that the installation succeeded.
*/
public function testInstalled() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Confirm that we are logged-in after installation.
$this->assertText($this->rootUser->getDisplayName());
// Verify German was configured but not English.
$this->drupalGet('admin/config/regional/language');
$this->assertText('German');
$this->assertNoText('English');
}
/**
* Returns the string for the test .po file.
*
* @param string $langcode
* The language code.
* @return string
* Contents for the test .po file.
*/
protected function getPo($langcode) {
return <<<ENDPO
msgid ""
msgstr ""
msgid "Save and continue"
msgstr "Save and continue $langcode"
ENDPO;
}
}
@@ -1,62 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Component\Utility\Crypt;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests the installer when a config_directory set up but does not exist.
*
* @group Installer
*/
class InstallerConfigDirectorySetNoDirectoryErrorTest extends InstallerTestBase {
/**
* The directory where the sync directory should be created during install.
*
* @var string
*/
protected $configDirectory;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->configDirectory = $this->publicFilesDirectory . '/config_' . Crypt::randomBytesBase64();
$this->settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) [
'value' => $this->configDirectory . '/sync',
'required' => TRUE,
];
// Create the files directory early so we can test the error case.
mkdir($this->publicFilesDirectory);
// Create a file so the directory can not be created.
file_put_contents($this->configDirectory, 'Test');
parent::setUp();
}
/**
* Installer step: Configure settings.
*/
protected function setUpSettings() {
// This step should not appear as we had a failure prior to the settings
// screen.
}
/**
* {@inheritdoc}
*/
protected function setUpSite() {
// This step should not appear as we had a failure prior to the settings
// screen.
}
/**
* Verifies that installation failed.
*/
public function testError() {
$this->assertText("An automated attempt to create the directory {$this->configDirectory}/sync failed, possibly due to a permissions problem.");
$this->assertFalse(file_exists($this->configDirectory . '/sync') && is_dir($this->configDirectory . '/sync'), "The directory {$this->configDirectory}/sync does not exist.");
}
}
@@ -1,49 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Component\Utility\Crypt;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests the installer when a config_directory set up but does not exist.
*
* @group Installer
*/
class InstallerConfigDirectorySetNoDirectoryTest extends InstallerTestBase {
/**
* The sync directory created during the install.
*
* @var string
*/
protected $syncDirectory;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->syncDirectory = $this->publicFilesDirectory . '/config_' . Crypt::randomBytesBase64() . '/sync';
$this->settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) [
'value' => $this->syncDirectory,
'required' => TRUE,
];
// Other directories will be created too.
$this->settings['config_directories']['custom'] = (object) [
'value' => $this->publicFilesDirectory . '/config_custom',
'required' => TRUE,
];
parent::setUp();
}
/**
* Verifies that installation succeeded.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
$this->assertTrue(file_exists($this->syncDirectory) && is_dir($this->syncDirectory), "The directory {$this->syncDirectory} exists.");
$this->assertTrue(file_exists($this->publicFilesDirectory . '/config_custom') && is_dir($this->publicFilesDirectory . '/config_custom'), "The directory {$this->publicFilesDirectory}/custom_config exists.");
}
}
@@ -1,41 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Database\Database;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests the installer with database errors.
*
* @group Installer
*/
class InstallerDatabaseErrorMessagesTest extends InstallerTestBase {
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// We are creating a table here to force an error in the installer because
// it will try and create the drupal_install_test table as this is part of
// the standard database tests performed by the installer in
// Drupal\Core\Database\Install\Tasks.
Database::getConnection('default')->query('CREATE TABLE {drupal_install_test} (id int NULL)');
parent::setUpSettings();
}
/**
* {@inheritdoc}
*/
protected function setUpSite() {
// This step should not appear as we had a failure on the settings screen.
}
/**
* Verifies that the error message in the settings step is correct.
*/
public function testSetUpSettingsErrorMessage() {
$this->assertRaw('<ul><li>Failed to <strong>CREATE</strong> a test table');
}
}
@@ -1,31 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests the installer with empty settings file.
*
* @group Installer
*/
class InstallerEmptySettingsTest extends InstallerTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
// Create an empty settings.php file.
touch($this->siteDirectory . '/settings.php');
parent::setUp();
}
/**
* Verifies that installation succeeded.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
}
}
@@ -1,44 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests the installer when a config_directory has already been set up.
*
* @group Installer
*/
class InstallerExistingConfigDirectoryTest extends InstallerTestBase {
/**
* The expected file perms of the folder.
*
* @var int
*/
protected $expectedFilePerms;
/**
* {@inheritdoc}
*/
protected function setUp() {
mkdir($this->siteDirectory . '/config_read_only', 0444);
$this->expectedFilePerms = fileperms($this->siteDirectory . '/config_read_only');
$this->settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) [
'value' => $this->siteDirectory . '/config_read_only',
'required' => TRUE,
];
parent::setUp();
}
/**
* Verifies that installation succeeded.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
$this->assertEqual($this->expectedFilePerms, fileperms($this->siteDirectory . '/config_read_only'));
$this->assertEqual([], glob($this->siteDirectory . '/config_read_only/*'), 'The sync directory is empty after install because it is read-only.');
}
}
@@ -1,62 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\InstallerTestBase;
use Drupal\Core\Database\Database;
/**
* Tests the installer with an existing settings file with database connection
* info.
*
* @group Installer
*/
class InstallerExistingDatabaseSettingsTest extends InstallerTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
// Pre-configure database credentials in settings.php.
$connection_info = Database::getConnectionInfo();
unset($connection_info['default']['pdo']);
unset($connection_info['default']['init_commands']);
$this->settings['databases']['default'] = (object) [
'value' => $connection_info,
'required' => TRUE,
];
parent::setUp();
}
/**
* {@inheritdoc}
*
* @todo The database settings form is not supposed to appear if settings.php
* contains a valid database connection already (but e.g. no config
* directories yet).
*/
protected function setUpSettings() {
// All database settings should be pre-configured, except password.
$values = $this->parameters['forms']['install_settings_form'];
$driver = $values['driver'];
$edit = [];
if (isset($values[$driver]['password']) && $values[$driver]['password'] !== '') {
$edit = $this->translatePostValues([
$driver => [
'password' => $values[$driver]['password'],
],
]);
}
$this->drupalPostForm(NULL, $edit, $this->translations['Save and continue']);
}
/**
* Verifies that installation succeeded.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
}
}
@@ -1,40 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests the installer with an existing Drupal installation.
*
* @group Installer
*/
class InstallerExistingInstallationTest extends InstallerTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
}
/**
* Verifies that Drupal can't be reinstalled while an existing installation is
* available.
*/
public function testInstaller() {
// Verify that Drupal can't be immediately reinstalled.
$this->visitInstaller();
$this->assertRaw('Drupal already installed');
// Delete settings.php and attempt to reinstall again.
unlink($this->siteDirectory . '/settings.php');
$this->visitInstaller();
$this->setUpLanguage();
$this->setUpProfile();
$this->setUpRequirementsProblem();
$this->setUpSettings();
$this->assertRaw('Drupal already installed');
}
}
@@ -1,108 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\DrupalKernel;
use Drupal\simpletest\InstallerTestBase;
use Drupal\Core\Database\Database;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests installer breaks with a profile mismatch and a read-only settings.php.
*
* @group Installer
*/
class InstallerExistingSettingsMismatchProfileBrokenTest extends InstallerTestBase {
/**
* {@inheritdoc}
*
* Configures a preexisting settings.php file without an install_profile
* setting before invoking the interactive installer.
*/
protected function setUp() {
// Pre-configure hash salt.
// Any string is valid, so simply use the class name of this test.
$this->settings['settings']['hash_salt'] = (object) [
'value' => __CLASS__,
'required' => TRUE,
];
// Pre-configure database credentials.
$connection_info = Database::getConnectionInfo();
unset($connection_info['default']['pdo']);
unset($connection_info['default']['init_commands']);
$this->settings['databases']['default'] = (object) [
'value' => $connection_info,
'required' => TRUE,
];
// During interactive install we'll change this to a different profile and
// this test will ensure that the new value is written to settings.php.
$this->settings['settings']['install_profile'] = (object) [
'value' => 'minimal',
'required' => TRUE,
];
// Pre-configure config directories.
$site_path = DrupalKernel::findSitePath(Request::createFromGlobals());
$this->settings['config_directories'] = [
CONFIG_SYNC_DIRECTORY => (object) [
'value' => $site_path . '/files/config_staging',
'required' => TRUE,
],
];
mkdir($this->settings['config_directories'][CONFIG_SYNC_DIRECTORY]->value, 0777, TRUE);
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function visitInstaller() {
// Make settings file not writable. This will break the installer.
$filename = $this->siteDirectory . '/settings.php';
// Make the settings file read-only.
// Not using File API; a potential error must trigger a PHP warning.
chmod($filename, 0444);
$this->drupalGet($GLOBALS['base_url'] . '/core/install.php?langcode=en&profile=testing');
}
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// This step is skipped, because there is a lagcode as a query param.
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// This step is skipped, because there is a profile as a query param.
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// This step should not appear, since settings.php is fully configured
// already.
}
protected function setUpSite() {
// This step should not appear, since settings.php could not be written.
}
/**
* Verifies that installation did not succeed.
*/
public function testBrokenInstaller() {
$this->assertTitle('Install profile mismatch | Drupal');
$this->assertText("The selected profile testing does not match the install_profile setting, which is minimal. Cannot write updated setting to {$this->siteDirectory}/settings.php.");
}
}
@@ -1,101 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
use Drupal\simpletest\InstallerTestBase;
use Drupal\Core\Database\Database;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests the installer with an existing settings file but no install profile.
*
* @group Installer
*/
class InstallerExistingSettingsMismatchProfileTest extends InstallerTestBase {
/**
* {@inheritdoc}
*
* Configures a preexisting settings.php file without an install_profile
* setting before invoking the interactive installer.
*/
protected function setUp() {
// Pre-configure hash salt.
// Any string is valid, so simply use the class name of this test.
$this->settings['settings']['hash_salt'] = (object) [
'value' => __CLASS__,
'required' => TRUE,
];
// Pre-configure database credentials.
$connection_info = Database::getConnectionInfo();
unset($connection_info['default']['pdo']);
unset($connection_info['default']['init_commands']);
$this->settings['databases']['default'] = (object) [
'value' => $connection_info,
'required' => TRUE,
];
// During interactive install we'll change this to a different profile and
// this test will ensure that the new value is written to settings.php.
$this->settings['settings']['install_profile'] = (object) [
'value' => 'minimal',
'required' => TRUE,
];
// Pre-configure config directories.
$this->settings['config_directories'] = [
CONFIG_SYNC_DIRECTORY => (object) [
'value' => DrupalKernel::findSitePath(Request::createFromGlobals()) . '/files/config_sync',
'required' => TRUE,
],
];
mkdir($this->settings['config_directories'][CONFIG_SYNC_DIRECTORY]->value, 0777, TRUE);
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function visitInstaller() {
// Provide profile and language in query string to skip these pages.
$this->drupalGet($GLOBALS['base_url'] . '/core/install.php?langcode=en&profile=testing');
}
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// This step is skipped, because there is a lagcode as a query param.
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// This step is skipped, because there is a profile as a query param.
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// This step should not appear, since settings.php is fully configured
// already.
}
/**
* Verifies that installation succeeded.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
$this->assertEqual('testing', \Drupal::installProfile());
$this->assertEqual('testing', Settings::get('install_profile'), 'Profile was correctly changed to testing in Settings.php');
}
}
@@ -1,70 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\DrupalKernel;
use Drupal\simpletest\InstallerTestBase;
use Drupal\Core\Database\Database;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests the installer with an existing settings file but no install profile.
*
* @group Installer
*/
class InstallerExistingSettingsNoProfileTest extends InstallerTestBase {
/**
* {@inheritdoc}
*
* Configures a preexisting settings.php file without an install_profile
* setting before invoking the interactive installer.
*/
protected function setUp() {
// Pre-configure hash salt.
// Any string is valid, so simply use the class name of this test.
$this->settings['settings']['hash_salt'] = (object) [
'value' => __CLASS__,
'required' => TRUE,
];
// Pre-configure database credentials.
$connection_info = Database::getConnectionInfo();
unset($connection_info['default']['pdo']);
unset($connection_info['default']['init_commands']);
$this->settings['databases']['default'] = (object) [
'value' => $connection_info,
'required' => TRUE,
];
// Pre-configure config directories.
$this->settings['config_directories'] = [
CONFIG_SYNC_DIRECTORY => (object) [
'value' => DrupalKernel::findSitePath(Request::createFromGlobals()) . '/files/config_sync',
'required' => TRUE,
],
];
mkdir($this->settings['config_directories'][CONFIG_SYNC_DIRECTORY]->value, 0777, TRUE);
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// This step should not appear, since settings.php is fully configured
// already.
}
/**
* Verifies that installation succeeded.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
$this->assertEqual('testing', \Drupal::installProfile());
}
}
@@ -1,82 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Site\Settings;
use Drupal\simpletest\InstallerTestBase;
use Drupal\Core\Database\Database;
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests the installer with an existing settings file.
*
* @group Installer
*/
class InstallerExistingSettingsTest extends InstallerTestBase {
/**
* {@inheritdoc}
*
* Fully configures a preexisting settings.php file before invoking the
* interactive installer.
*/
protected function setUp() {
// Pre-configure hash salt.
// Any string is valid, so simply use the class name of this test.
$this->settings['settings']['hash_salt'] = (object) [
'value' => __CLASS__,
'required' => TRUE,
];
// During interactive install we'll change this to a different profile and
// this test will ensure that the new value is written to settings.php.
$this->settings['settings']['install_profile'] = (object) [
'value' => 'minimal',
'required' => TRUE,
];
// Pre-configure database credentials.
$connection_info = Database::getConnectionInfo();
unset($connection_info['default']['pdo']);
unset($connection_info['default']['init_commands']);
$this->settings['databases']['default'] = (object) [
'value' => $connection_info,
'required' => TRUE,
];
// Use the kernel to find the site path because the site.path service should
// not be available at this point in the install process.
$site_path = DrupalKernel::findSitePath(Request::createFromGlobals());
// Pre-configure config directories.
$this->settings['config_directories'] = [
CONFIG_SYNC_DIRECTORY => (object) [
'value' => $site_path . '/files/config_sync',
'required' => TRUE,
],
];
mkdir($this->settings['config_directories'][CONFIG_SYNC_DIRECTORY]->value, 0777, TRUE);
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// This step should not appear, since settings.php is fully configured
// already.
}
/**
* Verifies that installation succeeded.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
$this->assertEqual('testing', \Drupal::installProfile(), 'Profile was changed from minimal to testing during interactive install.');
$this->assertEqual('testing', Settings::get('install_profile'), 'Profile was correctly changed to testing in Settings.php');
}
}
@@ -1,49 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\InstallerTestBase;
/**
* Verifies that the early installer uses the correct language direction.
*
* @group Installer
*/
class InstallerLanguageDirectionTest extends InstallerTestBase {
/**
* Overrides the language code the installer should use.
*
* @var string
*/
protected $langcode = 'ar';
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// Place a custom local translation in the translations directory.
mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.ar.po', "msgid \"\"\nmsgstr \"\"\nmsgid \"Save and continue\"\nmsgstr \"Save and continue Arabic\"");
parent::setUpLanguage();
// After selecting a different language than English, all following screens
// should be translated already.
$elements = $this->xpath('//input[@type="submit"]/@value');
$this->assertEqual((string) current($elements), 'Save and continue Arabic');
$this->translations['Save and continue'] = 'Save and continue Arabic';
// Verify that language direction is right-to-left.
$direction = (string) current($this->xpath('/html/@dir'));
$this->assertEqual($direction, 'rtl');
}
/**
* Confirms that the installation succeeded.
*/
public function testInstalled() {
$this->assertUrl('user/1');
$this->assertResponse(200);
}
}
@@ -1,45 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Language\LanguageManager;
use Drupal\simpletest\InstallerTestBase;
/**
* Verifies that the installer language list combines local and remote languages.
*
* @group Installer
*/
class InstallerLanguagePageTest extends InstallerTestBase {
/**
* Installer step: Select language.
*/
protected function setUpLanguage() {
// Place a custom local translation in the translations directory.
mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
touch(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.xoxo.po');
// Check that all predefined languages show up with their native names.
$this->visitInstaller();
foreach (LanguageManager::getStandardLanguageList() as $langcode => $names) {
$this->assertOption('edit-langcode', $langcode);
$this->assertRaw('>' . $names[1] . '<');
}
// Check that our custom one shows up with the file name indicated language.
$this->assertOption('edit-langcode', 'xoxo');
$this->assertRaw('>xoxo<');
parent::setUpLanguage();
}
/**
* Confirms that the installation succeeded.
*/
public function testInstalled() {
$this->assertUrl('user/1');
$this->assertResponse(200);
}
}
@@ -1,91 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests the interactive installer.
*
* @group Installer
*/
class InstallerTest extends InstallerTestBase {
/**
* Ensures that the user page is available after installation.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Confirm that we are logged-in after installation.
$this->assertText($this->rootUser->getUsername());
// Verify that the confirmation message appears.
require_once \Drupal::root() . '/core/includes/install.inc';
$this->assertRaw(t('Congratulations, you installed @drupal!', [
'@drupal' => drupal_install_profile_distribution_name(),
]));
// Ensure that the timezone is correct for sites under test after installing
// interactively.
$this->assertEqual($this->config('system.date')->get('timezone.default'), 'Australia/Sydney');
}
/**
* Installer step: Select language.
*/
protected function setUpLanguage() {
// Test that \Drupal\Core\Render\BareHtmlPageRenderer adds assets and
// metatags as expected to the first page of the installer.
$this->assertRaw('core/themes/seven/css/components/buttons.css');
$this->assertRaw('<meta charset="utf-8" />');
// Assert that the expected title is present.
$this->assertEqual('Choose language', $this->cssSelect('main h2')[0]);
parent::setUpLanguage();
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// Assert that the expected title is present.
$this->assertEqual('Select an installation profile', $this->cssSelect('main h2')[0]);
$result = $this->xpath('//span[contains(@class, :class) and contains(text(), :text)]', [':class' => 'visually-hidden', ':text' => 'Select an installation profile']);
$this->assertEqual(count($result), 1, "Title/Label not displayed when '#title_display' => 'invisible' attribute is set");
parent::setUpProfile();
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// Assert that the expected title is present.
$this->assertEqual('Database configuration', $this->cssSelect('main h2')[0]);
parent::setUpSettings();
}
/**
* {@inheritdoc}
*/
protected function setUpSite() {
// Assert that the expected title is present.
$this->assertEqual('Configure site', $this->cssSelect('main h2')[0]);
parent::setUpSite();
}
/**
* {@inheritdoc}
*/
protected function visitInstaller() {
parent::visitInstaller();
// Assert the title is correct and has the title suffix.
$this->assertTitle('Choose language | Drupal');
}
}
@@ -1,27 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
/**
* Tests translation files for multiple languages get imported during install.
*
* @group Installer
*/
class InstallerTranslationMultipleLanguageForeignTest extends InstallerTranslationMultipleLanguageTest {
/**
* Overrides the language code in which to install Drupal.
*
* @var string
*/
protected $langcode = 'de';
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
parent::setUpLanguage();
$this->translations['Save and continue'] = 'Save and continue de';
}
}
@@ -1,19 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
/**
* Tests that keeping English in a foreign language install works.
*
* @group Installer
*/
class InstallerTranslationMultipleLanguageKeepEnglishTest extends InstallerTranslationMultipleLanguageForeignTest {
/**
* Switch to the multilingual testing profile with English kept.
*
* @var string
*/
protected $profile = 'testing_multilingual_with_english';
}
@@ -1,178 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests translation files for multiple languages get imported during install.
*
* @group Installer
*/
class InstallerTranslationMultipleLanguageTest extends InstallerTestBase {
/**
* Switch to the multilingual testing profile.
*
* @var string
*/
protected $profile = 'testing_multilingual';
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// Place custom local translations in the translations directory.
mkdir(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de'));
file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.es.po', $this->getPo('es'));
parent::setUpLanguage();
}
/**
* Returns the string for the test .po file.
*
* @param string $langcode
* The language code.
* @return string
* Contents for the test .po file.
*/
protected function getPo($langcode) {
return <<<ENDPO
msgid ""
msgstr ""
msgid "Save and continue"
msgstr "Save and continue $langcode"
msgid "Anonymous"
msgstr "Anonymous $langcode"
msgid "Language"
msgstr "Language $langcode"
#: Testing site name configuration during the installer.
msgid "Drupal"
msgstr "Drupal"
ENDPO;
}
/**
* {@inheritdoc}
*/
protected function installParameters() {
$params = parent::installParameters();
$params['forms']['install_configure_form']['site_name'] = 'SITE_NAME_' . $this->langcode;
return $params;
}
/**
* Tests that translations ended up at the expected places.
*/
public function testTranslationsLoaded() {
// Ensure the title is correct.
$this->assertEqual('SITE_NAME_' . $this->langcode, \Drupal::config('system.site')->get('name'));
// Verify German and Spanish were configured.
$this->drupalGet('admin/config/regional/language');
$this->assertText('German');
$this->assertText('Spanish');
// If the installer was English or we used a profile that keeps English, we
// expect that configured also. Otherwise English should not be configured
// on the site.
if ($this->langcode == 'en' || $this->profile == 'testing_multilingual_with_english') {
$this->assertText('English');
}
else {
$this->assertNoText('English');
}
// Verify the strings from the translation files were imported.
$this->verifyImportedStringsTranslated();
/** @var \Drupal\language\ConfigurableLanguageManager $language_manager */
$language_manager = \Drupal::languageManager();
// If the site was installed in a foreign language (only tested with German
// in subclasses), then the active configuration should be updated and no
// override should exist in German. Otherwise the German translation should
// end up in overrides the same way as Spanish (which is not used as a site
// installation language). English should be available based on profile
// information and should be possible to add if not yet added, making
// English overrides available.
$config = \Drupal::config('user.settings');
$override_de = $language_manager->getLanguageConfigOverride('de', 'user.settings');
$override_en = $language_manager->getLanguageConfigOverride('en', 'user.settings');
$override_es = $language_manager->getLanguageConfigOverride('es', 'user.settings');
if ($this->langcode == 'de') {
// Active configuration should be in German and no German override should
// exist.
$this->assertEqual($config->get('anonymous'), 'Anonymous de');
$this->assertEqual($config->get('langcode'), 'de');
$this->assertTrue($override_de->isNew());
if ($this->profile == 'testing_multilingual_with_english') {
// English is already added in this profile. Should make the override
// available.
$this->assertEqual($override_en->get('anonymous'), 'Anonymous');
}
else {
// English is not yet available.
$this->assertTrue($override_en->isNew());
// Adding English should make the English override available.
$edit = ['predefined_langcode' => 'en'];
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
$override_en = $language_manager->getLanguageConfigOverride('en', 'user.settings');
$this->assertEqual($override_en->get('anonymous'), 'Anonymous');
}
// Activate a module, to make sure that config is not overridden by module
// installation.
$edit = [
'modules[views][enable]' => TRUE,
'modules[filter][enable]' => TRUE,
];
$this->drupalPostForm('admin/modules', $edit, t('Install'));
// Verify the strings from the translation are still as expected.
$this->verifyImportedStringsTranslated();
}
else {
// Active configuration should be English.
$this->assertEqual($config->get('anonymous'), 'Anonymous');
$this->assertEqual($config->get('langcode'), 'en');
// There should not be an English override.
$this->assertTrue($override_en->isNew());
// German should be an override.
$this->assertEqual($override_de->get('anonymous'), 'Anonymous de');
}
// Spanish is always an override (never used as installation language).
$this->assertEqual($override_es->get('anonymous'), 'Anonymous es');
}
/**
* Helper function to verify that the expected strings are translated.
*/
protected function verifyImportedStringsTranslated() {
$test_samples = ['Save and continue', 'Anonymous', 'Language'];
$langcodes = ['de', 'es'];
foreach ($test_samples as $sample) {
foreach ($langcodes as $langcode) {
$edit = [];
$edit['langcode'] = $langcode;
$edit['translation'] = 'translated';
$edit['string'] = $sample;
$this->drupalPostForm('admin/config/regional/translate', $edit, t('Filter'));
$this->assertText($sample . ' ' . $langcode);
}
}
}
}
@@ -1,85 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\InstallerTestBase;
/**
* Installs Drupal in German and checks resulting site.
*
* @group Installer
*
* @see \Drupal\system\Tests\Installer\InstallerTranslationTest
*/
class InstallerTranslationQueryTest extends InstallerTestBase {
/**
* Overrides the language code in which to install Drupal.
*
* @var string
*/
protected $langcode = 'de';
/**
* {@inheritdoc}
*/
protected function visitInstaller() {
// Place a custom local translation in the translations directory.
mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de'));
// The unrouted URL assembler does not exist at this point, so we build the
// URL ourselves.
$this->drupalGet($GLOBALS['base_url'] . '/core/install.php' . '?langcode=' . $this->langcode);
// The language should have been automatically detected, all following
// screens should be translated already.
$elements = $this->xpath('//input[@type="submit"]/@value');
$this->assertEqual((string) current($elements), 'Save and continue de');
$this->translations['Save and continue'] = 'Save and continue de';
// Check the language direction.
$direction = (string) current($this->xpath('/html/@dir'));
$this->assertEqual($direction, 'ltr');
}
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// The language was was preset by passing a query parameter in the URL, so
// no explicit language selection is necessary.
}
/**
* Verifies the expected behaviors of the installation result.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Verify German was configured but not English.
$this->drupalGet('admin/config/regional/language');
$this->assertText('German');
$this->assertNoText('English');
}
/**
* Returns the string for the test .po file.
*
* @param string $langcode
* The language code.
* @return string
* Contents for the test .po file.
*/
protected function getPo($langcode) {
return <<<ENDPO
msgid ""
msgstr ""
msgid "Save and continue"
msgstr "Save and continue $langcode"
ENDPO;
}
}
@@ -1,155 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Database\Database;
use Drupal\simpletest\InstallerTestBase;
use Drupal\user\Entity\User;
/**
* Installs Drupal in German and checks resulting site.
*
* @group Installer
*/
class InstallerTranslationTest extends InstallerTestBase {
/**
* Overrides the language code in which to install Drupal.
*
* @var string
*/
protected $langcode = 'de';
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// Place a custom local translation in the translations directory.
mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de'));
parent::setUpLanguage();
// After selecting a different language than English, all following screens
// should be translated already.
$elements = $this->xpath('//input[@type="submit"]/@value');
$this->assertEqual((string) current($elements), 'Save and continue de');
$this->translations['Save and continue'] = 'Save and continue de';
// Check the language direction.
$direction = (string) current($this->xpath('/html/@dir'));
$this->assertEqual($direction, 'ltr');
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// We are creating a table here to force an error in the installer because
// it will try and create the drupal_install_test table as this is part of
// the standard database tests performed by the installer in
// Drupal\Core\Database\Install\Tasks.
Database::getConnection('default')->query('CREATE TABLE {drupal_install_test} (id int NULL)');
parent::setUpSettings();
// Ensure that the error message translation is working.
$this->assertRaw('Beheben Sie alle Probleme unten, um die Installation fortzusetzen. Informationen zur Konfiguration der Datenbankserver finden Sie in der <a href="https://www.drupal.org/getting-started/install">Installationshandbuch</a>, oder kontaktieren Sie Ihren Hosting-Anbieter.');
$this->assertRaw('<strong>CREATE</strong> ein Test-Tabelle auf Ihrem Datenbankserver mit dem Befehl <em class="placeholder">CREATE TABLE {drupal_install_test} (id int NULL)</em> fehlgeschlagen.');
// Now do it successfully.
Database::getConnection('default')->query('DROP TABLE {drupal_install_test}');
parent::setUpSettings();
}
/**
* Verifies the expected behaviors of the installation result.
*/
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Verify German was configured but not English.
$this->drupalGet('admin/config/regional/language');
$this->assertText('German');
$this->assertNoText('English');
// The current container still has the english as current language, rebuild.
$this->rebuildContainer();
/** @var \Drupal\user\Entity\User $account */
$account = User::load(0);
$this->assertEqual($account->language()->getId(), 'en', 'Anonymous user is English.');
$account = User::load(1);
$this->assertEqual($account->language()->getId(), 'en', 'Administrator user is English.');
$account = $this->drupalCreateUser();
$this->assertEqual($account->language()->getId(), 'de', 'New user is German.');
// Ensure that we can enable basic_auth on a non-english site.
$this->drupalPostForm('admin/modules', ['modules[basic_auth][enable]' => TRUE], t('Install'));
$this->assertResponse(200);
// Assert that the theme CSS was added to the page.
$edit = ['preprocess_css' => FALSE];
$this->drupalPostForm('admin/config/development/performance', $edit, t('Save configuration'));
$this->drupalGet('<front>');
$this->assertRaw('classy/css/components/action-links.css');
// Verify the strings from the translation files were imported.
$test_samples = ['Save and continue', 'Anonymous'];
foreach ($test_samples as $sample) {
$edit = [];
$edit['langcode'] = 'de';
$edit['translation'] = 'translated';
$edit['string'] = $sample;
$this->drupalPostForm('admin/config/regional/translate', $edit, t('Filter'));
$this->assertText($sample . ' de');
}
/** @var \Drupal\language\ConfigurableLanguageManager $language_manager */
$language_manager = \Drupal::languageManager();
// Installed in German, configuration should be in German. No German or
// English overrides should be present.
$config = \Drupal::config('user.settings');
$override_de = $language_manager->getLanguageConfigOverride('de', 'user.settings');
$override_en = $language_manager->getLanguageConfigOverride('en', 'user.settings');
$this->assertEqual($config->get('anonymous'), 'Anonymous de');
$this->assertEqual($config->get('langcode'), 'de');
$this->assertTrue($override_de->isNew());
$this->assertTrue($override_en->isNew());
// Assert that adding English makes the English override available.
$edit = ['predefined_langcode' => 'en'];
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
$override_en = $language_manager->getLanguageConfigOverride('en', 'user.settings');
$this->assertFalse($override_en->isNew());
$this->assertEqual($override_en->get('anonymous'), 'Anonymous');
}
/**
* Returns the string for the test .po file.
*
* @param string $langcode
* The language code.
* @return string
* Contents for the test .po file.
*/
protected function getPo($langcode) {
return <<<ENDPO
msgid ""
msgstr ""
msgid "Save and continue"
msgstr "Save and continue $langcode"
msgid "Anonymous"
msgstr "Anonymous $langcode"
msgid "Resolve all issues below to continue the installation. For help configuring your database server, see the <a href="https://www.drupal.org/getting-started/install">installation handbook</a>, or contact your hosting provider."
msgstr "Beheben Sie alle Probleme unten, um die Installation fortzusetzen. Informationen zur Konfiguration der Datenbankserver finden Sie in der <a href="https://www.drupal.org/getting-started/install">Installationshandbuch</a>, oder kontaktieren Sie Ihren Hosting-Anbieter."
msgid "Failed to <strong>CREATE</strong> a test table on your database server with the command %query. The server reports the following message: %error.<p>Are you sure the configured username has the necessary permissions to create tables in the database?</p>"
msgstr "<strong>CREATE</strong> ein Test-Tabelle auf Ihrem Datenbankserver mit dem Befehl %query fehlgeschlagen."
ENDPO;
}
}
@@ -1,28 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\KernelTests\AssertConfigTrait;
/**
* Tests the interactive installer installing the minimal profile.
*
* @group Installer
*/
class MinimalInstallerTest extends ConfigAfterInstallerTestBase {
use AssertConfigTrait;
/**
* {@inheritdoc}
*/
protected $profile = 'minimal';
/**
* Ensures that the exported minimal configuration is up to date.
*/
public function testMinimalConfig() {
$this->assertInstalledConfig([]);
}
}
@@ -1,91 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Site\Settings;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests multiple distribution profile support.
*
* @group Installer
*/
class MultipleDistributionsProfileTest extends InstallerTestBase {
/**
* The distribution profile info.
*
* @var array
*/
protected $info;
/**
* {@inheritdoc}
*/
protected function setUp() {
// Create two distributions.
foreach (['distribution_one', 'distribution_two'] as $name) {
$info = [
'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY,
'name' => $name . ' profile',
'distribution' => [
'name' => $name,
'install' => [
'theme' => 'bartik',
],
],
];
// File API functions are not available yet.
$path = $this->siteDirectory . '/profiles/' . $name;
mkdir($path, 0777, TRUE);
file_put_contents("$path/$name.info.yml", Yaml::encode($info));
}
// Install the first distribution.
$this->profile = 'distribution_one';
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// Verify that the distribution name appears.
$this->assertRaw('distribution_one');
// Verify that the requested theme is used.
$this->assertRaw('bartik');
// Verify that the "Choose profile" step does not appear.
$this->assertNoText('profile');
parent::setUpLanguage();
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// This step is skipped, because there is a distribution profile.
}
/**
* Confirms that the installation succeeded.
*/
public function testInstalled() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Confirm that we are logged-in after installation.
$this->assertText($this->rootUser->getUsername());
// Confirm that Drupal recognizes this distribution as the current profile.
$this->assertEqual(\Drupal::installProfile(), 'distribution_one');
$this->assertEqual(Settings::get('install_profile'), 'distribution_one', 'The install profile has been written to settings.php.');
$this->assertEqual($this->config('core.extension')->get('profile'), 'distribution_one', 'The install profile has been written to core.extension configuration.');
$this->rebuildContainer();
$this->pass('Container can be rebuilt as distribution is written to configuration.');
$this->assertEqual(\Drupal::installProfile(), 'distribution_one');
}
}
@@ -1,60 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Serialization\Yaml;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests distribution profile support.
*
* @group Installer
*/
class SingleVisibleProfileTest extends InstallerTestBase {
/**
* The installation profile to install.
*
* Not needed when only one is visible.
*
* @var string
*/
protected $profile = NULL;
protected function setUp() {
$profiles = ['standard', 'demo_umami'];
foreach ($profiles as $profile) {
$info = [
'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY,
'name' => 'Override ' . $profile,
'hidden' => TRUE,
];
// File API functions are not available yet.
$path = $this->siteDirectory . '/profiles/' . $profile;
mkdir($path, 0777, TRUE);
file_put_contents("$path/$profile.info.yml", Yaml::encode($info));
}
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function setUpProfile() {
// This step is skipped, because there is only one visible profile.
}
/**
* Confirms that the installation succeeded.
*/
public function testInstalled() {
$this->assertUrl('user/1');
$this->assertResponse(200);
// Confirm that we are logged-in after installation.
$this->assertText($this->rootUser->getUsername());
// Confirm that the minimal profile was installed.
$this->assertEqual(drupal_get_profile(), 'minimal');
}
}
@@ -1,39 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
use Drupal\simpletest\WebTestBase;
/**
* Tests that the site name can be set during a non-interactive installation.
*
* @group Installer
*/
class SiteNameTest extends WebTestBase {
/**
* The site name to be used when testing.
*
* @var string
*/
protected $siteName;
/**
* {@inheritdoc}
*/
protected function installParameters() {
$this->siteName = $this->randomMachineName();
$parameters = parent::installParameters();
$parameters['forms']['install_configure_form']['site_name'] = $this->siteName;
return $parameters;
}
/**
* Tests that the desired site name appears on the page after installation.
*/
public function testSiteName() {
$this->drupalGet('');
$this->assertRaw($this->siteName, 'The site name that was set during the installation appears on the front page after installation.');
}
}
@@ -1,68 +0,0 @@
<?php
namespace Drupal\system\Tests\Installer;
/**
* Tests the interactive installer installing the standard profile.
*
* @group Installer
*/
class StandardInstallerTest extends ConfigAfterInstallerTestBase {
/**
* {@inheritdoc}
*/
protected $profile = 'standard';
/**
* Ensures that the user page is available after installation.
*/
public function testInstaller() {
// Verify that the Standard install profile's default frontpage appears.
$this->assertRaw('No front page content has been created yet.');
}
/**
* {@inheritdoc}
*/
protected function setUpSite() {
// Test that the correct theme is being used.
$this->assertNoRaw('bartik');
$this->assertRaw('themes/seven/css/theme/install-page.css');
parent::setUpSite();
}
/**
* {@inheritdoc}
*/
protected function curlExec($curl_options, $redirect = FALSE) {
// Ensure that we see the classy progress CSS on the batch page.
// Batch processing happens as part of HTTP redirects, so we can access the
// HTML of the batch page.
if (strpos($curl_options[CURLOPT_URL], '&id=1&op=do_nojs') !== FALSE) {
$this->assertRaw('themes/classy/css/components/progress.css');
}
return parent::curlExec($curl_options, $redirect);
}
/**
* Ensures that the exported standard configuration is up to date.
*/
public function testStandardConfig() {
$skipped_config = [];
// \Drupal\simpletest\WebTestBase::installParameters() uses
// simpletest@example.com as mail address.
$skipped_config['contact.form.feedback'][] = '- simpletest@example.com';
// \Drupal\filter\Entity\FilterFormat::toArray() drops the roles of filter
// formats.
$skipped_config['filter.format.basic_html'][] = 'roles:';
$skipped_config['filter.format.basic_html'][] = '- authenticated';
$skipped_config['filter.format.full_html'][] = 'roles:';
$skipped_config['filter.format.full_html'][] = '- administrator';
$skipped_config['filter.format.restricted_html'][] = 'roles:';
$skipped_config['filter.format.restricted_html'][] = '- anonymous';
$this->assertInstalledConfig($skipped_config);
}
}
@@ -59,8 +59,9 @@ abstract class ModuleTestBase extends WebTestBase {
public function assertModuleTablesExist($module) {
$tables = array_keys(drupal_get_module_schema($module));
$tables_exist = TRUE;
$schema = Database::getConnection()->schema();
foreach ($tables as $table) {
if (!db_table_exists($table)) {
if (!$schema->tableExists($table)) {
$tables_exist = FALSE;
}
}
@@ -76,8 +77,9 @@ abstract class ModuleTestBase extends WebTestBase {
public function assertModuleTablesDoNotExist($module) {
$tables = array_keys(drupal_get_module_schema($module));
$tables_exist = FALSE;
$schema = Database::getConnection()->schema();
foreach ($tables as $table) {
if (db_table_exists($table)) {
if ($schema->tableExists($table)) {
$tables_exist = TRUE;
}
}
@@ -51,27 +51,26 @@ class UrlAliasFixtures {
[
'source' => '/node/1',
'alias' => '/alias_for_node_1_en',
'langcode' => 'en'
'langcode' => 'en',
],
[
'source' => '/node/2',
'alias' => '/alias_for_node_2_en',
'langcode' => 'en'
'langcode' => 'en',
],
[
'source' => '/node/1',
'alias' => '/alias_for_node_1_fr',
'langcode' => 'fr'
'langcode' => 'fr',
],
[
'source' => '/node/1',
'alias' => '/alias_for_node_1_und',
'langcode' => 'und'
]
'langcode' => 'und',
],
];
}
/**
* Returns the table definition for the URL alias fixtures.
*
@@ -57,7 +57,7 @@ class SessionTest extends WebTestBase {
// session_test_user_login() which breaks a normal assertion.
$edit = [
'name' => $user->getUsername(),
'pass' => $user->pass_raw
'pass' => $user->pass_raw,
];
$this->drupalPostForm('user/login', $edit, t('Log in'));
$this->drupalGet('user');
@@ -1,129 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\WebTestBase;
use Drupal\user\RoleInterface;
/**
* Tests page access denied functionality, including custom 403 pages.
*
* @group system
*/
class AccessDeniedTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'node', 'system_test'];
protected $adminUser;
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('page_title_block');
// Create an administrative user.
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'administer site configuration', 'link to any page', 'administer blocks']);
$this->adminUser->roles[] = 'administrator';
$this->adminUser->save();
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access user profiles']);
user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access user profiles']);
}
public function testAccessDenied() {
$this->drupalGet('admin');
$this->assertText(t('Access denied'), 'Found the default 403 page');
$this->assertResponse(403);
// Ensure that users without permission are denied access and have the
// correct path information in drupalSettings.
$this->drupalLogin($this->createUser([]));
$this->drupalGet('admin', ['query' => ['foo' => 'bar']]);
$this->assertEqual($this->drupalSettings['path']['currentPath'], 'admin');
$this->assertEqual($this->drupalSettings['path']['currentPathIsAdmin'], TRUE);
$this->assertEqual($this->drupalSettings['path']['currentQuery'], ['foo' => 'bar']);
$this->drupalLogin($this->adminUser);
// Set a custom 404 page without a starting slash.
$edit = [
'site_403' => 'user/' . $this->adminUser->id(),
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
$this->assertRaw(SafeMarkup::format("The path '%path' has to start with a slash.", ['%path' => $edit['site_403']]));
// Use a custom 403 page.
$edit = [
'site_403' => '/user/' . $this->adminUser->id(),
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
// Enable the user login block.
$block = $this->drupalPlaceBlock('user_login_block', ['id' => 'login']);
// Log out and check that the user login block is shown on custom 403 pages.
$this->drupalLogout();
$this->drupalGet('admin');
$this->assertText($this->adminUser->getUsername(), 'Found the custom 403 page');
$this->assertText(t('Username'), 'Blocks are shown on the custom 403 page');
// Log back in and remove the custom 403 page.
$this->drupalLogin($this->adminUser);
$edit = [
'site_403' => '',
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
// Logout and check that the user login block is shown on default 403 pages.
$this->drupalLogout();
$this->drupalGet('admin');
$this->assertText(t('Access denied'), 'Found the default 403 page');
$this->assertResponse(403);
$this->assertText(t('Username'), 'Blocks are shown on the default 403 page');
// Log back in, set the custom 403 page to /user/login and remove the block
$this->drupalLogin($this->adminUser);
$this->config('system.site')->set('page.403', '/user/login')->save();
$block->disable()->save();
// Check that we can log in from the 403 page.
$this->drupalLogout();
$edit = [
'name' => $this->adminUser->getUsername(),
'pass' => $this->adminUser->pass_raw,
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Log in'));
// Check that we're still on the same page.
$this->assertText(t('Basic site settings'));
}
/**
* Tests that an inaccessible custom 403 page falls back to the default.
*/
public function testAccessDeniedCustomPageWithAccessDenied() {
// Sets up a 403 page not accessible by the anonymous user.
$this->config('system.site')->set('page.403', '/system-test/custom-4xx')->save();
$this->drupalGet('/system-test/always-denied');
$this->assertNoText('Admin-only 4xx response');
$this->assertText('You are not authorized to access this page.');
$this->assertResponse(403);
// Verify the access cacheability metadata for custom 403 is bubbled.
$this->assertCacheContext('user.roles');
$this->drupalLogin($this->adminUser);
$this->drupalGet('/system-test/always-denied');
$this->assertText('Admin-only 4xx response');
$this->assertResponse(403);
// Verify the access cacheability metadata for custom 403 is bubbled.
$this->assertCacheContext('user.roles');
}
}
@@ -1,171 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\simpletest\WebTestBase;
/**
* Tests output on administrative pages and compact mode functionality.
*
* @group system
*/
class AdminTest extends WebTestBase {
/**
* User account with all available permissions
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $adminUser;
/**
* User account with limited access to administration pages.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $webUser;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['locale'];
protected function setUp() {
// testAdminPages() requires Locale module.
parent::setUp();
// Create an administrator with all permissions, as well as a regular user
// who can only access administration pages and perform some Locale module
// administrative tasks, but not all of them.
$this->adminUser = $this->drupalCreateUser(array_keys(\Drupal::service('user.permissions')->getPermissions()));
$this->webUser = $this->drupalCreateUser([
'access administration pages',
'translate interface',
]);
$this->drupalLogin($this->adminUser);
}
/**
* Tests output on administrative listing pages.
*/
public function testAdminPages() {
// Go to Administration.
$this->drupalGet('admin');
// Verify that all visible, top-level administration links are listed on
// the main administration page.
foreach ($this->getTopLevelMenuLinks() as $item) {
$this->assertLink($item->getTitle());
$this->assertLinkByHref($item->getUrlObject()->toString());
// The description should appear below the link.
$this->assertText($item->getDescription());
}
// For each administrative listing page on which the Locale module appears,
// verify that there are links to the module's primary configuration pages,
// but no links to its individual sub-configuration pages. Also verify that
// a user with access to only some Locale module administration pages only
// sees links to the pages they have access to.
$admin_list_pages = [
'admin/index',
'admin/config',
'admin/config/regional',
];
foreach ($admin_list_pages as $page) {
// For the administrator, verify that there are links to Locale's primary
// configuration pages, but no links to individual sub-configuration
// pages.
$this->drupalLogin($this->adminUser);
$this->drupalGet($page);
$this->assertLinkByHref('admin/config');
$this->assertLinkByHref('admin/config/regional/settings');
$this->assertLinkByHref('admin/config/regional/date-time');
$this->assertLinkByHref('admin/config/regional/language');
$this->assertNoLinkByHref('admin/config/regional/language/detection/session');
$this->assertNoLinkByHref('admin/config/regional/language/detection/url');
$this->assertLinkByHref('admin/config/regional/translate');
// On admin/index only, the administrator should also see a "Configure
// permissions" link for the Locale module.
if ($page == 'admin/index') {
$this->assertLinkByHref("admin/people/permissions#module-locale");
}
// For a less privileged user, verify that there are no links to Locale's
// primary configuration pages, but a link to the translate page exists.
$this->drupalLogin($this->webUser);
$this->drupalGet($page);
$this->assertLinkByHref('admin/config');
$this->assertNoLinkByHref('admin/config/regional/settings');
$this->assertNoLinkByHref('admin/config/regional/date-time');
$this->assertNoLinkByHref('admin/config/regional/language');
$this->assertNoLinkByHref('admin/config/regional/language/detection/session');
$this->assertNoLinkByHref('admin/config/regional/language/detection/url');
$this->assertLinkByHref('admin/config/regional/translate');
// This user cannot configure permissions, so even on admin/index should
// not see a "Configure permissions" link for the Locale module.
if ($page == 'admin/index') {
$this->assertNoLinkByHref("admin/people/permissions#module-locale");
}
}
}
/**
* Returns all top level menu links.
*
* @return \Drupal\Core\Menu\MenuLinkInterface[]
*/
protected function getTopLevelMenuLinks() {
$menu_tree = \Drupal::menuTree();
// The system.admin link is normally the parent of all top-level admin links.
$parameters = new MenuTreeParameters();
$parameters->setRoot('system.admin')->excludeRoot()->setTopLevelOnly()->onlyEnabledLinks();
$tree = $menu_tree->load(NULL, $parameters);
$manipulators = [
['callable' => 'menu.default_tree_manipulators:checkAccess'],
['callable' => 'menu.default_tree_manipulators:flatten'],
];
$tree = $menu_tree->transform($tree, $manipulators);
// Transform the tree to a list of menu links.
$menu_links = [];
foreach ($tree as $element) {
$menu_links[] = $element->link;
}
return $menu_links;
}
/**
* Test compact mode.
*/
public function testCompactMode() {
// The front page defaults to 'user/login', which redirects to 'user/{user}'
// for authenticated users. We cannot use '<front>', since this does not
// match the redirected url.
$frontpage_url = 'user/' . $this->adminUser->id();
$this->drupalGet('admin/compact/on');
$this->assertResponse(200, 'A valid page is returned after turning on compact mode.');
$this->assertUrl($frontpage_url, [], 'The user is redirected to the front page after turning on compact mode.');
$this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode turns on.');
$this->drupalGet('admin/compact/on');
$this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode remains on after a repeat call.');
$this->drupalGet('');
$this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode persists on new requests.');
$this->drupalGet('admin/compact/off');
$this->assertResponse(200, 'A valid page is returned after turning off compact mode.');
$this->assertUrl($frontpage_url, [], 'The user is redirected to the front page after turning off compact mode.');
$this->assertEqual($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'deleted', 'Compact mode turns off.');
$this->drupalGet('admin/compact/off');
$this->assertEqual($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'deleted', 'Compact mode remains off after a repeat call.');
$this->drupalGet('');
$this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode persists on new requests.');
}
}
@@ -1,139 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\simpletest\WebTestBase;
/**
* Tests cron runs.
*
* @group system
*/
class CronRunTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['common_test', 'common_test_cron_helper', 'automated_cron'];
/**
* Test cron runs.
*/
public function testCronRun() {
// Run cron anonymously without any cron key.
$this->drupalGet('cron');
$this->assertResponse(404);
// Run cron anonymously with a random cron key.
$key = $this->randomMachineName(16);
$this->drupalGet('cron/' . $key);
$this->assertResponse(403);
// Run cron anonymously with the valid cron key.
$key = \Drupal::state()->get('system.cron_key');
$this->drupalGet('cron/' . $key);
$this->assertResponse(204);
}
/**
* Ensure that the automated cron run module is working.
*
* In these tests we do not use REQUEST_TIME to track start time, because we
* need the exact time when cron is triggered.
*/
public function testAutomatedCron() {
// Test with a logged in user; anonymous users likely don't cause Drupal to
// fully bootstrap, because of the internal page cache or an external
// reverse proxy. Reuse this user for disabling cron later in the test.
$admin_user = $this->drupalCreateUser(['administer site configuration']);
$this->drupalLogin($admin_user);
// Ensure cron does not run when a non-zero cron interval is specified and
// was not passed.
$cron_last = time();
$cron_safe_interval = 100;
\Drupal::state()->set('system.cron_last', $cron_last);
$this->config('automated_cron.settings')
->set('interval', $cron_safe_interval)
->save();
$this->drupalGet('');
$this->assertTrue($cron_last == \Drupal::state()->get('system.cron_last'), 'Cron does not run when the cron interval is not passed.');
// Test if cron runs when the cron interval was passed.
$cron_last = time() - 200;
\Drupal::state()->set('system.cron_last', $cron_last);
$this->drupalGet('');
sleep(1);
$this->assertTrue($cron_last < \Drupal::state()->get('system.cron_last'), 'Cron runs when the cron interval is passed.');
// Disable cron through the interface by setting the interval to zero.
$this->drupalPostForm('admin/config/system/cron', ['interval' => 0], t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
$this->drupalLogout();
// Test if cron does not run when the cron interval is set to zero.
$cron_last = time() - 200;
\Drupal::state()->set('system.cron_last', $cron_last);
$this->drupalGet('');
$this->assertTrue($cron_last == \Drupal::state()->get('system.cron_last'), 'Cron does not run when the cron threshold is disabled.');
}
/**
* Make sure exceptions thrown on hook_cron() don't affect other modules.
*/
public function testCronExceptions() {
\Drupal::state()->delete('common_test.cron');
// The common_test module throws an exception. If it isn't caught, the tests
// won't finish successfully.
// The common_test_cron_helper module sets the 'common_test_cron' variable.
$this->cronRun();
$result = \Drupal::state()->get('common_test.cron');
$this->assertEqual($result, 'success', 'Cron correctly handles exceptions thrown during hook_cron() invocations.');
}
/**
* Make sure the cron UI reads from the state storage.
*/
public function testCronUI() {
$admin_user = $this->drupalCreateUser(['administer site configuration']);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/config/system/cron');
// Don't use REQUEST to calculate the exact time, because that will
// fail randomly. Look for the word 'years', because without a timestamp,
// the time will start at 1 January 1970.
$this->assertNoText('years');
$cron_last = time() - 200;
\Drupal::state()->set('system.cron_last', $cron_last);
$this->drupalPostForm(NULL, [], 'Save configuration');
$this->assertText('The configuration options have been saved.');
$this->assertUrl('admin/config/system/cron');
// Check that cron does not run when saving the configuration form.
$this->assertEqual($cron_last, \Drupal::state()->get('system.cron_last'), 'Cron does not run when saving the configuration form.');
// Check that cron runs when triggered manually.
$this->drupalPostForm(NULL, [], 'Run cron');
$this->assertTrue($cron_last < \Drupal::state()->get('system.cron_last'), 'Cron runs when triggered manually.');
}
/**
* Ensure that the manual cron run is working.
*/
public function testManualCron() {
$admin_user = $this->drupalCreateUser(['administer site configuration']);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/reports/status/run-cron');
$this->assertResponse(403);
$this->drupalGet('admin/reports/status');
$this->clickLink(t('Run cron'));
$this->assertResponse(200);
$this->assertText(t('Cron ran successfully.'));
}
}
@@ -1,49 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\WebTestBase;
/**
* Confirm that the default mobile meta tags appear as expected.
*
* @group system
*/
class DefaultMobileMetaTagsTest extends WebTestBase {
/**
* Array of default meta tags to insert into the page.
*
* @var array
*/
protected $defaultMetaTags;
protected function setUp() {
parent::setUp();
$this->defaultMetaTags = [
'viewport' => '<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
];
}
/**
* Verifies that the default mobile meta tags are added.
*/
public function testDefaultMetaTagsExist() {
$this->drupalGet('');
foreach ($this->defaultMetaTags as $name => $metatag) {
$this->assertRaw($metatag, SafeMarkup::format('Default Mobile meta tag "@name" displayed properly.', ['@name' => $name]), 'System');
}
}
/**
* Verifies that the default mobile meta tags can be removed.
*/
public function testRemovingDefaultMetaTags() {
\Drupal::service('module_installer')->install(['system_module_test']);
$this->drupalGet('');
foreach ($this->defaultMetaTags as $name => $metatag) {
$this->assertNoRaw($metatag, SafeMarkup::format('Default Mobile meta tag "@name" removed properly.', ['@name' => $name]), 'System');
}
}
}
@@ -1,113 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Core\Flood\DatabaseBackend;
use Drupal\Core\Flood\MemoryBackend;
use Drupal\simpletest\WebTestBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Functional tests for the flood control mechanism.
*
* @group system
*/
class FloodTest extends WebTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Flood backends need a request object. Create a dummy one and insert it
// to the container.
$request = Request::createFromGlobals();
$this->container->get('request_stack')->push($request);
}
/**
* Test flood control mechanism clean-up.
*/
public function testCleanUp() {
$threshold = 1;
$window_expired = -1;
$name = 'flood_test_cleanup';
$flood = \Drupal::flood();
$this->assertTrue($flood->isAllowed($name, $threshold));
// Register expired event.
$flood->register($name, $window_expired);
// Verify event is not allowed.
$this->assertFalse($flood->isAllowed($name, $threshold));
// Run cron and verify event is now allowed.
$this->cronRun();
$this->assertTrue($flood->isAllowed($name, $threshold));
// Register unexpired event.
$flood->register($name);
// Verify event is not allowed.
$this->assertFalse($flood->isAllowed($name, $threshold));
// Run cron and verify event is still not allowed.
$this->cronRun();
$this->assertFalse($flood->isAllowed($name, $threshold));
}
/**
* Test flood control memory backend.
*/
public function testMemoryBackend() {
$threshold = 1;
$window_expired = -1;
$name = 'flood_test_cleanup';
$request_stack = \Drupal::service('request_stack');
$flood = new MemoryBackend($request_stack);
$this->assertTrue($flood->isAllowed($name, $threshold));
// Register expired event.
$flood->register($name, $window_expired);
// Verify event is not allowed.
$this->assertFalse($flood->isAllowed($name, $threshold));
// Run cron and verify event is now allowed.
$flood->garbageCollection();
$this->assertTrue($flood->isAllowed($name, $threshold));
// Register unexpired event.
$flood->register($name);
// Verify event is not allowed.
$this->assertFalse($flood->isAllowed($name, $threshold));
// Run cron and verify event is still not allowed.
$flood->garbageCollection();
$this->assertFalse($flood->isAllowed($name, $threshold));
}
/**
* Test flood control database backend.
*/
public function testDatabaseBackend() {
$threshold = 1;
$window_expired = -1;
$name = 'flood_test_cleanup';
$connection = \Drupal::service('database');
$request_stack = \Drupal::service('request_stack');
$flood = new DatabaseBackend($connection, $request_stack);
$this->assertTrue($flood->isAllowed($name, $threshold));
// Register expired event.
$flood->register($name, $window_expired);
// Verify event is not allowed.
$this->assertFalse($flood->isAllowed($name, $threshold));
// Run cron and verify event is now allowed.
$flood->garbageCollection();
$this->assertTrue($flood->isAllowed($name, $threshold));
// Register unexpired event.
$flood->register($name);
// Verify event is not allowed.
$this->assertFalse($flood->isAllowed($name, $threshold));
// Run cron and verify event is still not allowed.
$flood->garbageCollection();
$this->assertFalse($flood->isAllowed($name, $threshold));
}
}
@@ -1,88 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\WebTestBase;
/**
* Tests front page functionality and administration.
*
* @group system
*/
class FrontPageTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'system_test', 'views'];
/**
* The path to a node that is created for testing.
*
* @var string
*/
protected $nodePath;
protected function setUp() {
parent::setUp();
// Create admin user, log in admin user, and create one node.
$this->drupalLogin($this->drupalCreateUser([
'access content',
'administer site configuration',
]));
$this->drupalCreateContentType(['type' => 'page']);
$this->nodePath = "node/" . $this->drupalCreateNode(['promote' => 1])->id();
// Configure 'node' as front page.
$this->config('system.site')->set('page.front', '/node')->save();
// Enable front page logging in system_test.module.
\Drupal::state()->set('system_test.front_page_output', 1);
}
/**
* Test front page functionality.
*/
public function testDrupalFrontPage() {
// Create a promoted node to test the <title> tag on the front page view.
$settings = [
'title' => $this->randomMachineName(8),
'promote' => 1,
];
$this->drupalCreateNode($settings);
$this->drupalGet('');
$this->assertTitle('Home | Drupal');
$this->assertText(t('On front page.'), 'Path is the front page.');
$this->drupalGet('node');
$this->assertText(t('On front page.'), 'Path is the front page.');
$this->drupalGet($this->nodePath);
$this->assertNoText(t('On front page.'), 'Path is not the front page.');
// Change the front page to an invalid path.
$edit = ['site_frontpage' => '/kittens'];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
$this->assertText(t("Either the path '@path' is invalid or you do not have access to it.", ['@path' => $edit['site_frontpage']]));
// Change the front page to a path without a starting slash.
$edit = ['site_frontpage' => $this->nodePath];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
$this->assertRaw(SafeMarkup::format("The path '%path' has to start with a slash.", ['%path' => $edit['site_frontpage']]));
// Change the front page to a valid path.
$edit['site_frontpage'] = '/' . $this->nodePath;
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'), 'The front page path has been saved.');
$this->drupalGet('');
$this->assertText(t('On front page.'), 'Path is the front page.');
$this->drupalGet('node');
$this->assertNoText(t('On front page.'), 'Path is not the front page.');
$this->drupalGet($this->nodePath);
$this->assertText(t('On front page.'), 'Path is the front page.');
}
}
@@ -1,154 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\simpletest\WebTestBase;
/**
* Tests .htaccess is working correctly.
*
* @group system
*/
class HtaccessTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'path'];
/**
* Get an array of file paths for access testing.
*
* @return int[]
* An array keyed by file paths. Each value is the expected response code,
* for example, 200 or 403.
*/
protected function getProtectedFiles() {
$path = drupal_get_path('module', 'system') . '/tests/fixtures/HtaccessTest';
// Tests the FilesMatch directive which denies access to certain file
// extensions.
$file_exts_to_deny = [
'engine',
'inc',
'install',
'make',
'module',
'module~',
'module.bak',
'module.orig',
'module.save',
'module.swo',
'module.swp',
'php~',
'php.bak',
'php.orig',
'php.save',
'php.swo',
'php.swp',
'profile',
'po',
'sh',
'sql',
'theme',
'twig',
'tpl.php',
'xtmpl',
'yml',
];
foreach ($file_exts_to_deny as $file_ext) {
$file_paths["$path/access_test.$file_ext"] = 403;
}
// Tests the .htaccess file in vendor and created by a Composer script.
// Try and access a non PHP file in the vendor directory.
// @see Drupal\\Core\\Composer\\Composer::ensureHtaccess
$file_paths['vendor/composer/installed.json'] = 403;
// Tests the rewrite conditions and rule that denies access to php files.
$file_paths['core/lib/Drupal.php'] = 403;
$file_paths['vendor/autoload.php'] = 403;
$file_paths['autoload.php'] = 403;
// Test extensions that should be permitted.
$file_exts_to_allow = [
'php-info.txt'
];
foreach ($file_exts_to_allow as $file_ext) {
$file_paths["$path/access_test.$file_ext"] = 200;
}
// Ensure composer.json and composer.lock cannot be accessed.
$file_paths["$path/composer.json"] = 403;
$file_paths["$path/composer.lock"] = 403;
return $file_paths;
}
/**
* Iterates over protected files and calls assertNoFileAccess().
*/
public function testFileAccess() {
foreach ($this->getProtectedFiles() as $file => $response_code) {
$this->assertFileAccess($file, $response_code);
}
// Test that adding "/1" to a .php URL does not make it accessible.
$this->drupalGet('core/lib/Drupal.php/1');
$this->assertResponse(403, "Access to core/lib/Drupal.php/1 is denied.");
// Test that it is possible to have path aliases containing .php.
$type = $this->drupalCreateContentType();
// Create an node aliased to test.php.
$node = $this->drupalCreateNode([
'title' => 'This is a node',
'type' => $type->id(),
'path' => '/test.php'
]);
$node->save();
$this->drupalGet('test.php');
$this->assertResponse(200);
$this->assertText('This is a node');
// Update node's alias to test.php/test.
$node->path = '/test.php/test';
$node->save();
$this->drupalGet('test.php/test');
$this->assertResponse(200);
$this->assertText('This is a node');
}
/**
* Asserts that a file exists and requesting it returns a specific response.
*
* @param string $path
* Path to file. Without leading slash.
* @param int $response_code
* The expected response code. For example: 200, 403 or 404.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertFileAccess($path, $response_code) {
$result = $this->assertTrue(file_exists(\Drupal::root() . '/' . $path), "The file $path exists.");
$this->drupalGet($path);
$result = $result && $this->assertResponse($response_code, "Response code to $path is $response_code.");
return $result;
}
/**
* Tests that SVGZ files are served with Content-Encoding: gzip.
*/
public function testSvgzContentEncoding() {
$this->drupalGet('core/modules/system/tests/logo.svgz');
$this->assertResponse(200);
$header = $this->drupalGetHeader('Content-Encoding');
$this->assertEqual($header, 'gzip');
}
}
@@ -1,82 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\WebTestBase;
use Drupal\user\RoleInterface;
/**
* Tests page not found functionality, including custom 404 pages.
*
* @group system
*/
class PageNotFoundTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system_test'];
protected $adminUser;
protected function setUp() {
parent::setUp();
// Create an administrative user.
$this->adminUser = $this->drupalCreateUser(['administer site configuration', 'link to any page']);
$this->adminUser->roles[] = 'administrator';
$this->adminUser->save();
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access user profiles']);
user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access user profiles']);
}
public function testPageNotFound() {
$this->drupalLogin($this->adminUser);
$this->drupalGet($this->randomMachineName(10));
$this->assertText(t('Page not found'), 'Found the default 404 page');
// Set a custom 404 page without a starting slash.
$edit = [
'site_404' => 'user/' . $this->adminUser->id(),
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
$this->assertRaw(SafeMarkup::format("The path '%path' has to start with a slash.", ['%path' => $edit['site_404']]));
// Use a custom 404 page.
$edit = [
'site_404' => '/user/' . $this->adminUser->id(),
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
$this->drupalGet($this->randomMachineName(10));
$this->assertText($this->adminUser->getUsername(), 'Found the custom 404 page');
}
/**
* Tests that an inaccessible custom 404 page falls back to the default.
*/
public function testPageNotFoundCustomPageWithAccessDenied() {
// Sets up a 404 page not accessible by the anonymous user.
$this->config('system.site')->set('page.404', '/system-test/custom-4xx')->save();
$this->drupalGet('/this-path-does-not-exist');
$this->assertNoText('Admin-only 4xx response');
$this->assertText('The requested page could not be found.');
$this->assertResponse(404);
// Verify the access cacheability metadata for custom 404 is bubbled.
$this->assertCacheContext('user.roles');
$this->drupalLogin($this->adminUser);
$this->drupalGet('/this-path-does-not-exist');
$this->assertText('Admin-only 4xx response');
$this->assertNoText('The requested page could not be found.');
$this->assertResponse(404);
// Verify the access cacheability metadata for custom 404 is bubbled.
$this->assertCacheContext('user.roles');
}
}
@@ -1,145 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\simpletest\WebTestBase;
/**
* Tests HTML output escaping of page title, site name, and slogan.
*
* @group system
*/
class PageTitleTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'test_page_test', 'form_test', 'block'];
protected $contentUser;
protected $savedTitle;
/**
* Implement setUp().
*/
protected function setUp() {
parent::setUp();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
$this->drupalPlaceBlock('page_title_block');
$this->contentUser = $this->drupalCreateUser(['create page content', 'access content', 'administer themes', 'administer site configuration', 'link to any page']);
$this->drupalLogin($this->contentUser);
}
/**
* Tests the handling of HTML in node titles.
*/
public function testTitleTags() {
$title = "string with <em>HTML</em>";
// Generate node content.
$edit = [
'title[0][value]' => '!SimpleTest! ' . $title . $this->randomMachineName(20),
'body[0][value]' => '!SimpleTest! test body' . $this->randomMachineName(200),
];
// Create the node with HTML in the title.
$this->drupalPostForm('node/add/page', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertNotNull($node, 'Node created and found in database');
$this->assertText(Html::escape($edit['title[0][value]']), 'Check to make sure tags in the node title are converted.');
$this->drupalGet("node/" . $node->id());
$this->assertText(Html::escape($edit['title[0][value]']), 'Check to make sure tags in the node title are converted.');
}
/**
* Test if the title of the site is XSS proof.
*/
public function testTitleXSS() {
// Set some title with JavaScript and HTML chars to escape.
$title = '</title><script type="text/javascript">alert("Title XSS!");</script> & < > " \' ';
$title_filtered = Html::escape($title);
$slogan = '<script type="text/javascript">alert("Slogan XSS!");</script>';
$slogan_filtered = Xss::filterAdmin($slogan);
// Set title and slogan.
$edit = [
'site_name' => $title,
'site_slogan' => $slogan,
];
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
// Place branding block with site name and slogan into header region.
$this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
// Load frontpage.
$this->drupalGet('');
// Test the title.
$this->assertNoRaw($title, 'Check for the lack of the unfiltered version of the title.');
// Add </title> to make sure we're checking the title tag, rather than the
// first 'heading' on the page.
$this->assertRaw($title_filtered . '</title>', 'Check for the filtered version of the title in a <title> tag.');
// Test the slogan.
$this->assertNoRaw($slogan, 'Check for the unfiltered version of the slogan.');
$this->assertRaw($slogan_filtered, 'Check for the filtered version of the slogan.');
}
/**
* Tests the page title of render arrays.
*
* @see \Drupal\test_page_test\Controller\Test
*/
public function testRoutingTitle() {
// Test the '#title' render array attribute.
$this->drupalGet('test-render-title');
$this->assertTitle('Foo | Drupal');
$result = $this->xpath('//h1[@class="page-title"]');
$this->assertEqual('Foo', (string) $result[0]);
// Test forms
$this->drupalGet('form-test/object-builder');
$this->assertTitle('Test dynamic title | Drupal');
$result = $this->xpath('//h1[@class="page-title"]');
$this->assertEqual('Test dynamic title', (string) $result[0]);
// Set some custom translated strings.
$this->addCustomTranslations('en', [
'' => ['Static title' => 'Static title translated'],
]);
$this->writeCustomTranslations();
// Ensure that the title got translated.
$this->drupalGet('test-page-static-title');
$this->assertTitle('Static title translated | Drupal');
$result = $this->xpath('//h1[@class="page-title"]');
$this->assertEqual('Static title translated', (string) $result[0]);
// Test the dynamic '_title_callback' route option.
$this->drupalGet('test-page-dynamic-title');
$this->assertTitle('Dynamic title | Drupal');
$result = $this->xpath('//h1[@class="page-title"]');
$this->assertEqual('Dynamic title', (string) $result[0]);
// Ensure that titles are cacheable and are escaped normally if the
// controller does not escape them.
$this->drupalGet('test-page-cached-controller');
$this->assertTitle('Cached title | Drupal');
$this->assertRaw(Html::escape('<span>Cached title</span>') . '</h1>');
$this->drupalGet('test-page-cached-controller');
$this->assertTitle('Cached title | Drupal');
$this->assertRaw(Html::escape('<span>Cached title</span>') . '</h1>');
}
}
@@ -1,56 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\simpletest\WebTestBase;
/**
* Functional tests shutdown functions.
*
* @group system
*/
class ShutdownFunctionsTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system_test'];
protected function tearDown() {
// This test intentionally throws an exception in a PHP shutdown function.
// Prevent it from being interpreted as an actual test failure.
// Not using File API; a potential error must trigger a PHP warning.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
parent::tearDown();
}
/**
* Test shutdown functions.
*/
public function testShutdownFunctions() {
$arg1 = $this->randomMachineName();
$arg2 = $this->randomMachineName();
$this->drupalGet('system-test/shutdown-functions/' . $arg1 . '/' . $arg2);
// If using PHP-FPM then fastcgi_finish_request() will have been fired
// returning the response before shutdown functions have fired.
// @see \Drupal\system_test\Controller\SystemTestController::shutdownFunctions()
$server_using_fastcgi = strpos($this->getRawContent(), 'The function fastcgi_finish_request exists when serving the request.');
if ($server_using_fastcgi) {
// We need to wait to ensure that the shutdown functions have fired.
sleep(1);
}
$this->assertEqual(\Drupal::state()->get('_system_test_first_shutdown_function'), [$arg1, $arg2]);
$this->assertEqual(\Drupal::state()->get('_system_test_second_shutdown_function'), [$arg1, $arg2]);
if (!$server_using_fastcgi) {
// Make sure exceptions displayed through
// \Drupal\Core\Utility\Error::renderExceptionSafe() are correctly
// escaped.
$this->assertRaw('Drupal is &lt;blink&gt;awesome&lt;/blink&gt;.');
}
}
}
@@ -1,159 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Tests access to site while in maintenance mode.
*
* @group system
*/
class SiteMaintenanceTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node'];
protected $adminUser;
protected function setUp() {
parent::setUp();
// Configure 'node' as front page.
$this->config('system.site')->set('page.front', '/node')->save();
$this->config('system.performance')->set('js.preprocess', 1)->save();
// Create a user allowed to access site in maintenance mode.
$this->user = $this->drupalCreateUser(['access site in maintenance mode']);
// Create an administrative user.
$this->adminUser = $this->drupalCreateUser(['administer site configuration', 'access site in maintenance mode']);
$this->drupalLogin($this->adminUser);
}
/**
* Verifies site maintenance mode functionality.
*/
public function testSiteMaintenance() {
// Verify that permission message is displayed.
$permission_handler = $this->container->get('user.permissions');
$permissions = $permission_handler->getPermissions();
$permission_label = $permissions['access site in maintenance mode']['title'];
$permission_message = t('Visitors will only see the maintenance mode message. Only users with the "@permission-label" <a href=":permissions-url">permission</a> will be able to access the site. Authorized users can log in directly via the <a href=":user-login">user login</a> page.', ['@permission-label' => $permission_label, ':permissions-url' => \Drupal::url('user.admin_permissions'), ':user-login' => \Drupal::url('user.login')]);
$this->drupalGet(Url::fromRoute('system.site_maintenance_mode'));
$this->assertRaw($permission_message, 'Found the permission message.');
$this->drupalGet(Url::fromRoute('user.page'));
// JS should be aggregated, so drupal.js is not in the page source.
$links = $this->xpath('//script[contains(@src, :href)]', [':href' => '/core/misc/drupal.js']);
$this->assertFalse(isset($links[0]), 'script /core/misc/drupal.js not in page');
// Turn on maintenance mode.
$edit = [
'maintenance_mode' => 1,
];
$this->drupalPostForm('admin/config/development/maintenance', $edit, t('Save configuration'));
$admin_message = t('Operating in maintenance mode. <a href=":url">Go online.</a>', [':url' => \Drupal::url('system.site_maintenance_mode')]);
$user_message = t('Operating in maintenance mode.');
$offline_message = t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', ['@site' => $this->config('system.site')->get('name')]);
$this->drupalGet(Url::fromRoute('user.page'));
// JS should not be aggregated, so drupal.js is expected in the page source.
$links = $this->xpath('//script[contains(@src, :href)]', [':href' => '/core/misc/drupal.js']);
$this->assertTrue(isset($links[0]), 'script /core/misc/drupal.js in page');
$this->assertRaw($admin_message, 'Found the site maintenance mode message.');
// Logout and verify that offline message is displayed.
$this->drupalLogout();
$this->drupalGet('');
$this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
$this->assertText($offline_message);
$this->drupalGet('node');
$this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
$this->assertText($offline_message);
$this->drupalGet('user/register');
$this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
$this->assertText($offline_message);
// Verify that user is able to log in.
$this->drupalGet('user');
$this->assertNoText($offline_message);
$this->drupalGet('user/login');
$this->assertNoText($offline_message);
// Log in user and verify that maintenance mode message is displayed
// directly after login.
$edit = [
'name' => $this->user->getUsername(),
'pass' => $this->user->pass_raw,
];
$this->drupalPostForm(NULL, $edit, t('Log in'));
$this->assertText($user_message);
// Log in administrative user and configure a custom site offline message.
$this->drupalLogout();
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/config/development/maintenance');
$this->assertNoRaw($admin_message, 'Site maintenance mode message not displayed.');
$offline_message = 'Sorry, not online.';
$edit = [
'maintenance_mode_message' => $offline_message,
];
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
// Logout and verify that custom site offline message is displayed.
$this->drupalLogout();
$this->drupalGet('');
$this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
$this->assertRaw($offline_message, 'Found the site offline message.');
// Verify that custom site offline message is not displayed on user/password.
$this->drupalGet('user/password');
$this->assertText(t('Username or email address'), 'Anonymous users can access user/password');
// Submit password reset form.
$edit = [
'name' => $this->user->getUsername(),
];
$this->drupalPostForm('user/password', $edit, t('Submit'));
$mails = $this->drupalGetMails();
$start = strpos($mails[0]['body'], 'user/reset/' . $this->user->id());
$path = substr($mails[0]['body'], $start, 66 + strlen($this->user->id()));
// Log in with temporary login link.
$this->drupalPostForm($path, [], t('Log in'));
$this->assertText($user_message);
// Regression test to check if title displays in Bartik on maintenance page.
\Drupal::service('theme_handler')->install(['bartik']);
$this->config('system.theme')->set('default', 'bartik')->save();
// Logout and verify that offline message is displayed in Bartik.
$this->drupalLogout();
$this->drupalGet('');
$this->assertEqual('Site under maintenance', $this->cssSelect('main h1')[0]);
}
/**
* Tests responses to non-HTML requests when in maintenance mode.
*/
public function testNonHtmlRequest() {
$this->drupalLogout();
\Drupal::state()->set('system.maintenance_mode', TRUE);
$formats = ['json', 'xml', 'non-existing'];
foreach ($formats as $format) {
$this->pass('Testing format ' . $format);
$this->drupalGet('<front>', ['query' => ['_format' => $format]]);
$this->assertResponse(503);
$this->assertRaw('Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.');
$this->assertHeader('Content-Type', 'text/plain; charset=UTF-8');
}
}
}
@@ -2,6 +2,8 @@
namespace Drupal\system\Tests\System;
@trigger_error('\Drupal\system\Tests\System\SystemConfigFormTestBase is deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Use \Drupal\KernelTests\ConfigFormTestBase instead.', E_USER_DEPRECATED);
use Drupal\Core\Form\FormState;
use Drupal\simpletest\WebTestBase;
@@ -10,12 +12,17 @@ use Drupal\simpletest\WebTestBase;
*
* @see UserAdminSettingsFormTest
* For a full working implementation.
*
* @deprecated in Drupal 8.6.x and will be removed before Drupal 9.0.0. Use
* \Drupal\KernelTests\ConfigFormTestBase instead.
*
* @see https://www.drupal.org/node/2941907
*/
abstract class SystemConfigFormTestBase extends WebTestBase {
/**
* Form ID to use for testing.
*
* @var \Drupal\Core\Form\FormInterface.
* @var \Drupal\Core\Form\FormInterface
*/
protected $form;
@@ -1,431 +0,0 @@
<?php
namespace Drupal\system\Tests\System;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\simpletest\WebTestBase;
/**
* Tests the theme interface functionality by enabling and switching themes, and
* using an administration theme.
*
* @group system
*/
class ThemeTest extends WebTestBase {
/**
* A user with administrative permissions.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'block', 'file'];
protected function setUp() {
parent::setUp();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'view the administration theme', 'administer themes', 'bypass node access', 'administer blocks']);
$this->drupalLogin($this->adminUser);
$this->node = $this->drupalCreateNode();
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Test the theme settings form.
*/
public function testThemeSettings() {
// Ensure invalid theme settings form URLs return a proper 404.
$this->drupalGet('admin/appearance/settings/bartik');
$this->assertResponse(404, 'The theme settings form URL for a uninstalled theme could not be found.');
$this->drupalGet('admin/appearance/settings/' . $this->randomMachineName());
$this->assertResponse(404, 'The theme settings form URL for a non-existent theme could not be found.');
$this->assertTrue(\Drupal::service('theme_installer')->install(['stable']));
$this->drupalGet('admin/appearance/settings/stable');
$this->assertResponse(404, 'The theme settings form URL for a hidden theme is unavailable.');
// Specify a filesystem path to be used for the logo.
$file = current($this->drupalGetTestFiles('image'));
$file_relative = strtr($file->uri, ['public:/' => PublicStream::basePath()]);
$default_theme_path = 'core/themes/classy';
$supported_paths = [
// Raw stream wrapper URI.
$file->uri => [
'form' => file_uri_target($file->uri),
'src' => file_url_transform_relative(file_create_url($file->uri)),
],
// Relative path within the public filesystem.
file_uri_target($file->uri) => [
'form' => file_uri_target($file->uri),
'src' => file_url_transform_relative(file_create_url($file->uri)),
],
// Relative path to a public file.
$file_relative => [
'form' => $file_relative,
'src' => file_url_transform_relative(file_create_url($file->uri)),
],
// Relative path to an arbitrary file.
'core/misc/druplicon.png' => [
'form' => 'core/misc/druplicon.png',
'src' => base_path() . 'core/misc/druplicon.png',
],
// Relative path to a file in a theme.
$default_theme_path . '/logo.svg' => [
'form' => $default_theme_path . '/logo.svg',
'src' => base_path() . $default_theme_path . '/logo.svg',
],
];
foreach ($supported_paths as $input => $expected) {
$edit = [
'default_logo' => FALSE,
'logo_path' => $input,
];
$this->drupalPostForm('admin/appearance/settings', $edit, t('Save configuration'));
$this->assertNoText('The custom logo path is invalid.');
$this->assertFieldByName('logo_path', $expected['form']);
// Verify logo path examples.
$elements = $this->xpath('//div[contains(@class, :item)]/div[@class=:description]/code', [
':item' => 'js-form-item-logo-path',
':description' => 'description',
]);
// Expected default values (if all else fails).
$implicit_public_file = 'logo.svg';
$explicit_file = 'public://logo.svg';
$local_file = $default_theme_path . '/logo.svg';
// Adjust for fully qualified stream wrapper URI in public filesystem.
if (file_uri_scheme($input) == 'public') {
$implicit_public_file = file_uri_target($input);
$explicit_file = $input;
$local_file = strtr($input, ['public:/' => PublicStream::basePath()]);
}
// Adjust for fully qualified stream wrapper URI elsewhere.
elseif (file_uri_scheme($input) !== FALSE) {
$explicit_file = $input;
}
// Adjust for relative path within public filesystem.
elseif ($input == file_uri_target($file->uri)) {
$implicit_public_file = $input;
$explicit_file = 'public://' . $input;
$local_file = PublicStream::basePath() . '/' . $input;
}
$this->assertEqual((string) $elements[0], $implicit_public_file);
$this->assertEqual((string) $elements[1], $explicit_file);
$this->assertEqual((string) $elements[2], $local_file);
// Verify the actual 'src' attribute of the logo being output in a site
// branding block.
$this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
$this->drupalGet('');
$elements = $this->xpath('//header//a[@rel=:rel]/img', [
':rel' => 'home',
]
);
$this->assertEqual((string) $elements[0]['src'], $expected['src']);
}
$unsupported_paths = [
// Stream wrapper URI to non-existing file.
'public://whatever.png',
'private://whatever.png',
'temporary://whatever.png',
// Bogus stream wrapper URIs.
'public:/whatever.png',
'://whatever.png',
':whatever.png',
'public://',
// Relative path within the public filesystem to non-existing file.
'whatever.png',
// Relative path to non-existing file in public filesystem.
PublicStream::basePath() . '/whatever.png',
// Semi-absolute path to non-existing file in public filesystem.
'/' . PublicStream::basePath() . '/whatever.png',
// Relative path to arbitrary non-existing file.
'core/misc/whatever.png',
// Semi-absolute path to arbitrary non-existing file.
'/core/misc/whatever.png',
// Absolute paths to any local file (even if it exists).
\Drupal::service('file_system')->realpath($file->uri),
];
$this->drupalGet('admin/appearance/settings');
foreach ($unsupported_paths as $path) {
$edit = [
'default_logo' => FALSE,
'logo_path' => $path,
];
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
$this->assertText('The custom logo path is invalid.');
}
// Upload a file to use for the logo.
$edit = [
'default_logo' => FALSE,
'logo_path' => '',
'files[logo_upload]' => \Drupal::service('file_system')->realpath($file->uri),
];
$this->drupalPostForm('admin/appearance/settings', $edit, t('Save configuration'));
$fields = $this->xpath($this->constructFieldXpath('name', 'logo_path'));
$uploaded_filename = 'public://' . $fields[0]['value'];
$this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
$this->drupalGet('');
$elements = $this->xpath('//header//a[@rel=:rel]/img', [
':rel' => 'home',
]
);
$this->assertEqual($elements[0]['src'], file_url_transform_relative(file_create_url($uploaded_filename)));
$this->container->get('theme_handler')->install(['bartik']);
// Ensure only valid themes are listed in the local tasks.
$this->drupalPlaceBlock('local_tasks_block', ['region' => 'header']);
$this->drupalGet('admin/appearance/settings');
$theme_handler = \Drupal::service('theme_handler');
$this->assertLink($theme_handler->getName('classy'));
$this->assertLink($theme_handler->getName('bartik'));
$this->assertNoLink($theme_handler->getName('stable'));
// If a hidden theme is an admin theme it should be viewable.
\Drupal::configFactory()->getEditable('system.theme')->set('admin', 'stable')->save();
\Drupal::service('router.builder')->rebuildIfNeeded();
$this->drupalPlaceBlock('local_tasks_block', ['region' => 'header', 'theme' => 'stable']);
$this->drupalGet('admin/appearance/settings');
$this->assertLink($theme_handler->getName('stable'));
$this->drupalGet('admin/appearance/settings/stable');
$this->assertResponse(200, 'The theme settings form URL for a hidden theme that is the admin theme is available.');
// Ensure default logo and favicons are not triggering custom path
// validation errors if their custom paths are set on the form.
$edit = [
'default_logo' => TRUE,
'logo_path' => 'public://whatever.png',
'default_favicon' => TRUE,
'favicon_path' => 'public://whatever.ico',
];
$this->drupalPostForm('admin/appearance/settings', $edit, 'Save configuration');
$this->assertNoText('The custom logo path is invalid.');
$this->assertNoText('The custom favicon path is invalid.');
}
/**
* Test the theme settings logo form.
*/
public function testThemeSettingsLogo() {
// Visit Bartik's theme settings page to replace the logo.
$this->container->get('theme_handler')->install(['bartik']);
$this->drupalGet('admin/appearance/settings/bartik');
$edit = [
'default_logo' => FALSE,
'logo_path' => 'core/misc/druplicon.png',
];
$this->drupalPostForm('admin/appearance/settings/bartik', $edit, t('Save configuration'));
$this->assertFieldByName('default_logo', FALSE);
$this->assertFieldByName('logo_path', 'core/misc/druplicon.png');
// Make sure the logo and favicon settings are not available when the file
// module is not enabled.
\Drupal::service('module_installer')->uninstall(['file']);
$this->drupalGet('admin/appearance/settings');
$this->assertNoText('Logo image settings');
$this->assertNoText('Shortcut icon settings');
}
/**
* Test the administration theme functionality.
*/
public function testAdministrationTheme() {
$this->container->get('theme_handler')->install(['seven']);
// Install an administration theme and show it on the node admin pages.
$edit = [
'admin_theme' => 'seven',
'use_admin_theme' => TRUE,
];
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
$this->drupalGet('admin/config');
$this->assertRaw('core/themes/seven', 'Administration theme used on an administration page.');
$this->drupalGet('node/' . $this->node->id());
$this->assertRaw('core/themes/classy', 'Site default theme used on node page.');
$this->drupalGet('node/add');
$this->assertRaw('core/themes/seven', 'Administration theme used on the add content page.');
$this->drupalGet('node/' . $this->node->id() . '/edit');
$this->assertRaw('core/themes/seven', 'Administration theme used on the edit content page.');
// Disable the admin theme on the node admin pages.
$edit = [
'use_admin_theme' => FALSE,
];
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
$this->drupalGet('admin/config');
$this->assertRaw('core/themes/seven', 'Administration theme used on an administration page.');
// Ensure that the admin theme is also visible on the 403 page.
$normal_user = $this->drupalCreateUser(['view the administration theme']);
$this->drupalLogin($normal_user);
$this->drupalGet('admin/config');
$this->assertResponse(403);
$this->assertRaw('core/themes/seven', 'Administration theme used on an administration page.');
$this->drupalLogin($this->adminUser);
$this->drupalGet('node/add');
$this->assertRaw('core/themes/classy', 'Site default theme used on the add content page.');
// Reset to the default theme settings.
$edit = [
'admin_theme' => '0',
'use_admin_theme' => FALSE,
];
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
$this->drupalGet('admin');
$this->assertRaw('core/themes/classy', 'Site default theme used on administration page.');
$this->drupalGet('node/add');
$this->assertRaw('core/themes/classy', 'Site default theme used on the add content page.');
}
/**
* Test switching the default theme.
*/
public function testSwitchDefaultTheme() {
/** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
$theme_handler = \Drupal::service('theme_handler');
// First, install Stark and set it as the default theme programmatically.
$theme_handler->install(['stark']);
$this->config('system.theme')->set('default', 'stark')->save();
// Install Bartik and set it as the default theme.
$theme_handler->install(['bartik']);
$this->drupalGet('admin/appearance');
$this->clickLink(t('Set as default'));
$this->assertEqual($this->config('system.theme')->get('default'), 'bartik');
// Test the default theme on the secondary links (blocks admin page).
$this->drupalGet('admin/structure/block');
$this->assertText('Bartik(' . t('active tab') . ')', 'Default local task on blocks admin page is the default theme.');
// Switch back to Stark and test again to test that the menu cache is cleared.
$this->drupalGet('admin/appearance');
// Stark is the first 'Set as default' link.
$this->clickLink(t('Set as default'));
$this->drupalGet('admin/structure/block');
$this->assertText('Stark(' . t('active tab') . ')', 'Default local task on blocks admin page has changed.');
}
/**
* Test themes can't be installed when the base theme or engine is missing.
*
* Include test for themes that have a missing base theme somewhere further up
* the chain than the immediate base theme.
*/
public function testInvalidTheme() {
// theme_page_test_system_info_alter() un-hides all hidden themes.
$this->container->get('module_installer')->install(['theme_page_test']);
// Clear the system_list() and theme listing cache to pick up the change.
$this->container->get('theme_handler')->reset();
$this->drupalGet('admin/appearance');
$this->assertText(t('This theme requires the base theme @base_theme to operate correctly.', ['@base_theme' => 'not_real_test_basetheme']));
$this->assertText(t('This theme requires the base theme @base_theme to operate correctly.', ['@base_theme' => 'test_invalid_basetheme']));
$this->assertText(t('This theme requires the theme engine @theme_engine to operate correctly.', ['@theme_engine' => 'not_real_engine']));
// Check for the error text of a theme with the wrong core version.
$this->assertText("This theme is not compatible with Drupal 8.x. Check that the .info.yml file contains the correct 'core' value.");
// Check for the error text of a theme without a content region.
$this->assertText("This theme is missing a 'content' region.");
}
/**
* Test uninstalling of themes works.
*/
public function testUninstallingThemes() {
// Install Bartik and set it as the default theme.
\Drupal::service('theme_handler')->install(['bartik']);
// Set up seven as the admin theme.
\Drupal::service('theme_handler')->install(['seven']);
$edit = [
'admin_theme' => 'seven',
'use_admin_theme' => TRUE,
];
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
$this->drupalGet('admin/appearance');
$this->clickLink(t('Set as default'));
// Check that seven cannot be uninstalled as it is the admin theme.
$this->assertNoRaw('Uninstall Seven theme', 'A link to uninstall the Seven theme does not appear on the theme settings page.');
// Check that bartik cannot be uninstalled as it is the default theme.
$this->assertNoRaw('Uninstall Bartik theme', 'A link to uninstall the Bartik theme does not appear on the theme settings page.');
// Check that the classy theme cannot be uninstalled as it is a base theme
// of seven and bartik.
$this->assertNoRaw('Uninstall Classy theme', 'A link to uninstall the Classy theme does not appear on the theme settings page.');
// Install Stark and set it as the default theme.
\Drupal::service('theme_handler')->install(['stark']);
$edit = [
'admin_theme' => 'stark',
'use_admin_theme' => TRUE,
];
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
// Check that seven can be uninstalled now.
$this->assertRaw('Uninstall Seven theme', 'A link to uninstall the Seven theme does appear on the theme settings page.');
// Check that the classy theme still cannot be uninstalled as it is a
// base theme of bartik.
$this->assertNoRaw('Uninstall Classy theme', 'A link to uninstall the Classy theme does not appear on the theme settings page.');
// Change the default theme to stark, stark is second in the list.
$this->clickLink(t('Set as default'), 1);
// Check that bartik can be uninstalled now.
$this->assertRaw('Uninstall Bartik theme', 'A link to uninstall the Bartik theme does appear on the theme settings page.');
// Check that the classy theme still can't be uninstalled as neither of its
// base themes have been.
$this->assertNoRaw('Uninstall Classy theme', 'A link to uninstall the Classy theme does not appear on the theme settings page.');
// Uninstall each of the three themes starting with Bartik.
$this->clickLink(t('Uninstall'));
$this->assertRaw('The <em class="placeholder">Bartik</em> theme has been uninstalled');
// Seven is the second in the list.
$this->clickLink(t('Uninstall'));
$this->assertRaw('The <em class="placeholder">Seven</em> theme has been uninstalled');
// Check that the classy theme still can't be uninstalled as it is hidden.
$this->assertNoRaw('Uninstall Classy theme', 'A link to uninstall the Classy theme does not appear on the theme settings page.');
}
/**
* Tests installing a theme and setting it as default.
*/
public function testInstallAndSetAsDefault() {
$this->drupalGet('admin/appearance');
// Bartik is uninstalled in the test profile and has the third "Install and
// set as default" link.
$this->clickLink(t('Install and set as default'), 2);
// Test the confirmation message.
$this->assertText('Bartik is now the default theme.');
// Make sure Bartik is now set as the default theme in config.
$this->assertEqual($this->config('system.theme')->get('default'), 'bartik');
// This checks for a regression. See https://www.drupal.org/node/2498691.
$this->assertNoText('The bartik theme was not found.');
$themes = \Drupal::service('theme_handler')->rebuildThemeData();
$version = $themes['bartik']->info['version'];
// Confirm Bartik is indicated as the default theme.
$this->assertTextPattern('/Bartik ' . preg_quote($version) . '\s{2,}\(default theme\)/');
}
}

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