Move all files to 2017/

This commit is contained in:
Oliver Davies 2025-09-29 22:25:17 +01:00
parent ac7370f67f
commit 2875863330
15717 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,28 @@
uuid: 1a6c0f14-78dc-4ede-bade-b8ce83881453
langcode: en
status: true
dependencies:
module:
- block_test
- system
theme:
- bartik
id: missing_schema
theme: bartik
region: sidebar_first
weight: 0
provider: null
plugin: system_branding_block
settings:
id: system_branding_block
label: 'Test missing schema on conditions'
provider: system
label_display: visible
use_site_logo: true
use_site_name: true
use_site_slogan: true
visibility:
missing_schema:
id: missing_schema
negate: 0
context_mapping: { }

View file

@ -0,0 +1,49 @@
<?php
/**
* @file
* Partial database to mimic the installation of the block_test module.
*/
use Drupal\Core\Database\Database;
use Symfony\Component\Yaml\Yaml;
$connection = Database::getConnection();
// Set the schema version.
$connection->insert('key_value')
->fields([
'collection' => 'system.schema',
'name' => 'block_test',
'value' => 'i:8000;',
])
->execute();
// Update core.extension.
$extensions = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'core.extension')
->execute()
->fetchField();
$extensions = unserialize($extensions);
$extensions['module']['block_test'] = 8000;
$connection->update('config')
->fields([
'data' => serialize($extensions),
])
->condition('collection', '')
->condition('name', 'core.extension')
->execute();
// Install the block configuration.
$config = file_get_contents(__DIR__ . '/block.block.missing_schema.yml');
$config = Yaml::parse($config);
$connection->insert('config')
->fields(['data', 'name', 'collection'])
->values([
'name' => 'block.block.missing_schema',
'data' => serialize($config),
'collection' => '',
])
->execute();

View file

@ -0,0 +1,8 @@
name: 'Block test'
type: module
description: 'Provides test blocks.'
package: Testing
version: VERSION
core: 8.x
dependencies:
- drupal:block

View file

@ -0,0 +1,62 @@
<?php
/**
* @file
* Provide test blocks.
*/
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Cache\Cache;
/**
* Implements hook_block_alter().
*/
function block_test_block_alter(&$block_info) {
if (\Drupal::state()->get('block_test_info_alter') && isset($block_info['test_block_instantiation'])) {
$block_info['test_block_instantiation']['category'] = t('Custom category');
}
}
/**
* Implements hook_block_view_BASE_BLOCK_ID_alter().
*/
function block_test_block_view_test_cache_alter(array &$build, BlockPluginInterface $block) {
if (\Drupal::state()->get('block_test_view_alter_suffix') !== NULL) {
$build['#attributes']['foo'] = 'bar';
}
if (\Drupal::state()->get('block_test_view_alter_append_pre_render_prefix') !== NULL) {
$build['#pre_render'][] = 'block_test_pre_render_alter_content';
}
}
/**
* Implements hook_block_build_BASE_BLOCK_ID_alter().
*/
function block_test_block_build_test_cache_alter(array &$build, BlockPluginInterface $block) {
// Test altering cache keys, contexts, tags and max-age.
if (\Drupal::state()->get('block_test_block_alter_cache_key') !== NULL) {
$build['#cache']['keys'][] = \Drupal::state()->get('block_test_block_alter_cache_key');
}
if (\Drupal::state()->get('block_test_block_alter_cache_context') !== NULL) {
$build['#cache']['contexts'][] = \Drupal::state()->get('block_test_block_alter_cache_context');
}
if (\Drupal::state()->get('block_test_block_alter_cache_tag') !== NULL) {
$build['#cache']['tags'] = Cache::mergeTags($build['#cache']['tags'], [\Drupal::state()->get('block_test_block_alter_cache_tag')]);
}
if (\Drupal::state()->get('block_test_block_alter_cache_max_age') !== NULL) {
$build['#cache']['max-age'] = \Drupal::state()->get('block_test_block_alter_cache_max_age');
}
// Test setting #create_placeholder.
if (\Drupal::state()->get('block_test_block_alter_create_placeholder') !== NULL) {
$build['#create_placeholder'] = \Drupal::state()->get('block_test_block_alter_create_placeholder');
}
}
/**
* #pre_render callback for a block to alter its content.
*/
function block_test_pre_render_alter_content($build) {
$build['#prefix'] = 'Hiya!<br>';
return $build;
}

View file

@ -0,0 +1,7 @@
block_test.test_multipleforms:
path: '/test-multiple-forms'
defaults:
_controller: '\Drupal\block_test\Controller\TestMultipleFormController::testMultipleForms'
_title: 'Multiple forms'
requirements:
_access: 'TRUE'

View file

@ -0,0 +1,6 @@
services:
block_test.multiple_static_context:
class: Drupal\block_test\ContextProvider\MultipleStaticContext
arguments: ['@current_user', '@entity.manager']
tags:
- { name: 'context_provider' }

View file

@ -0,0 +1,17 @@
id: test_block
theme: stark
weight: 0
status: true
langcode: en
region: '-1'
plugin: test_html
settings:
label: 'Test HTML block'
provider: block_test
label_display: 'hidden'
dependencies:
module:
- block_test
theme:
- classy
visibility: { }

View file

@ -0,0 +1,10 @@
block.settings.test_block_instantiation:
type: block_settings
label: 'Test block instantiation settings'
mapping:
display_message:
type: string
label: 'Message text'
condition.plugin.baloney_spam:
type: condition.plugin

View file

@ -0,0 +1,71 @@
<?php
namespace Drupal\block_test\ContextProvider;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\Plugin\Context\EntityContext;
use Drupal\Core\Session\AccountInterface;
/**
* Sets multiple contexts for a static value.
*/
class MultipleStaticContext implements ContextProviderInterface {
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* Constructs a new MultipleStaticContext.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct(AccountInterface $account, EntityManagerInterface $entity_manager) {
$this->account = $account;
$this->userStorage = $entity_manager->getStorage('user');
}
/**
* {@inheritdoc}
*/
public function getRuntimeContexts(array $unqualified_context_ids) {
$current_user = $this->userStorage->load($this->account->id());
$context1 = EntityContext::fromEntity($current_user, 'User A');
$context2 = EntityContext::fromEntity($current_user, 'User B');
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['user']);
$context1->addCacheableDependency($cacheability);
$context2->addCacheableDependency($cacheability);
return [
'userA' => $context1,
'userB' => $context2,
];
}
/**
* {@inheritdoc}
*/
public function getAvailableContexts() {
return $this->getRuntimeContexts([]);
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\block_test\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Form\FormState;
/**
* Controller for block_test module
*/
class TestMultipleFormController extends ControllerBase {
public function testMultipleForms() {
$form_state = new FormState();
$build = [
'form1' => $this->formBuilder()->buildForm('\Drupal\block_test\Form\TestForm', $form_state),
'form2' => $this->formBuilder()->buildForm('\Drupal\block_test\Form\FavoriteAnimalTestForm', $form_state),
];
// Output all attached placeholders trough
// \Drupal\Core\Messenger\MessengerInterface::addMessage(), so we can
// see if there's only one in the tests.
$post_render_callable = function ($elements) {
$matches = [];
preg_match_all('<form\s(.*?)action="(.*?)"(.*)>', $elements, $matches);
$action_values = $matches[2];
foreach ($action_values as $action_value) {
$this->messenger()->addStatus('Form action: ' . $action_value);
}
return $elements;
};
$build['#post_render'] = [$post_render_callable];
return $build;
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\block_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Form that performs favorite animal test.
*
* @internal
*/
class FavoriteAnimalTestForm extends FormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'block_test_form_favorite_animal_test';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['favorite_animal'] = [
'#type' => 'textfield',
'#title' => $this->t('Your favorite animal.'),
];
$form['submit_animal'] = [
'#type' => 'submit',
'#value' => $this->t('Submit your chosen animal'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->messenger()->addStatus($this->t('Your favorite animal is: @favorite_animal', ['@favorite_animal' => $form['favorite_animal']['#value']]));
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Drupal\block_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Form that performs base block form test.
*
* @internal
*/
class TestForm extends FormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'block_test_form_test';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['email'] = [
'#type' => 'email',
'#title' => $this->t('Your .com email address.'),
];
$form['show'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (strpos($form_state->getValue('email'), '.com') === FALSE) {
$form_state->setErrorByName('email', $this->t('This is not a .com email address.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->messenger()->addStatus($this->t('Your email address is @email', ['@email' => $form['email']['#value']]));
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a block to test access.
*
* @Block(
* id = "test_access",
* admin_label = @Translation("Test block access")
* )
*/
class TestAccessBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* Tests the test access block.
*
*
* @param array $configuration
* The plugin configuration, i.e. an array with configuration values keyed
* by configuration option name. The special key 'context' may be used to
* initialize the defined contexts by setting it to an array of context
* values keyed by context names.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\State\StateInterface $state
* The state.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, StateInterface $state) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('state')
);
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
return $this->state->get('test_block_access', FALSE) ? AccessResult::allowed()->setCacheMaxAge(0) : AccessResult::forbidden()->setCacheMaxAge(0);
}
/**
* {@inheritdoc}
*/
public function build() {
return ['#markup' => 'Hello test world'];
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return Cache::PERMANENT;
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Provides a basic block for testing block instantiation and configuration.
*
* @Block(
* id = "test_block_instantiation",
* admin_label = @Translation("Display message")
* )
*/
class TestBlockInstantiation extends BlockBase {
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'display_message' => 'no message set',
];
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
return AccessResult::allowedIfHasPermission($account, 'access content');
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$form['display_message'] = [
'#type' => 'textfield',
'#title' => $this->t('Display message'),
'#default_value' => $this->configuration['display_message'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['display_message'] = $form_state->getValue('display_message');
}
/**
* {@inheritdoc}
*/
public function build() {
return [
'#children' => $this->configuration['display_message'],
];
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a block to test caching.
*
* @Block(
* id = "test_cache",
* admin_label = @Translation("Test block caching")
* )
*/
class TestCacheBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
$content = \Drupal::state()->get('block_test.content');
$build = [];
if (!empty($content)) {
$build['#markup'] = $content;
}
return $build;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return \Drupal::state()->get('block_test.cache_contexts', []);
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return \Drupal::state()->get('block_test.cache_max_age', parent::getCacheMaxAge());
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\UserInterface;
/**
* Provides a context-aware block.
*
* @Block(
* id = "test_context_aware",
* admin_label = @Translation("Test context-aware block"),
* context = {
* "user" = @ContextDefinition("entity:user", required = FALSE)
* }
* )
*/
class TestContextAwareBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
/** @var $user \Drupal\user\UserInterface */
$user = $this->getContextValue('user');
return [
'#prefix' => '<div id="' . $this->getPluginId() . '--username">',
'#suffix' => '</div>',
'#markup' => $user ? $user->getUsername() : 'No context mapping selected.' ,
];
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
if ($this->getContextValue('user') instanceof UserInterface) {
$this->messenger()->addStatus('User context found.');
}
return parent::blockAccess($account);
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a context-aware block that uses a not-passed, non-required context.
*
* @Block(
* id = "test_context_aware_no_valid_context_options",
* admin_label = @Translation("Test context-aware block - no valid context options"),
* context_definitions = {
* "email" = @ContextDefinition("email", required = FALSE)
* }
* )
*/
class TestContextAwareNoValidContextOptionsBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
return [
'#markup' => 'Rendered block with no valid context options',
];
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a context-aware block.
*
* @Block(
* id = "test_context_aware_unsatisfied",
* admin_label = @Translation("Test context-aware unsatisfied block"),
* context = {
* "user" = @ContextDefinition("entity:foobar")
* }
* )
*/
class TestContextAwareUnsatisfiedBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
return [
'#markup' => 'test',
];
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a block to test caching.
*
* @Block(
* id = "test_form_in_block",
* admin_label = @Translation("Test form block caching")
* )
*/
class TestFormBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
return \Drupal::formBuilder()->getForm('Drupal\block_test\Form\TestForm');
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a block to test HTML.
*
* @Block(
* id = "test_html",
* admin_label = @Translation("Test HTML block")
* )
*/
class TestHtmlBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
return [
'#attributes' => \Drupal::state()->get('block_test.attributes'),
'#children' => \Drupal::state()->get('block_test.content'),
];
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a block with multiple forms.
*
* @Block(
* id = "test_multiple_forms_block",
* forms = {
* "secondary" = "\Drupal\block_test\PluginForm\EmptyBlockForm"
* },
* admin_label = @Translation("Multiple forms test block")
* )
*/
class TestMultipleFormsBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
return [];
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Drupal\block_test\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a test settings validation block.
*
* @Block(
* id = "test_settings_validation",
* admin_label = @Translation("Test settings validation block"),
* )
*/
class TestSettingsValidationBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
return ['digits' => ['#type' => 'textfield']] + $form;
}
/**
* {@inheritdoc}
*/
public function blockValidate($form, FormStateInterface $form_state) {
if (!ctype_digit($form_state->getValue('digits'))) {
$form_state->setErrorByName('digits', $this->t('Only digits are allowed'));
}
}
/**
* {@inheritdoc}
*/
public function build() {
return ['#markup' => 'foo'];
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace Drupal\block_test\Plugin\Block;
/**
* Provides a block to test XSS in title.
*
* @Block(
* id = "test_xss_title",
* admin_label = "<script>alert('XSS subject');</script>"
* )
*/
class TestXSSTitleBlock extends TestCacheBlock {
}

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\block_test\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
/**
* Provides a 'baloney_spam' condition.
*
* @Condition(
* id = "baloney_spam",
* label = @Translation("Baloney spam"),
* )
*/
class BaloneySpam extends ConditionPluginBase {
/**
* {@inheritdoc}
*/
public function evaluate() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function summary() {
return 'Summary';
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\block_test\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
/**
* Provides a 'missing_schema' condition.
*
* @Condition(
* id = "missing_schema",
* label = @Translation("Missing schema"),
* )
*/
class MissingSchema extends ConditionPluginBase {
/**
* {@inheritdoc}
*/
public function evaluate() {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function summary() {
return 'Summary';
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Drupal\block_test\PluginForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginFormBase;
/**
* Provides a form for a block that is empty.
*/
class EmptyBlockForm extends PluginFormBase {
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
// Intentionally empty.
}
}

View file

@ -0,0 +1,7 @@
name: '<"Cat" & ''Mouse''>'
type: theme
description: 'Theme for testing special characters in block admin.'
core: 8.x
regions:
content: Content
help: Help

View file

@ -0,0 +1,16 @@
name: 'Block test theme'
type: theme
description: 'Theme for testing the block system'
version: VERSION
core: 8.x
regions:
sidebar_first: 'Left sidebar'
sidebar_second: 'Right sidebar'
content: Content
header: Header
footer: Footer
highlighted: Highlighted
help: Help
regions_hidden:
- sidebar_first
- sidebar_second

View file

@ -0,0 +1,9 @@
name: 'Block test views'
type: module
description: 'Provides a view and block to test block displays in views.'
package: Testing
version: VERSION
core: 8.x
dependencies:
- drupal:block
- drupal:views

View file

@ -0,0 +1,45 @@
langcode: en
status: true
dependencies: { }
id: test_view_block
label: test_view_block
module: views
description: ''
tag: ''
base_table: views_test_data
base_field: id
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: null
display_options:
access:
type: none
cache:
type: tag
query:
type: views_query
exposed_form:
type: basic
pager:
type: some
options:
items_per_page: 5
style:
type: default
row:
type: fields
fields:
name:
id: name
table: views_test_data
field: name
title: test_view_block
block_1:
display_plugin: block
id: block_1
display_title: Block
position: null

View file

@ -0,0 +1,57 @@
langcode: en
status: true
dependencies:
module:
- user
id: test_view_block2
label: test_view_block2
module: views
description: ''
tag: ''
base_table: views_test_data
base_field: id
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: null
display_options:
access:
type: perm
cache:
type: tag
query:
type: views_query
exposed_form:
type: basic
pager:
type: some
options:
items_per_page: 5
style:
type: default
row:
type: fields
fields:
name:
id: name
table: views_test_data
field: name
title: test_view_block2
block_1:
display_plugin: block
id: block_1
display_title: Block
position: null
block_2:
display_plugin: block
id: block_2
display_title: Block
position: null
block_3:
display_plugin: block
id: block_3
display_title: Block
position: null

View file

@ -0,0 +1,433 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_view_block_with_context
label: test_view_block_with_context
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: some
options:
items_per_page: 5
offset: 0
style:
type: default
row:
type: fields
fields:
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: true
text: 'Test view row: {{ title }}'
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
plugin_id: field
filters:
status:
value: '1'
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
group: 1
sorts:
created:
id: created
table: node_field_data
field: created
order: DESC
entity_type: node
entity_field: created
plugin_id: date
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
granularity: second
title: test_view_block_with_context
header: { }
footer: { }
empty:
area_text_custom:
id: area_text_custom
table: views
field: area_text_custom
relationship: none
group_type: group
admin_label: ''
empty: true
tokenize: false
content: 'Test view: No results found.'
plugin_id: text_custom
relationships: { }
arguments:
'null':
id: 'null'
table: views
field: 'null'
relationship: none
group_type: group
admin_label: ''
default_action: default
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: foo
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
must_not_be: false
plugin_id: 'null'
null_1:
id: null_1
table: views
field: 'null'
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
must_not_be: false
plugin_id: 'null'
nid:
id: nid
table: node_field_data
field: nid
relationship: none
group_type: group
admin_label: ''
default_action: empty
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: true
validate:
type: 'entity:node'
fail: 'not found'
validate_options:
operation: view
multiple: 0
bundles: { }
access: false
break_phrase: false
not: false
entity_type: node
entity_field: nid
plugin_id: node_nid
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
cacheable: false
max-age: -1
tags: { }
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
cacheable: false
max-age: -1
tags: { }
block_2:
display_plugin: block
id: block_2
display_title: 'Block 2'
position: 2
display_options:
display_extenders: { }
arguments:
created:
id: created
table: node_field_data
field: created
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: true
validate:
type: numeric
fail: 'not found'
validate_options: { }
entity_type: node
entity_field: created
plugin_id: date
vid:
id: vid
table: node_field_data
field: vid
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
not: false
entity_type: node
entity_field: vid
plugin_id: numeric
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
glossary: false
limit: 0
case: none
path_case: none
transform_dash: false
break_phrase: false
entity_type: node
entity_field: title
plugin_id: string
defaults:
arguments: false
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
tags: { }

View file

@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\block\Entity\Block;
use Drupal\Component\Render\FormattableMarkup;
/**
* Provides test assertions for testing block appearance.
*
* Can be used by test classes that extend \Drupal\Tests\BrowserTestBase.
*/
trait AssertBlockAppearsTrait {
/**
* Checks to see whether a block appears on the page.
*
* @param \Drupal\block\Entity\Block $block
* The block entity to find on the page.
*/
protected function assertBlockAppears(Block $block) {
$result = $this->findBlockInstance($block);
$this->assertTrue(!empty($result), new FormattableMarkup('The block @id appears on the page', ['@id' => $block->id()]));
}
/**
* Checks to see whether a block does not appears on the page.
*
* @param \Drupal\block\Entity\Block $block
* The block entity to find on the page.
*/
protected function assertNoBlockAppears(Block $block) {
$result = $this->findBlockInstance($block);
$this->assertFalse(!empty($result), new FormattableMarkup('The block @id does not appear on the page', ['@id' => $block->id()]));
}
/**
* Find a block instance on the page.
*
* @param \Drupal\block\Entity\Block $block
* The block entity to find on the page.
*
* @return array
* The result from the xpath query.
*/
protected function findBlockInstance(Block $block) {
return $this->xpath('//div[@id = :id]', [':id' => 'block-' . $block->id()]);
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the block system with admin themes.
*
* @group block
*/
class BlockAdminThemeTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'contextual'];
/**
* Check for the accessibility of the admin theme on the block admin page.
*/
public function testAdminTheme() {
// Create administrative user.
$admin_user = $this->drupalCreateUser(['administer blocks', 'administer themes']);
$this->drupalLogin($admin_user);
// Ensure that access to block admin page is denied when theme is not
// installed.
$this->drupalGet('admin/structure/block/list/bartik');
$this->assertResponse(403);
// Install admin theme and confirm that tab is accessible.
\Drupal::service('theme_handler')->install(['bartik']);
$edit['admin_theme'] = 'bartik';
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
$this->drupalGet('admin/structure/block/list/bartik');
$this->assertResponse(200);
}
/**
* Ensure contextual links are disabled in Seven theme.
*/
public function testSevenAdminTheme() {
// Create administrative user.
$admin_user = $this->drupalCreateUser([
'access administration pages',
'administer themes',
'access contextual links',
'view the administration theme',
]);
$this->drupalLogin($admin_user);
// Install admin theme and confirm that tab is accessible.
\Drupal::service('theme_handler')->install(['seven']);
$edit['admin_theme'] = 'seven';
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
// Define our block settings.
$settings = [
'theme' => 'seven',
'region' => 'header',
];
// Place a block.
$block = $this->drupalPlaceBlock('local_tasks_block', $settings);
// Open admin page.
$this->drupalGet('admin');
// Check if contextual link classes are unavailable.
$this->assertNoRaw('<div data-contextual-id="block:block=' . $block->id() . ':langcode=en"></div>');
$this->assertNoRaw('contextual-region');
}
}

View file

@ -0,0 +1,215 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\BrowserTestBase;
/**
* Tests block caching.
*
* @group block
*/
class BlockCacheTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_test', 'test_page_test'];
/**
* A user with permission to create and edit books and to administer blocks.
*
* @var object
*/
protected $adminUser;
/**
* An authenticated user to test block caching.
*
* @var object
*/
protected $normalUser;
/**
* Another authenticated user to test block caching.
*
* @var object
*/
protected $normalUserAlt;
/**
* The block used by this test.
*
* @var \Drupal\block\BlockInterface
*/
protected $block;
protected function setUp() {
parent::setUp();
// Create an admin user, log in and enable test blocks.
$this->adminUser = $this->drupalCreateUser(['administer blocks', 'access administration pages']);
$this->drupalLogin($this->adminUser);
// Create additional users to test caching modes.
$this->normalUser = $this->drupalCreateUser();
$this->normalUserAlt = $this->drupalCreateUser();
// Sync the roles, since drupalCreateUser() creates separate roles for
// the same permission sets.
$this->normalUserAlt->roles = $this->normalUser->getRoles();
$this->normalUserAlt->save();
// Enable our test block.
$this->block = $this->drupalPlaceBlock('test_cache');
}
/**
* Test "user.roles" cache context.
*/
public function testCachePerRole() {
\Drupal::state()->set('block_test.cache_contexts', ['user.roles']);
// Enable our test block. Set some content for it to display.
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalLogin($this->normalUser);
$this->drupalGet('');
$this->assertText($current_content, 'Block content displays.');
// Change the content, but the cached copy should still be served.
$old_content = $current_content;
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalGet('');
$this->assertText($old_content, 'Block is served from the cache.');
// Clear the cache and verify that the stale data is no longer there.
Cache::invalidateTags(['block_view']);
$this->drupalGet('');
$this->assertNoText($old_content, 'Block cache clear removes stale cache data.');
$this->assertText($current_content, 'Fresh block content is displayed after clearing the cache.');
// Test whether the cached data is served for the correct users.
$old_content = $current_content;
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalLogout();
$this->drupalGet('');
$this->assertNoText($old_content, 'Anonymous user does not see content cached per-role for normal user.');
$this->drupalLogin($this->normalUserAlt);
$this->drupalGet('');
$this->assertText($old_content, 'User with the same roles sees per-role cached content.');
$this->drupalLogin($this->adminUser);
$this->drupalGet('');
$this->assertNoText($old_content, 'Admin user does not see content cached per-role for normal user.');
$this->drupalLogin($this->normalUser);
$this->drupalGet('');
$this->assertText($old_content, 'Block is served from the per-role cache.');
}
/**
* Test a cacheable block without any additional cache context.
*/
public function testCachePermissions() {
// user.permissions is a required context, so a user with different
// permissions will see a different version of the block.
\Drupal::state()->set('block_test.cache_contexts', []);
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalGet('');
$this->assertText($current_content, 'Block content displays.');
$old_content = $current_content;
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalGet('user');
$this->assertText($old_content, 'Block content served from cache.');
$this->drupalLogout();
$this->drupalGet('user');
$this->assertText($current_content, 'Block content not served from cache.');
}
/**
* Test non-cacheable block.
*/
public function testNoCache() {
\Drupal::state()->set('block_test.cache_max_age', 0);
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
// If max_age = 0 has no effect, the next request would be cached.
$this->drupalGet('');
$this->assertText($current_content, 'Block content displays.');
// A cached copy should not be served.
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalGet('');
$this->assertText($current_content, 'Maximum age of zero prevents blocks from being cached.');
}
/**
* Test "user" cache context.
*/
public function testCachePerUser() {
\Drupal::state()->set('block_test.cache_contexts', ['user']);
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalLogin($this->normalUser);
$this->drupalGet('');
$this->assertText($current_content, 'Block content displays.');
$old_content = $current_content;
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalGet('');
$this->assertText($old_content, 'Block is served from per-user cache.');
$this->drupalLogin($this->normalUserAlt);
$this->drupalGet('');
$this->assertText($current_content, 'Per-user block cache is not served for other users.');
$this->drupalLogin($this->normalUser);
$this->drupalGet('');
$this->assertText($old_content, 'Per-user block cache is persistent.');
}
/**
* Test "url" cache context.
*/
public function testCachePerPage() {
\Drupal::state()->set('block_test.cache_contexts', ['url']);
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalGet('test-page');
$this->assertText($current_content, 'Block content displays on the test page.');
$old_content = $current_content;
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
$this->drupalGet('user');
$this->assertResponse(200);
$this->assertNoText($old_content, 'Block content cached for the test page does not show up for the user page.');
$this->drupalGet('test-page');
$this->assertResponse(200);
$this->assertText($old_content, 'Block content cached for the test page.');
}
}

View file

@ -0,0 +1,63 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the block demo page with admin themes.
*
* @group block
*/
class BlockDemoTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block'];
/**
* Check for the accessibility of the admin block demo page.
*/
public function testBlockDemo() {
// Create administrative user.
$admin_user = $this->drupalCreateUser(['administer blocks', 'administer themes']);
$this->drupalLogin($admin_user);
// Confirm we have access to the block demo page for the default theme.
$config = $this->container->get('config.factory')->get('system.theme');
$default_theme = $config->get('default');
$this->drupalGet('admin/structure/block/demo/' . $default_theme);
$this->assertResponse(200);
$this->assertLinkByHref('admin/structure/block');
$this->assertNoLinkByHref('admin/structure/block/list/' . $default_theme);
// All available themes in core.
$available_themes = [
'bartik',
'classy',
'seven',
'stark',
];
// All available themes minute minus the default theme.
$themes = array_diff($available_themes, [$default_theme]);
foreach ($themes as $theme) {
// Install theme.
$this->container->get('theme_handler')->install([$theme]);
// Confirm access to the block demo page for the theme.
$this->drupalGet('admin/structure/block/demo/' . $theme);
$this->assertResponse(200);
// Confirm existence of link for "Exit block region demonstration".
$this->assertLinkByHref('admin/structure/block/list/' . $theme);
}
// Confirm access to the block demo page is denied for an invalid theme.
$this->drupalGet('admin/structure/block/demo/invalid_theme');
$this->assertResponse(403);
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Component\Utility\Crypt;
use Drupal\Tests\BrowserTestBase;
/**
* Tests form in block caching.
*
* @group block
*/
class BlockFormInBlockTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_test', 'test_page_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Enable our test block.
$this->drupalPlaceBlock('test_form_in_block');
}
/**
* Test to see if form in block's redirect isn't cached.
*/
public function testCachePerPage() {
$form_values = ['email' => 'test@example.com'];
// Go to "test-page" and test if the block is enabled.
$this->drupalGet('test-page');
$this->assertResponse(200);
$this->assertText('Your .com email address.', 'form found');
// Make sure that we're currently still on /test-page after submitting the
// form.
$this->drupalPostForm(NULL, $form_values, t('Submit'));
$this->assertUrl('test-page');
$this->assertText(t('Your email address is @email', ['@email' => 'test@example.com']));
// Go to a different page and see if the block is enabled there as well.
$this->drupalGet('test-render-title');
$this->assertResponse(200);
$this->assertText('Your .com email address.', 'form found');
// Make sure that submitting the form didn't redirect us to the first page
// we submitted the form from after submitting the form from
// /test-render-title.
$this->drupalPostForm(NULL, $form_values, t('Submit'));
$this->assertUrl('test-render-title');
$this->assertText(t('Your email address is @email', ['@email' => 'test@example.com']));
}
/**
* Test the actual placeholders
*/
public function testPlaceholders() {
$this->drupalGet('test-multiple-forms');
$placeholder = 'form_action_' . Crypt::hashBase64('Drupal\Core\Form\FormBuilder::prepareForm');
$this->assertText('Form action: ' . $placeholder, 'placeholder found.');
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests that a newly installed theme does not inherit blocks to its hidden
* regions.
*
* @group block
*/
class BlockHiddenRegionTest extends BrowserTestBase {
/**
* An administrative user to configure the test environment.
*/
protected $adminUser;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_test', 'search'];
protected function setUp() {
parent::setUp();
// Create administrative user.
$this->adminUser = $this->drupalCreateUser([
'administer blocks',
'administer themes',
'search content',
]
);
$this->drupalLogin($this->adminUser);
$this->drupalPlaceBlock('search_form_block');
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Tests that hidden regions do not inherit blocks when a theme is installed.
*/
public function testBlockNotInHiddenRegion() {
// Ensure that the search form block is displayed.
$this->drupalGet('');
$this->assertText('Search', 'Block was displayed on the front page.');
// Install "block_test_theme" and set it as the default theme.
$theme = 'block_test_theme';
// We need to install a non-hidden theme so that there is more than one
// local task.
\Drupal::service('theme_handler')->install([$theme, 'stark']);
$this->config('system.theme')
->set('default', $theme)
->save();
// Installing a theme will cause the kernel terminate event to rebuild the
// router. Simulate that here.
\Drupal::service('router.builder')->rebuildIfNeeded();
// Ensure that "block_test_theme" is set as the default theme.
$this->drupalGet('admin/structure/block');
$this->assertText('Block test theme(' . t('active tab') . ')', 'Default local task on blocks admin page is the block test theme.');
// Ensure that the search form block is displayed.
$this->drupalGet('');
$this->assertText('Search', 'Block was displayed on the front page.');
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests for Block module regarding hook_entity_operations_alter().
*
* @group block
*/
class BlockHookOperationTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'entity_test'];
protected function setUp() {
parent::setUp();
$permissions = [
'administer blocks',
];
// Create and log in user.
$admin_user = $this->drupalCreateUser($permissions);
$this->drupalLogin($admin_user);
}
/**
* Tests the block list to see if the test_operation link is added.
*/
public function testBlockOperationAlter() {
// Add a test block, any block will do.
// Set the machine name so the test_operation link can be built later.
$block_id = mb_strtolower($this->randomMachineName(16));
$this->drupalPlaceBlock('system_powered_by_block', ['id' => $block_id]);
// Get the Block listing.
$this->drupalGet('admin/structure/block');
$test_operation_link = 'admin/structure/block/manage/' . $block_id . '/test_operation';
// Test if the test_operation link is on the page.
$this->assertLinkByHref($test_operation_link);
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests block HTML ID validity.
*
* @group block
*/
class BlockHtmlTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_test'];
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->rootUser);
// Enable the test_html block, to test HTML ID and attributes.
\Drupal::state()->set('block_test.attributes', ['data-custom-attribute' => 'foo']);
\Drupal::state()->set('block_test.content', $this->randomMachineName());
$this->drupalPlaceBlock('test_html', ['id' => 'test_html_block']);
// Enable a menu block, to test more complicated HTML.
$this->drupalPlaceBlock('system_menu_block:admin');
}
/**
* Tests for valid HTML for a block.
*/
public function testHtml() {
$this->drupalGet('');
// Ensure that a block's ID is converted to an HTML valid ID, and that
// block-specific attributes are added to the same DOM element.
$this->assertFieldByXPath('//div[@id="block-test-html-block" and @data-custom-attribute="foo"]', NULL, 'HTML ID and attributes for test block are valid and on the same DOM element.');
// Ensure expected markup for a menu block.
$elements = $this->xpath('//nav[contains(@class, :nav-class)]/ul[contains(@class, :ul-class)]/li', [':nav-class' => 'block-menu', ':ul-class' => 'menu']);
$this->assertTrue(!empty($elements), 'The proper block markup was found.');
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests block module's installation.
*
* @group block
*/
class BlockInstallTest extends BrowserTestBase {
public function testCacheTagInvalidationUponInstallation() {
// Warm the page cache.
$this->drupalGet('');
$this->assertNoText('Powered by Drupal');
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:block_list');
// Install the block module, and place the "Powered by Drupal" block.
$this->container->get('module_installer')->install(['block', 'shortcut']);
$this->rebuildContainer();
$this->container->get('router.builder')->rebuild();
$this->drupalPlaceBlock('system_powered_by_block');
// Check the same page, block.module's hook_install() should have
// invalidated the 'rendered' cache tag to make blocks show up.
$this->drupalGet('');
$this->assertCacheTag('config:block_list');
$this->assertText('Powered by Drupal');
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\block\Entity\Block;
/**
* Tests that an active block assigned to a non-existing region triggers the
* warning message and is disabled.
*
* @group block
*/
class BlockInvalidRegionTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_test'];
protected function setUp() {
parent::setUp();
// Create an admin user.
$admin_user = $this->drupalCreateUser([
'administer site configuration',
'access administration pages',
'administer blocks',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests that blocks assigned to invalid regions work correctly.
*/
public function testBlockInInvalidRegion() {
// Enable a test block and place it in an invalid region.
$block = $this->drupalPlaceBlock('test_html');
\Drupal::configFactory()->getEditable('block.block.' . $block->id())->set('region', 'invalid_region')->save();
$block = Block::load($block->id());
$warning_message = t('The block %info was assigned to the invalid region %region and has been disabled.', ['%info' => $block->id(), '%region' => 'invalid_region']);
// Clearing the cache should disable the test block placed in the invalid region.
$this->drupalPostForm('admin/config/development/performance', [], 'Clear all caches');
$this->assertRaw($warning_message, 'Enabled block was in the invalid region and has been disabled.');
// Clear the cache to check if the warning message is not triggered.
$this->drupalPostForm('admin/config/development/performance', [], 'Clear all caches');
$this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.');
// Place disabled test block in the invalid region of the default theme.
\Drupal::configFactory()->getEditable('block.block.' . $block->id())->set('region', 'invalid_region')->save();
$block = Block::load($block->id());
// Clear the cache to check if the warning message is not triggered.
$this->drupalPostForm('admin/config/development/performance', [], 'Clear all caches');
$this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.');
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Tests display of menu blocks with multiple languages.
*
* @group block
*/
class BlockLanguageCacheTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'language', 'menu_ui'];
/**
* List of langcodes.
*
* @var array
*/
protected $langcodes = [];
protected function setUp() {
parent::setUp();
// Create test languages.
$this->langcodes = [ConfigurableLanguage::load('en')];
for ($i = 1; $i < 3; ++$i) {
$language = ConfigurableLanguage::create([
'id' => 'l' . $i,
'label' => $this->randomString(),
]);
$language->save();
$this->langcodes[$i] = $language;
}
}
/**
* Creates a block in a language, check blocks page in all languages.
*/
public function testBlockLinks() {
// Create admin user to be able to access block admin.
$admin_user = $this->drupalCreateUser([
'administer blocks',
'access administration pages',
'administer menu',
]);
$this->drupalLogin($admin_user);
// Create the block cache for all languages.
foreach ($this->langcodes as $langcode) {
$this->drupalGet('admin/structure/block', ['language' => $langcode]);
$this->clickLink('Place block');
}
// Create a menu in the default language.
$edit['label'] = $this->randomMachineName();
$edit['id'] = mb_strtolower($edit['label']);
$this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
$this->assertText(t('Menu @label has been added.', ['@label' => $edit['label']]));
// Check that the block is listed for all languages.
foreach ($this->langcodes as $langcode) {
$this->drupalGet('admin/structure/block', ['language' => $langcode]);
$this->clickLink('Place block');
$this->assertText($edit['label']);
}
}
}

View file

@ -0,0 +1,185 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\block\Entity\Block;
/**
* Tests if a block can be configured to be only visible on a particular
* language.
*
* @group block
*/
class BlockLanguageTest extends BrowserTestBase {
/**
* An administrative user to configure the test environment.
*/
protected $adminUser;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['language', 'block', 'content_translation'];
protected function setUp() {
parent::setUp();
// Create a new user, allow him to manage the blocks and the languages.
$this->adminUser = $this->drupalCreateUser(['administer blocks', 'administer languages']);
$this->drupalLogin($this->adminUser);
// Add predefined language.
$edit = [
'predefined_langcode' => 'fr',
];
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
$this->assertText('French', 'Language added successfully.');
}
/**
* Tests the visibility settings for the blocks based on language.
*/
public function testLanguageBlockVisibility() {
// Check if the visibility setting is available.
$default_theme = $this->config('system.theme')->get('default');
$this->drupalGet('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme);
$this->assertField('visibility[language][langcodes][en]', 'Language visibility field is visible.');
$this->assertNoField('visibility[language][context_mapping][language]', 'Language type field is not visible.');
// Enable a standard block and set the visibility setting for one language.
$edit = [
'visibility[language][langcodes][en]' => TRUE,
'id' => strtolower($this->randomMachineName(8)),
'region' => 'sidebar_first',
];
$this->drupalPostForm('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme, $edit, t('Save block'));
// Change the default language.
$edit = [
'site_default_language' => 'fr',
];
$this->drupalPostForm('admin/config/regional/language', $edit, t('Save configuration'));
// Check that a page has a block.
$this->drupalGet('en');
$this->assertText('Powered by Drupal', 'The body of the custom block appears on the page.');
// Check that a page doesn't has a block for the current language anymore.
$this->drupalGet('fr');
$this->assertNoText('Powered by Drupal', 'The body of the custom block does not appear on the page.');
}
/**
* Tests if the visibility settings are removed if the language is deleted.
*/
public function testLanguageBlockVisibilityLanguageDelete() {
// Enable a standard block and set the visibility setting for one language.
$edit = [
'visibility' => [
'language' => [
'langcodes' => [
'fr' => 'fr',
],
'context_mapping' => ['language' => '@language.current_language_context:language_interface'],
],
],
];
$block = $this->drupalPlaceBlock('system_powered_by_block', $edit);
// Check that we have the language in config after saving the setting.
$visibility = $block->getVisibility();
$this->assertEqual('fr', $visibility['language']['langcodes']['fr'], 'Language is set in the block configuration.');
// Delete the language.
$this->drupalPostForm('admin/config/regional/language/delete/fr', [], t('Delete'));
// Check that the language is no longer stored in the configuration after
// it is deleted.
$block = Block::load($block->id());
$visibility = $block->getVisibility();
$this->assertTrue(empty($visibility['language']['langcodes']['fr']), 'Language is no longer not set in the block configuration after deleting the block.');
// Ensure that the block visibility for language is gone from the UI.
$this->drupalGet('admin/structure/block');
$this->clickLink('Configure');
$elements = $this->xpath('//details[@id="edit-visibility-language"]');
$this->assertTrue(empty($elements));
}
/**
* Tests block language visibility with different language types.
*/
public function testMultipleLanguageTypes() {
// Customize content language detection to be different from interface
// language detection.
$edit = [
// Interface language detection: only using session.
'language_interface[enabled][language-url]' => FALSE,
'language_interface[enabled][language-session]' => TRUE,
// Content language detection: only using URL.
'language_content[configurable]' => TRUE,
'language_content[enabled][language-url]' => TRUE,
'language_content[enabled][language-interface]' => FALSE,
];
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
// Check if the visibility setting is available with a type setting.
$default_theme = $this->config('system.theme')->get('default');
$this->drupalGet('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme);
$this->assertField('visibility[language][langcodes][en]', 'Language visibility field is visible.');
$this->assertField('visibility[language][context_mapping][language]', 'Language type field is visible.');
// Enable a standard block and set visibility to French only.
$block_id = strtolower($this->randomMachineName(8));
$edit = [
'visibility[language][context_mapping][language]' => '@language.current_language_context:language_interface',
'visibility[language][langcodes][fr]' => TRUE,
'id' => $block_id,
'region' => 'sidebar_first',
];
$this->drupalPostForm('admin/structure/block/add/system_powered_by_block' . '/' . $default_theme, $edit, t('Save block'));
// Interface negotiation depends on request arguments.
$this->drupalGet('node', ['query' => ['language' => 'en']]);
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
$this->drupalGet('node', ['query' => ['language' => 'fr']]);
$this->assertText('Powered by Drupal', 'The body of the block appears on the page.');
// Log in again in order to clear the interface language stored in the
// session.
$this->drupalLogout();
$this->drupalLogin($this->adminUser);
// Content language does not depend on session/request arguments.
// It will fall back on English (site default) and not display the block.
$this->drupalGet('en');
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
$this->drupalGet('fr');
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
// Change visibility to now depend on content language for this block.
$edit = [
'visibility[language][context_mapping][language]' => '@language.current_language_context:language_content',
];
$this->drupalPostForm('admin/structure/block/manage/' . $block_id, $edit, t('Save block'));
// Content language negotiation does not depend on request arguments.
// It will fall back on English (site default) and not display the block.
$this->drupalGet('node', ['query' => ['language' => 'en']]);
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
$this->drupalGet('node', ['query' => ['language' => 'fr']]);
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
// Content language negotiation depends on path prefix.
$this->drupalGet('en');
$this->assertNoText('Powered by Drupal', 'The body of the block does not appear on the page.');
$this->drupalGet('fr');
$this->assertText('Powered by Drupal', 'The body of the block appears on the page.');
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Component\Utility\Html;
use Drupal\Tests\BrowserTestBase;
/**
* Tests blocks are being rendered in order by weight.
*
* @group block
*/
class BlockRenderOrderTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['node', 'block'];
protected function setUp() {
parent::setUp();
// Create a test user.
$end_user = $this->drupalCreateUser([
'access content',
]);
$this->drupalLogin($end_user);
}
/**
* Tests the render order of the blocks.
*/
public function testBlockRenderOrder() {
// Enable test blocks and place them in the same region.
$region = 'header';
$test_blocks = [
'stark_powered' => [
'weight' => '-3',
'id' => 'stark_powered',
'label' => 'Test block A',
],
'stark_by' => [
'weight' => '3',
'id' => 'stark_by',
'label' => 'Test block C',
],
'stark_drupal' => [
'weight' => '3',
'id' => 'stark_drupal',
'label' => 'Test block B',
],
];
// Place the test blocks.
foreach ($test_blocks as $test_block) {
$this->drupalPlaceBlock('system_powered_by_block', [
'label' => $test_block['label'],
'region' => $region,
'weight' => $test_block['weight'],
'id' => $test_block['id'],
]);
}
$this->drupalGet('');
$test_content = $this->getSession()->getPage()->getContent();
$controller = $this->container->get('entity_type.manager')->getStorage('block');
foreach ($controller->loadMultiple() as $return_block) {
$id = $return_block->id();
if ($return_block_weight = $return_block->getWeight()) {
$this->assertTrue($test_blocks[$id]['weight'] == $return_block_weight, 'Block weight is set as "' . $return_block_weight . '" for ' . $id . ' block.');
$position[$id] = strpos($test_content, Html::getClass('block-' . $test_blocks[$id]['id']));
}
}
$this->assertTrue($position['stark_powered'] < $position['stark_by'], 'Blocks with different weight are rendered in the correct order.');
$this->assertTrue($position['stark_drupal'] < $position['stark_by'], 'Blocks with identical weight are rendered in alphabetical order.');
}
}

View file

@ -0,0 +1,119 @@
<?php
namespace Drupal\Tests\block\Functional;
/**
* Tests branding block display.
*
* @group block
*/
class BlockSystemBrandingTest extends BlockTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'system'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Set a site slogan.
$this->config('system.site')
->set('slogan', 'Community plumbing')
->save();
// Add the system branding block to the page.
$this->drupalPlaceBlock('system_branding_block', ['region' => 'header', 'id' => 'site-branding']);
}
/**
* Tests system branding block configuration.
*/
public function testSystemBrandingSettings() {
$site_logo_xpath = '//div[@id="block-site-branding"]//a[@class="site-logo"]';
$site_name_xpath = '//div[@id="block-site-branding"]//div[@class="site-name"]';
$site_slogan_xpath = '//div[@id="block-site-branding"]//div[@class="site-slogan"]';
// Set default block settings.
$this->drupalGet('');
$site_logo_element = $this->xpath($site_logo_xpath);
$site_name_element = $this->xpath($site_name_xpath);
$site_slogan_element = $this->xpath($site_slogan_xpath);
// Test that all branding elements are displayed.
$this->assertTrue(!empty($site_logo_element), 'The branding block logo was found.');
$this->assertTrue(!empty($site_name_element), 'The branding block site name was found.');
$this->assertTrue(!empty($site_slogan_element), 'The branding block slogan was found.');
$this->assertCacheTag('config:system.site');
// Be sure the slogan is XSS-filtered.
$this->config('system.site')
->set('slogan', '<script>alert("Community carpentry");</script>')
->save();
$this->drupalGet('');
$site_slogan_element = $this->xpath($site_slogan_xpath);
$this->assertEqual($site_slogan_element[0]->getText(), 'alert("Community carpentry");', 'The site slogan was XSS-filtered.');
// Turn just the logo off.
$this->config('block.block.site-branding')
->set('settings.use_site_logo', 0)
->save();
$this->drupalGet('');
$site_logo_element = $this->xpath($site_logo_xpath);
$site_name_element = $this->xpath($site_name_xpath);
$site_slogan_element = $this->xpath($site_slogan_xpath);
// Re-test all branding elements.
$this->assertTrue(empty($site_logo_element), 'The branding block logo was disabled.');
$this->assertTrue(!empty($site_name_element), 'The branding block site name was found.');
$this->assertTrue(!empty($site_slogan_element), 'The branding block slogan was found.');
$this->assertCacheTag('config:system.site');
// Turn just the site name off.
$this->config('block.block.site-branding')
->set('settings.use_site_logo', 1)
->set('settings.use_site_name', 0)
->save();
$this->drupalGet('');
$site_logo_element = $this->xpath($site_logo_xpath);
$site_name_element = $this->xpath($site_name_xpath);
$site_slogan_element = $this->xpath($site_slogan_xpath);
// Re-test all branding elements.
$this->assertTrue(!empty($site_logo_element), 'The branding block logo was found.');
$this->assertTrue(empty($site_name_element), 'The branding block site name was disabled.');
$this->assertTrue(!empty($site_slogan_element), 'The branding block slogan was found.');
$this->assertCacheTag('config:system.site');
// Turn just the site slogan off.
$this->config('block.block.site-branding')
->set('settings.use_site_name', 1)
->set('settings.use_site_slogan', 0)
->save();
$this->drupalGet('');
$site_logo_element = $this->xpath($site_logo_xpath);
$site_name_element = $this->xpath($site_name_xpath);
$site_slogan_element = $this->xpath($site_slogan_xpath);
// Re-test all branding elements.
$this->assertTrue(!empty($site_logo_element), 'The branding block logo was found.');
$this->assertTrue(!empty($site_name_element), 'The branding block site name was found.');
$this->assertTrue(empty($site_slogan_element), 'The branding block slogan was disabled.');
$this->assertCacheTag('config:system.site');
// Turn the site name and the site slogan off.
$this->config('block.block.site-branding')
->set('settings.use_site_name', 0)
->set('settings.use_site_slogan', 0)
->save();
$this->drupalGet('');
$site_logo_element = $this->xpath($site_logo_xpath);
$site_name_element = $this->xpath($site_name_xpath);
$site_slogan_element = $this->xpath($site_slogan_xpath);
// Re-test all branding elements.
$this->assertTrue(!empty($site_logo_element), 'The branding block logo was found.');
$this->assertTrue(empty($site_name_element), 'The branding block site name was disabled.');
$this->assertTrue(empty($site_slogan_element), 'The branding block slogan was disabled.');
$this->assertCacheTag('config:system.site');
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\block\Entity\Block;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the block_theme_suggestions_block() function.
*
* @group block
*/
class BlockTemplateSuggestionsTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block'];
/**
* Tests template suggestions from block_theme_suggestions_block().
*/
public function testBlockThemeHookSuggestions() {
// Define a block with a derivative to be preprocessed, which includes both
// an underscore (not transformed) and a hyphen (transformed to underscore),
// and generates possibilities for each level of derivative.
// @todo Clarify this comment.
$block = Block::create([
'plugin' => 'system_menu_block:admin',
'region' => 'footer',
'id' => 'machinename',
]);
$variables = [];
$plugin = $block->getPlugin();
$variables['elements']['#configuration'] = $plugin->getConfiguration();
$variables['elements']['#plugin_id'] = $plugin->getPluginId();
$variables['elements']['#id'] = $block->id();
$variables['elements']['#base_plugin_id'] = $plugin->getBaseId();
$variables['elements']['#derivative_plugin_id'] = $plugin->getDerivativeId();
$variables['elements']['content'] = [];
$suggestions = block_theme_suggestions_block($variables);
$this->assertEqual($suggestions, ['block__system', 'block__system_menu_block', 'block__system_menu_block__admin', 'block__machinename']);
}
}

View file

@ -0,0 +1,551 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Component\Utility\Html;
use Drupal\block\Entity\Block;
use Drupal\Core\Url;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests basic block functionality.
*
* @group block
*/
class BlockTest extends BlockTestBase {
/**
* Tests block visibility.
*/
public function testBlockVisibility() {
$block_name = 'system_powered_by_block';
// Create a random title for the block.
$title = $this->randomMachineName(8);
// Enable a standard block.
$default_theme = $this->config('system.theme')->get('default');
$edit = [
'id' => strtolower($this->randomMachineName(8)),
'region' => 'sidebar_first',
'settings[label]' => $title,
'settings[label_display]' => TRUE,
];
// Set the block to be hidden on any user path, and to be shown only to
// authenticated users.
$edit['visibility[request_path][pages]'] = '/user*';
$edit['visibility[request_path][negate]'] = TRUE;
$edit['visibility[user_role][roles][' . RoleInterface::AUTHENTICATED_ID . ']'] = TRUE;
$this->drupalGet('admin/structure/block/add/' . $block_name . '/' . $default_theme);
$this->assertFieldChecked('edit-visibility-request-path-negate-0');
$this->drupalPostForm(NULL, $edit, t('Save block'));
$this->assertText('The block configuration has been saved.', 'Block was saved');
$this->clickLink('Configure');
$this->assertFieldChecked('edit-visibility-request-path-negate-1');
$this->drupalGet('');
$this->assertText($title, 'Block was displayed on the front page.');
$this->drupalGet('user');
$this->assertNoText($title, 'Block was not displayed according to block visibility rules.');
// Confirm that the block is not displayed to anonymous users.
$this->drupalLogout();
$this->drupalGet('');
$this->assertNoText($title, 'Block was not displayed to anonymous users.');
// Confirm that an empty block is not displayed.
$this->assertNoText('Powered by Drupal', 'Empty block not displayed.');
$this->assertNoRaw('sidebar-first', 'Empty sidebar-first region is not displayed.');
}
/**
* Tests that visibility can be properly toggled.
*/
public function testBlockToggleVisibility() {
$block_name = 'system_powered_by_block';
// Create a random title for the block.
$title = $this->randomMachineName(8);
// Enable a standard block.
$default_theme = $this->config('system.theme')->get('default');
$edit = [
'id' => strtolower($this->randomMachineName(8)),
'region' => 'sidebar_first',
'settings[label]' => $title,
];
$block_id = $edit['id'];
// Set the block to be shown only to authenticated users.
$edit['visibility[user_role][roles][' . RoleInterface::AUTHENTICATED_ID . ']'] = TRUE;
$this->drupalPostForm('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block'));
$this->clickLink('Configure');
$this->assertFieldChecked('edit-visibility-user-role-roles-authenticated');
$edit = [
'visibility[user_role][roles][' . RoleInterface::AUTHENTICATED_ID . ']' => FALSE,
];
$this->drupalPostForm(NULL, $edit, 'Save block');
$this->clickLink('Configure');
$this->assertNoFieldChecked('edit-visibility-user-role-roles-authenticated');
// Ensure that no visibility is configured.
/** @var \Drupal\block\BlockInterface $block */
$block = Block::load($block_id);
$visibility_config = $block->getVisibilityConditions()->getConfiguration();
$this->assertIdentical([], $visibility_config);
$this->assertIdentical([], $block->get('visibility'));
}
/**
* Test block visibility when leaving "pages" textarea empty.
*/
public function testBlockVisibilityListedEmpty() {
$block_name = 'system_powered_by_block';
// Create a random title for the block.
$title = $this->randomMachineName(8);
// Enable a standard block.
$default_theme = $this->config('system.theme')->get('default');
$edit = [
'id' => strtolower($this->randomMachineName(8)),
'region' => 'sidebar_first',
'settings[label]' => $title,
'visibility[request_path][negate]' => TRUE,
];
// Set the block to be hidden on any user path, and to be shown only to
// authenticated users.
$this->drupalPostForm('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block'));
$this->assertText('The block configuration has been saved.', 'Block was saved');
$this->drupalGet('user');
$this->assertNoText($title, 'Block was not displayed according to block visibility rules.');
$this->drupalGet('USER');
$this->assertNoText($title, 'Block was not displayed according to block visibility rules regardless of path case.');
// Confirm that the block is not displayed to anonymous users.
$this->drupalLogout();
$this->drupalGet('');
$this->assertNoText($title, 'Block was not displayed to anonymous users on the front page.');
}
/**
* Tests adding a block from the library page with a weight query string.
*/
public function testAddBlockFromLibraryWithWeight() {
$default_theme = $this->config('system.theme')->get('default');
// Test one positive, zero, and one negative weight.
foreach (['7', '0', '-9'] as $weight) {
$options = [
'query' => [
'region' => 'sidebar_first',
'weight' => $weight,
],
];
$this->drupalGet(Url::fromRoute('block.admin_library', ['theme' => $default_theme], $options));
$block_name = 'system_powered_by_block';
$add_url = Url::fromRoute('block.admin_add', [
'plugin_id' => $block_name,
'theme' => $default_theme,
]);
$links = $this->xpath('//a[contains(@href, :href)]', [':href' => $add_url->toString()]);
$this->assertEqual(1, count($links), 'Found one matching link.');
$this->assertEqual(t('Place block'), $links[0]->getText(), 'Found the expected link text.');
list($path, $query_string) = explode('?', $links[0]->getAttribute('href'), 2);
parse_str($query_string, $query_parts);
$this->assertEqual($weight, $query_parts['weight'], 'Found the expected weight query string.');
// Create a random title for the block.
$title = $this->randomMachineName(8);
$block_id = strtolower($this->randomMachineName(8));
$edit = [
'id' => $block_id,
'settings[label]' => $title,
];
// Create the block using the link parsed from the library page.
$this->drupalPostForm($this->getAbsoluteUrl($links[0]->getAttribute('href')), $edit, t('Save block'));
// Ensure that the block was created with the expected weight.
/** @var \Drupal\block\BlockInterface $block */
$block = Block::load($block_id);
$this->assertEqual($weight, $block->getWeight(), 'Found the block with expected weight.');
}
}
/**
* Test configuring and moving a module-define block to specific regions.
*/
public function testBlock() {
// Place page title block to test error messages.
$this->drupalPlaceBlock('page_title_block');
// Disable the block.
$this->drupalGet('admin/structure/block');
$this->clickLink('Disable');
// Select the 'Powered by Drupal' block to be configured and moved.
$block = [];
$block['id'] = 'system_powered_by_block';
$block['settings[label]'] = $this->randomMachineName(8);
$block['settings[label_display]'] = TRUE;
$block['theme'] = $this->config('system.theme')->get('default');
$block['region'] = 'header';
// Set block title to confirm that interface works and override any custom titles.
$this->drupalPostForm('admin/structure/block/add/' . $block['id'] . '/' . $block['theme'], ['settings[label]' => $block['settings[label]'], 'settings[label_display]' => $block['settings[label_display]'], 'id' => $block['id'], 'region' => $block['region']], t('Save block'));
$this->assertText(t('The block configuration has been saved.'), 'Block title set.');
// Check to see if the block was created by checking its configuration.
$instance = Block::load($block['id']);
$this->assertEqual($instance->label(), $block['settings[label]'], 'Stored block title found.');
// Check whether the block can be moved to all available regions.
foreach ($this->regions as $region) {
$this->moveBlockToRegion($block, $region);
}
// Disable the block.
$this->drupalGet('admin/structure/block');
$this->clickLink('Disable');
// Confirm that the block is now listed as disabled.
$this->assertText(t('The block settings have been updated.'), 'Block successfully moved to disabled region.');
// Confirm that the block instance title and markup are not displayed.
$this->drupalGet('node');
$this->assertNoText(t($block['settings[label]']));
// Check for <div id="block-my-block-instance-name"> if the machine name
// is my_block_instance_name.
$xpath = $this->buildXPathQuery('//div[@id=:id]/*', [':id' => 'block-' . str_replace('_', '-', strtolower($block['id']))]);
$this->assertNoFieldByXPath($xpath, FALSE, 'Block found in no regions.');
// Test deleting the block from the edit form.
$this->drupalGet('admin/structure/block/manage/' . $block['id']);
$this->clickLink(t('Remove block'));
$this->assertRaw(t('Are you sure you want to remove the block @name?', ['@name' => $block['settings[label]']]));
$this->drupalPostForm(NULL, [], t('Remove'));
$this->assertRaw(t('The block %name has been removed.', ['%name' => $block['settings[label]']]));
// Test deleting a block via "Configure block" link.
$block = $this->drupalPlaceBlock('system_powered_by_block');
$this->drupalGet('admin/structure/block/manage/' . $block->id(), ['query' => ['destination' => 'admin']]);
$this->clickLink(t('Remove block'));
$this->assertRaw(t('Are you sure you want to remove the block @name?', ['@name' => $block->label()]));
$this->drupalPostForm(NULL, [], t('Remove'));
$this->assertRaw(t('The block %name has been removed.', ['%name' => $block->label()]));
$this->assertUrl('admin');
$this->assertNoRaw($block->id());
}
/**
* Tests that the block form has a theme selector when not passed via the URL.
*/
public function testBlockThemeSelector() {
// Install all themes.
\Drupal::service('theme_handler')->install(['bartik', 'seven', 'stark']);
$theme_settings = $this->config('system.theme');
foreach (['bartik', 'seven', 'stark'] as $theme) {
$this->drupalGet('admin/structure/block/list/' . $theme);
$this->assertTitle(t('Block layout') . ' | Drupal');
// Select the 'Powered by Drupal' block to be placed.
$block = [];
$block['id'] = strtolower($this->randomMachineName());
$block['theme'] = $theme;
$block['region'] = 'content';
$this->drupalPostForm('admin/structure/block/add/system_powered_by_block', $block, t('Save block'));
$this->assertText(t('The block configuration has been saved.'));
$this->assertUrl('admin/structure/block/list/' . $theme . '?block-placement=' . Html::getClass($block['id']));
// Set the default theme and ensure the block is placed.
$theme_settings->set('default', $theme)->save();
$this->drupalGet('');
$elements = $this->xpath('//div[@id = :id]', [':id' => Html::getUniqueId('block-' . $block['id'])]);
$this->assertTrue(!empty($elements), 'The block was found.');
}
}
/**
* Test block display of theme titles.
*/
public function testThemeName() {
// Enable the help block.
$this->drupalPlaceBlock('help_block', ['region' => 'help']);
$this->drupalPlaceBlock('local_tasks_block');
// Explicitly set the default and admin themes.
$theme = 'block_test_specialchars_theme';
\Drupal::service('theme_handler')->install([$theme]);
\Drupal::service('router.builder')->rebuild();
$this->drupalGet('admin/structure/block');
$this->assertEscaped('<"Cat" & \'Mouse\'>');
$this->drupalGet('admin/structure/block/list/block_test_specialchars_theme');
$this->assertEscaped('Demonstrate block regions (<"Cat" & \'Mouse\'>)');
}
/**
* Test block title display settings.
*/
public function testHideBlockTitle() {
$block_name = 'system_powered_by_block';
// Create a random title for the block.
$title = $this->randomMachineName(8);
$id = strtolower($this->randomMachineName(8));
// Enable a standard block.
$default_theme = $this->config('system.theme')->get('default');
$edit = [
'id' => $id,
'region' => 'sidebar_first',
'settings[label]' => $title,
];
$this->drupalPostForm('admin/structure/block/add/' . $block_name . '/' . $default_theme, $edit, t('Save block'));
$this->assertText('The block configuration has been saved.', 'Block was saved');
$this->drupalGet('user');
$this->assertNoText($title, 'Block title was not displayed by default.');
$edit = [
'settings[label_display]' => TRUE,
];
$this->drupalPostForm('admin/structure/block/manage/' . $id, $edit, t('Save block'));
$this->assertText('The block configuration has been saved.', 'Block was saved');
$this->drupalGet('admin/structure/block/manage/' . $id);
$this->assertFieldChecked('edit-settings-label-display', 'The display_block option has the correct default value on the configuration form.');
$this->drupalGet('user');
$this->assertText($title, 'Block title was displayed when enabled.');
}
/**
* Moves a block to a given region via the UI and confirms the result.
*
* @param array $block
* An array of information about the block, including the following keys:
* - module: The module providing the block.
* - title: The title of the block.
* - delta: The block's delta key.
* @param string $region
* The machine name of the theme region to move the block to, for example
* 'header' or 'sidebar_first'.
*/
public function moveBlockToRegion(array $block, $region) {
// Set the created block to a specific region.
$block += ['theme' => $this->config('system.theme')->get('default')];
$edit = [];
$edit['blocks[' . $block['id'] . '][region]'] = $region;
$this->drupalPostForm('admin/structure/block', $edit, t('Save blocks'));
// Confirm that the block was moved to the proper region.
$this->assertText(t('The block settings have been updated.'), format_string('Block successfully moved to %region_name region.', ['%region_name' => $region]));
// Confirm that the block is being displayed.
$this->drupalGet('');
$this->assertText(t($block['settings[label]']), 'Block successfully being displayed on the page.');
// Confirm that the custom block was found at the proper region.
$xpath = $this->buildXPathQuery('//div[@class=:region-class]//div[@id=:block-id]/*', [
':region-class' => 'region region-' . Html::getClass($region),
':block-id' => 'block-' . str_replace('_', '-', strtolower($block['id'])),
]);
$this->assertFieldByXPath($xpath, NULL, t('Block found in %region_name region.', ['%region_name' => Html::getClass($region)]));
}
/**
* Test that cache tags are properly set and bubbled up to the page cache.
*
* Verify that invalidation of these cache tags works:
* - "block:<block ID>"
* - "block_plugin:<block plugin ID>"
*/
public function testBlockCacheTags() {
// The page cache only works for anonymous users.
$this->drupalLogout();
// Enable page caching.
$config = $this->config('system.performance');
$config->set('cache.page.max_age', 300);
$config->save();
// Place the "Powered by Drupal" block.
$block = $this->drupalPlaceBlock('system_powered_by_block', ['id' => 'powered']);
// Prime the page cache.
$this->drupalGet('<front>');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags in
// both the page and block caches.
$this->drupalGet('<front>');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
$cid_parts = [\Drupal::url('<front>', [], ['absolute' => TRUE]), 'html'];
$cid = implode(':', $cid_parts);
$cache_entry = \Drupal::cache('page')->get($cid);
$expected_cache_tags = [
'config:block_list',
'block_view',
'config:block.block.powered',
'config:user.role.anonymous',
'http_response',
'rendered',
];
sort($expected_cache_tags);
$keys = \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:language_interface', 'theme', 'user.permissions'])->getKeys();
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
$cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:' . implode(':', $keys));
$expected_cache_tags = [
'block_view',
'config:block.block.powered',
'rendered',
];
sort($expected_cache_tags);
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
// The "Powered by Drupal" block is modified; verify a cache miss.
$block->setRegion('content');
$block->save();
$this->drupalGet('<front>');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
// Now we should have a cache hit again.
$this->drupalGet('<front>');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
// Place the "Powered by Drupal" block another time; verify a cache miss.
$block_2 = $this->drupalPlaceBlock('system_powered_by_block', ['id' => 'powered-2']);
$this->drupalGet('<front>');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$this->drupalGet('<front>');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
$cid_parts = [\Drupal::url('<front>', [], ['absolute' => TRUE]), 'html'];
$cid = implode(':', $cid_parts);
$cache_entry = \Drupal::cache('page')->get($cid);
$expected_cache_tags = [
'config:block_list',
'block_view',
'config:block.block.powered',
'config:block.block.powered-2',
'config:user.role.anonymous',
'http_response',
'rendered',
];
sort($expected_cache_tags);
$this->assertEqual($cache_entry->tags, $expected_cache_tags);
$expected_cache_tags = [
'block_view',
'config:block.block.powered',
'rendered',
];
sort($expected_cache_tags);
$keys = \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:language_interface', 'theme', 'user.permissions'])->getKeys();
$cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:' . implode(':', $keys));
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
$expected_cache_tags = [
'block_view',
'config:block.block.powered-2',
'rendered',
];
sort($expected_cache_tags);
$keys = \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:language_interface', 'theme', 'user.permissions'])->getKeys();
$cache_entry = \Drupal::cache('render')->get('entity_view:block:powered-2:' . implode(':', $keys));
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
// Now we should have a cache hit again.
$this->drupalGet('<front>');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
// Delete the "Powered by Drupal" blocks; verify a cache miss.
entity_delete_multiple('block', ['powered', 'powered-2']);
$this->drupalGet('<front>');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
}
/**
* Tests that a link exists to block layout from the appearance form.
*/
public function testThemeAdminLink() {
$this->drupalPlaceBlock('help_block', ['region' => 'help']);
$theme_admin = $this->drupalCreateUser([
'administer blocks',
'administer themes',
'access administration pages',
]);
$this->drupalLogin($theme_admin);
$this->drupalGet('admin/appearance');
$this->assertText('You can place blocks for each theme on the block layout page');
$this->assertLinkByHref('admin/structure/block');
}
/**
* Tests that uninstalling a theme removes its block configuration.
*/
public function testUninstallTheme() {
/** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
$theme_handler = \Drupal::service('theme_handler');
$theme_handler->install(['seven']);
$this->config('system.theme')->set('default', 'seven')->save();
$block = $this->drupalPlaceBlock('system_powered_by_block', ['theme' => 'seven', 'region' => 'help']);
$this->drupalGet('<front>');
$this->assertText('Powered by Drupal');
$this->config('system.theme')->set('default', 'classy')->save();
$theme_handler->uninstall(['seven']);
// Ensure that the block configuration does not exist anymore.
$this->assertIdentical(NULL, Block::load($block->id()));
}
/**
* Tests the block access.
*/
public function testBlockAccess() {
$this->drupalPlaceBlock('test_access', ['region' => 'help']);
$this->drupalGet('<front>');
$this->assertNoText('Hello test world');
\Drupal::state()->set('test_block_access', TRUE);
$this->drupalGet('<front>');
$this->assertText('Hello test world');
}
/**
* Tests block_user_role_delete.
*/
public function testBlockUserRoleDelete() {
$role1 = Role::create(['id' => 'test_role1', 'name' => $this->randomString()]);
$role1->save();
$role2 = Role::create(['id' => 'test_role2', 'name' => $this->randomString()]);
$role2->save();
$block = Block::create([
'id' => $this->randomMachineName(),
'plugin' => 'system_powered_by_block',
]);
$block->setVisibilityConfig('user_role', [
'roles' => [
$role1->id() => $role1->id(),
$role2->id() => $role2->id(),
],
]);
$block->save();
$this->assertEqual($block->getVisibility()['user_role']['roles'], [
$role1->id() => $role1->id(),
$role2->id() => $role2->id(),
]);
$role1->delete();
$block = Block::load($block->id());
$this->assertEqual($block->getVisibility()['user_role']['roles'], [
$role2->id() => $role2->id(),
]);
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\filter\Entity\FilterFormat;
use Drupal\Tests\BrowserTestBase;
/**
* Provides setup and helper methods for block module tests.
*/
abstract class BlockTestBase extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'filter', 'test_page_test', 'help', 'block_test'];
/**
* A list of theme regions to test.
*
* @var array
*/
protected $regions;
/**
* A test user with administrative privileges.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Use the test page as the front page.
$this->config('system.site')->set('page.front', '/test-page')->save();
// Create Full HTML text format.
$full_html_format = FilterFormat::create([
'format' => 'full_html',
'name' => 'Full HTML',
]);
$full_html_format->save();
// Create and log in an administrative user having access to the Full HTML
// text format.
$this->adminUser = $this->drupalCreateUser([
'administer blocks',
$full_html_format->getPermissionName(),
'access administration pages',
]);
$this->drupalLogin($this->adminUser);
// Define the existing regions.
$this->regions = [
'header',
'sidebar_first',
'content',
'sidebar_second',
'footer',
];
$block_storage = $this->container->get('entity_type.manager')->getStorage('block');
$blocks = $block_storage->loadByProperties(['theme' => $this->config('system.theme')->get('default')]);
foreach ($blocks as $block) {
$block->delete();
}
}
}

View file

@ -0,0 +1,375 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Component\Utility\Html;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
use Drupal\Tests\BrowserTestBase;
/**
* Tests that the block configuration UI exists and stores data correctly.
*
* @group block
*/
class BlockUiTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_test', 'help', 'condition_test'];
protected $regions;
/**
* The submitted block values used by this test.
*
* @var array
*/
protected $blockValues;
/**
* The block entities used by this test.
*
* @var \Drupal\block\BlockInterface[]
*/
protected $blocks;
/**
* An administrative user to configure the test environment.
*/
protected $adminUser;
protected function setUp() {
parent::setUp();
// Create and log in an administrative user.
$this->adminUser = $this->drupalCreateUser([
'administer blocks',
'access administration pages',
]);
$this->drupalLogin($this->adminUser);
// Enable some test blocks.
$this->blockValues = [
[
'label' => 'Tools',
'tr' => '5',
'plugin_id' => 'system_menu_block:tools',
'settings' => ['region' => 'sidebar_second', 'id' => 'tools'],
'test_weight' => '-1',
],
[
'label' => 'Powered by Drupal',
'tr' => '16',
'plugin_id' => 'system_powered_by_block',
'settings' => ['region' => 'footer', 'id' => 'powered'],
'test_weight' => '0',
],
];
$this->blocks = [];
foreach ($this->blockValues as $values) {
$this->blocks[] = $this->drupalPlaceBlock($values['plugin_id'], $values['settings']);
}
}
/**
* Test block demo page exists and functions correctly.
*/
public function testBlockDemoUiPage() {
$this->drupalPlaceBlock('help_block', ['region' => 'help']);
$this->drupalGet('admin/structure/block');
$this->clickLink(t('Demonstrate block regions (@theme)', ['@theme' => 'Classy']));
$elements = $this->xpath('//div[contains(@class, "region-highlighted")]/div[contains(@class, "block-region") and contains(text(), :title)]', [':title' => 'Highlighted']);
$this->assertTrue(!empty($elements), 'Block demo regions are shown.');
\Drupal::service('theme_handler')->install(['test_theme']);
$this->drupalGet('admin/structure/block/demo/test_theme');
$this->assertEscaped('<strong>Test theme</strong>');
\Drupal::service('theme_handler')->install(['stable']);
$this->drupalGet('admin/structure/block/demo/stable');
$this->assertResponse(404, 'Hidden themes that are not the default theme are not supported by the block demo screen');
}
/**
* Test block admin page exists and functions correctly.
*/
public function testBlockAdminUiPage() {
// Visit the blocks admin ui.
$this->drupalGet('admin/structure/block');
// Look for the blocks table.
$blocks_table = $this->xpath("//table[@id='blocks']");
$this->assertTrue(!empty($blocks_table), 'The blocks table is being rendered.');
// Look for test blocks in the table.
foreach ($this->blockValues as $delta => $values) {
$block = $this->blocks[$delta];
$label = $block->label();
$element = $this->xpath('//*[@id="blocks"]/tbody/tr[' . $values['tr'] . ']/td[1]/text()');
$this->assertEquals($element[0]->getText(), $label, 'The "' . $label . '" block title is set inside the ' . $values['settings']['region'] . ' region.');
// Look for a test block region select form element.
$this->assertField('blocks[' . $values['settings']['id'] . '][region]', 'The block "' . $values['label'] . '" has a region assignment field.');
// Move the test block to the header region.
$edit['blocks[' . $values['settings']['id'] . '][region]'] = 'header';
// Look for a test block weight select form element.
$this->assertField('blocks[' . $values['settings']['id'] . '][weight]', 'The block "' . $values['label'] . '" has a weight assignment field.');
// Change the test block's weight.
$edit['blocks[' . $values['settings']['id'] . '][weight]'] = $values['test_weight'];
}
$this->drupalPostForm('admin/structure/block', $edit, t('Save blocks'));
foreach ($this->blockValues as $values) {
// Check if the region and weight settings changes have persisted.
$this->assertOptionSelected(
'edit-blocks-' . $values['settings']['id'] . '-region',
'header',
'The block "' . $label . '" has the correct region assignment (header).'
);
$this->assertOptionSelected(
'edit-blocks-' . $values['settings']['id'] . '-weight',
$values['test_weight'],
'The block "' . $label . '" has the correct weight assignment (' . $values['test_weight'] . ').'
);
}
// Add a block with a machine name the same as a region name.
$this->drupalPlaceBlock('system_powered_by_block', ['region' => 'header', 'id' => 'header']);
$this->drupalGet('admin/structure/block');
$element = $this->xpath('//tr[contains(@class, :class)]', [':class' => 'region-title-header']);
$this->assertTrue(!empty($element));
// Ensure hidden themes do not appear in the UI. Enable another non base
// theme and place the local tasks block.
$this->assertTrue(\Drupal::service('theme_handler')->themeExists('classy'), 'The classy base theme is enabled');
$this->drupalPlaceBlock('local_tasks_block', ['region' => 'header']);
\Drupal::service('theme_installer')->install(['stable', 'stark']);
$this->drupalGet('admin/structure/block');
$theme_handler = \Drupal::service('theme_handler');
$this->assertLink($theme_handler->getName('classy'));
$this->assertLink($theme_handler->getName('stark'));
$this->assertNoLink($theme_handler->getName('stable'));
$this->drupalGet('admin/structure/block/list/stable');
$this->assertResponse(404, 'Placing blocks through UI is not possible for a hidden base theme.');
\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/structure/block');
$this->assertLink($theme_handler->getName('stable'));
$this->drupalGet('admin/structure/block/list/stable');
$this->assertResponse(200, 'Placing blocks through UI is possible for a hidden base theme that is the admin theme.');
}
/**
* Tests the block categories on the listing page.
*/
public function testCandidateBlockList() {
$arguments = [
':title' => 'Display message',
':category' => 'Block test',
':href' => 'admin/structure/block/add/test_block_instantiation/classy',
];
$pattern = '//tr[.//td/div[text()=:title] and .//td[text()=:category] and .//td//a[contains(@href, :href)]]';
$this->drupalGet('admin/structure/block');
$this->clickLink('Place block');
$elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The test block appears in the category for its module.');
// Trigger the custom category addition in block_test_block_alter().
$this->container->get('state')->set('block_test_info_alter', TRUE);
$this->container->get('plugin.manager.block')->clearCachedDefinitions();
$this->drupalGet('admin/structure/block');
$this->clickLink('Place block');
$arguments[':category'] = 'Custom category';
$elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The test block appears in a custom category controlled by block_test_block_alter().');
}
/**
* Tests the behavior of unsatisfied context-aware blocks.
*/
public function testContextAwareUnsatisfiedBlocks() {
$arguments = [
':category' => 'Block test',
':href' => 'admin/structure/block/add/test_context_aware_unsatisfied/classy',
':text' => 'Test context-aware unsatisfied block',
];
$this->drupalGet('admin/structure/block');
$this->clickLink('Place block');
$elements = $this->xpath('//tr[.//td/div[text()=:text] and .//td[text()=:category] and .//td//a[contains(@href, :href)]]', $arguments);
$this->assertTrue(empty($elements), 'The context-aware test block does not appear.');
$definition = \Drupal::service('plugin.manager.block')->getDefinition('test_context_aware_unsatisfied');
$this->assertTrue(!empty($definition), 'The context-aware test block does not exist.');
}
/**
* Tests the behavior of context-aware blocks.
*/
public function testContextAwareBlocks() {
$expected_text = '<div id="test_context_aware--username">' . \Drupal::currentUser()->getUsername() . '</div>';
$this->drupalGet('');
$this->assertNoText('Test context-aware block');
$this->assertNoRaw($expected_text);
$block_url = 'admin/structure/block/add/test_context_aware/classy';
$arguments = [
':title' => 'Test context-aware block',
':category' => 'Block test',
':href' => $block_url,
];
$pattern = '//tr[.//td/div[text()=:title] and .//td[text()=:category] and .//td//a[contains(@href, :href)]]';
$this->drupalGet('admin/structure/block');
$this->clickLink('Place block');
$elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The context-aware test block appears.');
$definition = \Drupal::service('plugin.manager.block')->getDefinition('test_context_aware');
$this->assertTrue(!empty($definition), 'The context-aware test block exists.');
$edit = [
'region' => 'content',
'settings[context_mapping][user]' => '@block_test.multiple_static_context:userB',
];
$this->drupalPostForm($block_url, $edit, 'Save block');
$this->drupalGet('');
$this->assertText('Test context-aware block');
$this->assertText('User context found.');
$this->assertRaw($expected_text);
// Test context mapping form element is not visible if there are no valid
// context options for the block (the test_context_aware_no_valid_context_options
// block has one context defined which is not available for it on the
// Block Layout interface).
$this->drupalGet('admin/structure/block/add/test_context_aware_no_valid_context_options/classy');
$this->assertSession()->fieldNotExists('edit-settings-context-mapping-email');
// Test context mapping allows empty selection for optional contexts.
$this->drupalGet('admin/structure/block/manage/testcontextawareblock');
$edit = [
'settings[context_mapping][user]' => '',
];
$this->drupalPostForm(NULL, $edit, 'Save block');
$this->drupalGet('');
$this->assertText('No context mapping selected.');
$this->assertNoText('User context found.');
// Tests that conditions with missing context are not displayed.
$this->drupalGet('admin/structure/block/manage/testcontextawareblock');
$this->assertNoRaw('No existing type');
$this->assertNoFieldByXPath('//*[@name="visibility[condition_test_no_existing_type][negate]"]');
}
/**
* Tests that the BlockForm populates machine name correctly.
*/
public function testMachineNameSuggestion() {
$url = 'admin/structure/block/add/test_block_instantiation/classy';
$this->drupalGet($url);
$this->assertFieldByName('id', 'displaymessage', 'Block form uses raw machine name suggestion when no instance already exists.');
$edit = ['region' => 'content'];
$this->drupalPostForm($url, $edit, 'Save block');
$this->assertText('The block configuration has been saved.');
// Now, check to make sure the form starts by autoincrementing correctly.
$this->drupalGet($url);
$this->assertFieldByName('id', 'displaymessage_2', 'Block form appends _2 to plugin-suggested machine name when an instance already exists.');
$this->drupalPostForm($url, $edit, 'Save block');
$this->assertText('The block configuration has been saved.');
// And verify that it continues working beyond just the first two.
$this->drupalGet($url);
$this->assertFieldByName('id', 'displaymessage_3', 'Block form appends _3 to plugin-suggested machine name when two instances already exist.');
}
/**
* Tests the block placement indicator.
*/
public function testBlockPlacementIndicator() {
// Test the block placement indicator with using the domain as URL language
// indicator. This causes destination query parameters to be absolute URLs.
\Drupal::service('module_installer')->install(['language', 'locale']);
$this->container = \Drupal::getContainer();
ConfigurableLanguage::createFromLangcode('it')->save();
$config = $this->config('language.types');
$config->set('negotiation.language_interface.enabled', [
LanguageNegotiationUrl::METHOD_ID => -10,
]);
$config->save();
$config = $this->config('language.negotiation');
$config->set('url.source', LanguageNegotiationUrl::CONFIG_DOMAIN);
$config->set('url.domains', [
'en' => \Drupal::request()->getHost(),
'it' => 'it.example.com',
]);
$config->save();
// Select the 'Powered by Drupal' block to be placed.
$block = [];
$block['id'] = strtolower($this->randomMachineName());
$block['theme'] = 'classy';
$block['region'] = 'content';
// After adding a block, it will indicate which block was just added.
$this->drupalPostForm('admin/structure/block/add/system_powered_by_block', $block, t('Save block'));
$this->assertSession()->addressEquals('admin/structure/block/list/classy?block-placement=' . Html::getClass($block['id']));
// Resaving the block page will remove the block placement indicator.
$this->drupalPostForm(NULL, [], t('Save blocks'));
$this->assertSession()->addressEquals('admin/structure/block/list/classy');
// Place another block and test the remove functionality works with the
// block placement indicator. Click the first 'Place block' link to bring up
// the list of blocks to place in the first available region.
$this->clickLink('Place block');
// Select the first available block.
$this->clickLink('Place block');
$block = [];
$block['id'] = strtolower($this->randomMachineName());
$block['theme'] = 'classy';
$this->submitForm([], 'Save block');
$this->assertSession()->addressEquals('admin/structure/block/list/classy?block-placement=' . Html::getClass($block['id']));
// Removing a block will remove the block placement indicator.
$this->clickLink('Remove');
$this->submitForm([], 'Remove');
// @todo https://www.drupal.org/project/drupal/issues/2980527 this should be
// 'admin/structure/block/list/classy' but there is a bug.
$this->assertSession()->addressEquals('admin/structure/block');
}
/**
* Tests if validation errors are passed plugin form to the parent form.
*/
public function testBlockValidateErrors() {
$this->drupalPostForm('admin/structure/block/add/test_settings_validation/classy', ['region' => 'content', 'settings[digits]' => 'abc'], t('Save block'));
$arguments = [':message' => 'Only digits are allowed'];
$pattern = '//div[contains(@class,"messages messages--error")]/div[contains(text()[2],:message)]';
$elements = $this->xpath($pattern, $arguments);
$this->assertTrue($elements, 'Plugin error message found in parent form.');
$error_class_pattern = '//div[contains(@class,"form-item-settings-digits")]/input[contains(@class,"error")]';
$error_class = $this->xpath($error_class_pattern);
$this->assertTrue($error_class, 'Plugin error class found in parent form.');
}
/**
* Tests that the enable/disable routes are protected from CSRF.
*/
public function testRouteProtection() {
// Get the first block generated in our setUp method.
/** @var \Drupal\block\BlockInterface $block */
$block = reset($this->blocks);
// Ensure that the enable and disable routes are protected.
$this->drupalGet('admin/structure/block/manage/' . $block->id() . '/disable');
$this->assertResponse(403);
$this->drupalGet('admin/structure/block/manage/' . $block->id() . '/enable');
$this->assertResponse(403);
}
}

View file

@ -0,0 +1,167 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\Core\Url;
use Drupal\system\Entity\Menu;
use Drupal\Tests\BrowserTestBase;
use Drupal\views\Entity\View;
/**
* Tests that the block module properly escapes block descriptions.
*
* @group block
*/
class BlockXssTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_content', 'menu_ui', 'views'];
/**
* Tests that nothing is escaped other than the blocks explicitly tested.
*/
public function testNoUnexpectedEscaping() {
$this->drupalLogin($this->drupalCreateUser(['administer blocks', 'access administration pages']));
$this->drupalGet(Url::fromRoute('block.admin_display'));
$this->clickLink('Place block');
$this->assertNoEscaped('<');
}
/**
* Tests XSS in title.
*/
public function testXssInTitle() {
$this->container->get('module_installer')->install(['block_test']);
$this->drupalPlaceBlock('test_xss_title', ['label' => '<script>alert("XSS label");</script>']);
\Drupal::state()->set('block_test.content', $this->randomMachineName());
$this->drupalGet('');
$this->assertNoRaw('<script>alert("XSS label");</script>', 'The block title was properly sanitized when rendered.');
$this->drupalLogin($this->drupalCreateUser(['administer blocks', 'access administration pages']));
$default_theme = $this->config('system.theme')->get('default');
$this->drupalGet('admin/structure/block/list/' . $default_theme);
$this->assertNoRaw("<script>alert('XSS subject');</script>", 'The block title was properly sanitized in Block Plugin UI Admin page.');
}
/**
* Tests XSS in category.
*/
public function testXssInCategory() {
$this->container->get('module_installer')->install(['block_test']);
$this->drupalPlaceBlock('test_xss_title');
$this->drupalLogin($this->drupalCreateUser(['administer blocks', 'access administration pages']));
$this->drupalGet(Url::fromRoute('block.admin_display'));
$this->clickLink('Place block');
$this->assertNoRaw("<script>alert('XSS category');</script>");
}
/**
* Tests various modules that provide blocks for XSS.
*/
public function testBlockXss() {
$this->drupalLogin($this->rootUser);
$this->doViewTest();
$this->doMenuTest();
$this->doBlockContentTest();
$this->drupalGet(Url::fromRoute('block.admin_display'));
$this->clickLink('Place block');
$this->assertNoRaw('&amp;lt;', 'The page does not have double escaped HTML tags.');
}
/**
* Tests XSS coming from View block labels.
*/
protected function doViewTest() {
// Create a View without a custom label for its block Display. The
// admin_label of the block then becomes just the View's label.
$view = View::create([
'id' => $this->randomMachineName(),
'label' => '<script>alert("view1");</script>',
]);
$view->addDisplay('block');
$view->save();
// Create a View with a custom label for its block Display. The
// admin_label of the block then becomes the View's label combined with
// the Display's label.
$view = View::create([
'id' => $this->randomMachineName(),
'label' => '<script>alert("view2");</script>',
]);
$view->addDisplay('block', 'Fish & chips');
$view->save();
$this->drupalGet(Url::fromRoute('block.admin_display'));
$this->clickLink('Place block');
// \Drupal\views\Plugin\Derivative\ViewsBlock::getDerivativeDefinitions()
// has a different code path for an admin label based only on the View
// label versus one based on both the View label and the Display label.
// Ensure that this test is covering both code paths by asserting the
// absence of a ":" for the first View and the presence of a ":" for the
// second one. Note that the second assertion is redundant with the one
// further down which also checks for the Display label, but is included
// here for clarity.
$this->assertNoEscaped('<script>alert("view1");</script>:');
$this->assertEscaped('<script>alert("view2");</script>:');
// Assert that the blocks have their admin labels escaped and
// don't appear anywhere unescaped.
$this->assertEscaped('<script>alert("view1");</script>');
$this->assertNoRaw('<script>alert("view1");</script>');
$this->assertEscaped('<script>alert("view2");</script>: Fish & chips');
$this->assertNoRaw('<script>alert("view2");</script>');
$this->assertNoRaw('Fish & chips');
// Assert the Display label doesn't appear anywhere double escaped.
$this->assertNoRaw('Fish & chips');
$this->assertNoRaw('Fish &amp;amp; chips');
}
/**
* Tests XSS coming from Menu block labels.
*/
protected function doMenuTest() {
Menu::create([
'id' => $this->randomMachineName(),
'label' => '<script>alert("menu");</script>',
])->save();
$this->drupalGet(Url::fromRoute('block.admin_display'));
$this->clickLink('Place block');
$this->assertEscaped('<script>alert("menu");</script>');
$this->assertNoRaw('<script>alert("menu");</script>');
}
/**
* Tests XSS coming from Block Content block info.
*/
protected function doBlockContentTest() {
BlockContentType::create([
'id' => 'basic',
'label' => 'basic',
'revision' => TRUE,
])->save();
BlockContent::create([
'type' => 'basic',
'info' => '<script>alert("block_content");</script>',
])->save();
$this->drupalGet(Url::fromRoute('block.admin_display'));
$this->clickLink('Place block');
$this->assertEscaped('<script>alert("block_content");</script>');
$this->assertNoRaw('<script>alert("block_content");</script>');
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\block\Functional\Hal;
use Drupal\Tests\block\Functional\Rest\BlockResourceTestBase;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group hal
*/
class BlockHalJsonAnonTest extends BlockResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\block\Functional\Hal;
use Drupal\Tests\block\Functional\Rest\BlockResourceTestBase;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class BlockHalJsonBasicAuthTest extends BlockResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal', 'basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\block\Functional\Hal;
use Drupal\Tests\block\Functional\Rest\BlockResourceTestBase;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class BlockHalJsonCookieTest extends BlockResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,76 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests that the new default theme gets blocks.
*
* @group block
*/
class NewDefaultThemeBlocksTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block'];
/**
* Check the enabled Bartik blocks are correctly copied over.
*/
public function testNewDefaultThemeBlocks() {
$default_theme = $this->config('system.theme')->get('default');
// Add two instances of the user login block.
$this->drupalPlaceBlock('user_login_block', [
'id' => $default_theme . '_' . strtolower($this->randomMachineName(8)),
]);
$this->drupalPlaceBlock('user_login_block', [
'id' => $default_theme . '_' . strtolower($this->randomMachineName(8)),
]);
// Add an instance of a different block.
$this->drupalPlaceBlock('system_powered_by_block', [
'id' => $default_theme . '_' . strtolower($this->randomMachineName(8)),
]);
// Install a different theme.
$new_theme = 'bartik';
$this->assertFalse($new_theme == $default_theme, 'The new theme is different from the previous default theme.');
\Drupal::service('theme_handler')->install([$new_theme]);
$this->config('system.theme')
->set('default', $new_theme)
->save();
/** @var \Drupal\Core\Entity\EntityStorageInterface $block_storage */
$block_storage = $this->container->get('entity_type.manager')->getStorage('block');
// Ensure that the new theme has all the blocks as the previous default.
$default_block_names = $block_storage->getQuery()
->condition('theme', $default_theme)
->execute();
$new_blocks = $block_storage->getQuery()
->condition('theme', $new_theme)
->execute();
$this->assertTrue(count($default_block_names) == count($new_blocks), 'The new default theme has the same number of blocks as the previous theme.');
foreach ($default_block_names as $default_block_name) {
// Remove the matching block from the list of blocks in the new theme.
// E.g., if the old theme has block.block.stark_admin,
// unset block.block.bartik_admin.
unset($new_blocks[str_replace($default_theme . '_', $new_theme . '_', $default_block_name)]);
}
$this->assertTrue(empty($new_blocks), 'The new theme has exactly the same blocks as the previous default theme.');
// Install a hidden base theme and ensure blocks are not copied.
$base_theme = 'test_basetheme';
\Drupal::service('theme_handler')->install([$base_theme]);
$new_blocks = $block_storage->getQuery()
->condition('theme', $base_theme)
->execute();
$this->assertTrue(empty($new_blocks), 'Installing a hidden base theme does not copy blocks from the default theme.');
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Drupal\Tests\block\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the block administration page for a non-default theme.
*
* @group block
*/
class NonDefaultBlockAdminTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Test non-default theme admin.
*/
public function testNonDefaultBlockAdmin() {
$admin_user = $this->drupalCreateUser(['administer blocks', 'administer themes']);
$this->drupalLogin($admin_user);
$new_theme = 'bartik';
\Drupal::service('theme_handler')->install([$new_theme]);
$this->drupalGet('admin/structure/block/list/' . $new_theme);
$this->assertText('Bartik(' . t('active tab') . ')', 'Tab for non-default theme found.');
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\block\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class BlockJsonAnonTest extends BlockResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\block\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class BlockJsonBasicAuthTest extends BlockResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\block\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class BlockJsonCookieTest extends BlockResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,159 @@
<?php
namespace Drupal\Tests\block\Functional\Rest;
use Drupal\block\Entity\Block;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class BlockResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'block';
/**
* @var \Drupal\block\BlockInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->entity->setVisibilityConfig('user_role', [])->save();
break;
case 'POST':
$this->grantPermissionsToTestedRole(['administer blocks']);
break;
case 'PATCH':
$this->grantPermissionsToTestedRole(['administer blocks']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$block = Block::create([
'plugin' => 'llama_block',
'region' => 'header',
'id' => 'llama',
'theme' => 'classy',
]);
// All blocks can be viewed by the anonymous user by default. An interesting
// side effect of this is that any anonymous user is also able to read the
// corresponding block config entity via REST, even if an authentication
// provider is configured for the block config entity REST resource! In
// other words: Block entities do not distinguish between 'view' as in
// "render on a page" and 'view' as in "read the configuration".
// This prevents that.
// @todo Fix this in https://www.drupal.org/node/2820315.
$block->setVisibilityConfig('user_role', [
'id' => 'user_role',
'roles' => ['non-existing-role' => 'non-existing-role'],
'negate' => FALSE,
'context_mapping' => [
'user' => '@user.current_user_context:current_user',
],
]);
$block->save();
return $block;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$normalization = [
'uuid' => $this->entity->uuid(),
'id' => 'llama',
'weight' => NULL,
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [
'theme' => [
'classy',
],
],
'theme' => 'classy',
'region' => 'header',
'provider' => NULL,
'plugin' => 'llama_block',
'settings' => [
'id' => 'broken',
'label' => '',
'provider' => 'core',
'label_display' => 'visible',
],
'visibility' => [],
];
return $normalization;
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
// @see ::createEntity()
return ['url.site'];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheTags() {
// Because the 'user.permissions' cache context is missing, the cache tag
// for the anonymous user role is never added automatically.
return array_values(array_diff(parent::getExpectedCacheTags(), ['config:user.role.anonymous']));
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
switch ($method) {
case 'GET':
return "You are not authorized to view this block entity.";
default:
return parent::getExpectedUnauthorizedAccessMessage($method);
}
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessCacheability() {
// @see \Drupal\block\BlockAccessControlHandler::checkAccess()
return parent::getExpectedUnauthorizedAccessCacheability()
->setCacheTags([
'4xx-response',
'config:block.block.llama',
'http_response',
static::$auth ? 'user:2' : 'user:0',
])
->setCacheContexts(['user.roles']);
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace Drupal\Tests\block\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockXmlAnonTest extends BlockResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\block\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockXmlBasicAuthTest extends BlockResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\Tests\block\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class BlockXmlCookieTest extends BlockResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\block\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests the upgrade path for block with conditions missing context.
*
* @see https://www.drupal.org/node/2811519
*
* @group Update
* @group legacy
*/
class BlockConditionMissingSchemaUpdateTest extends UpdatePathTestBase {
/**
* This test does not have a failed update but the configuration has missing
* schema so can not do the full post update testing offered by
* UpdatePathTestBase.
*
* @var bool
*
* @see \Drupal\system\Tests\Update\UpdatePathTestBase::runUpdates()
*/
protected $checkFailedUpdates = FALSE;
/**
* {@inheritdoc}
*/
protected static $modules = ['block_test', 'language'];
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/drupal-8.block-test-enabled-missing-schema.php',
];
}
/**
* Tests that block context mapping is updated properly.
*/
public function testUpdateHookN() {
$this->runUpdates();
$this->drupalGet('<front>');
// If the block is fixed by block_post_update_fix_negate_in_conditions()
// then it will be visible.
$this->assertText('Test missing schema on conditions');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Drupal\Tests\block\Functional\Update;
/**
* Runs BlockContextMappingUpdateTest with a dump filled with content.
*
* @group Update
* @group legacy
*/
class BlockContextMappingUpdateFilledTest extends BlockContextMappingUpdateTest {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
parent::setDatabaseDumpFiles();
$this->databaseDumpFiles[0] = __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.filled.standard.php.gz';
}
}

View file

@ -0,0 +1,107 @@
<?php
namespace Drupal\Tests\block\Functional\Update;
use Drupal\block\Entity\Block;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\node\Entity\Node;
/**
* Tests the upgrade path for block context mapping renames.
*
* @see https://www.drupal.org/node/2354889
*
* @group Update
* @group legacy
*/
class BlockContextMappingUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['block_test', 'language'];
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php',
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.language-enabled.php',
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.block-test-enabled.php',
];
}
/**
* Tests that block context mapping is updated properly.
*/
public function testUpdateHookN() {
$this->runUpdates();
$this->assertRaw('Encountered an unknown context mapping key coming probably from a contributed or custom module: One or more mappings could not be updated. Please manually review your visibility settings for the following blocks, which are disabled now:<ul><li>User login (Visibility: Baloney spam)</li></ul>');
// Disable maintenance mode.
\Drupal::state()->set('system.maintenance_mode', FALSE);
// We finished updating so we can log in the user now.
$this->drupalLogin($this->rootUser);
// The block that we are testing has the following visibility rules:
// - only visible on node pages
// - only visible to authenticated users.
$block_title = 'Test for 2354889';
// Create two nodes, a page and an article.
$page = Node::create([
'type' => 'page',
'title' => 'Page node',
]);
$page->save();
$article = Node::create([
'type' => 'article',
'title' => 'Article node',
]);
$article->save();
// Check that the block appears only on Page nodes for authenticated users.
$this->drupalGet('node/' . $page->id());
$this->assertRaw($block_title, 'Test block is visible on a Page node as an authenticated user.');
$this->drupalGet('node/' . $article->id());
$this->assertNoRaw($block_title, 'Test block is not visible on a Article node as an authenticated user.');
$this->drupalLogout();
// Check that the block does not appear on any page for anonymous users.
$this->drupalGet('node/' . $page->id());
$this->assertNoRaw($block_title, 'Test block is not visible on a Page node as an anonymous user.');
$this->drupalGet('node/' . $article->id());
$this->assertNoRaw($block_title, 'Test block is not visible on a Article node as an anonymous user.');
// Ensure that all the context mappings got updated properly.
$block = Block::load('testfor2354889');
$visibility = $block->get('visibility');
$this->assertEqual('@node.node_route_context:node', $visibility['node_type']['context_mapping']['node']);
$this->assertEqual('@user.current_user_context:current_user', $visibility['user_role']['context_mapping']['user']);
$this->assertEqual('@language.current_language_context:language_interface', $visibility['language']['context_mapping']['language']);
// Check that a block with invalid context is being disabled and that it can
// still be edited afterward.
$disabled_block = Block::load('thirdtestfor2354889');
$this->assertFalse($disabled_block->status(), 'Block with invalid context is disabled');
$this->assertEqual(['thirdtestfor2354889' => ['missing_context_ids' => ['baloney_spam' => ['node_type']], 'status' => TRUE]], \Drupal::keyValue('update_backup')->get('block_update_8001'));
$disabled_block_visibility = $disabled_block->get('visibility');
$this->assertTrue(!isset($disabled_block_visibility['node_type']), 'The problematic visibility condition has been removed.');
$admin_user = $this->drupalCreateUser(['administer blocks']);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/structure/block/manage/thirdtestfor2354889');
$this->assertResponse('200');
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace Drupal\Tests\block\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests the upgrade path for removal of disabled region.
*
* @see https://www.drupal.org/node/2513534
*
* @group Update
* @group legacy
*/
class BlockRemoveDisabledRegionUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['block_test', 'language'];
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.update-test-block-disabled-2513534.php',
];
}
/**
* Tests that block context mapping is updated properly.
*/
public function testUpdateHookN() {
$this->runUpdates();
// Disable maintenance mode.
\Drupal::state()->set('system.maintenance_mode', FALSE);
// We finished updating so we can login the user now.
$this->drupalLogin($this->rootUser);
// Verify that a disabled block is in the default region.
$this->drupalGet('admin/structure/block');
$element = $this->xpath("//tr[contains(@data-drupal-selector, :block) and contains(@class, :status)]//select/option[@selected and @value=:region]",
[':block' => 'edit-blocks-pagetitle-1', ':status' => 'block-disabled', ':region' => 'header']);
$this->assertTrue(!empty($element));
// Verify that an enabled block is now disabled and in the default region.
$this->drupalGet('admin/structure/block');
$element = $this->xpath("//tr[contains(@data-drupal-selector, :block) and contains(@class, :status)]//select/option[@selected and @value=:region]",
[':block' => 'edit-blocks-pagetitle-2', ':status' => 'block-disabled', ':region' => 'header']);
$this->assertTrue(!empty($element));
}
}

View file

@ -0,0 +1,383 @@
<?php
namespace Drupal\Tests\block\Functional\Views;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
use Drupal\Tests\block\Functional\AssertBlockAppearsTrait;
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Entity\View;
use Drupal\views\Views;
use Drupal\views\Tests\ViewTestData;
use Drupal\Core\Template\Attribute;
/**
* Tests the block display plugin.
*
* @group block
* @see \Drupal\views\Plugin\views\display\Block
*/
class DisplayBlockTest extends ViewTestBase {
use AssertPageCacheContextsAndTagsTrait;
use AssertBlockAppearsTrait;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['node', 'block_test_views', 'test_page_test', 'contextual', 'views_ui'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_view_block', 'test_view_block2'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['block_test_views']);
$this->enableViewsTestModule();
}
/**
* Tests default and custom block categories.
*/
public function testBlockCategory() {
$this->drupalLogin($this->drupalCreateUser(['administer views', 'administer blocks']));
// Create a new view in the UI.
$edit = [];
$edit['label'] = $this->randomString();
$edit['id'] = strtolower($this->randomMachineName());
$edit['show[wizard_key]'] = 'standard:views_test_data';
$edit['description'] = $this->randomString();
$edit['block[create]'] = TRUE;
$edit['block[style][row_plugin]'] = 'fields';
$this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit'));
$pattern = '//tr[.//td[text()=:category] and .//td//a[contains(@href, :href)]]';
// Test that the block was given a default category corresponding to its
// base table.
$arguments = [
':href' => \Drupal::Url('block.admin_add', [
'plugin_id' => 'views_block:' . $edit['id'] . '-block_1',
'theme' => 'classy',
]),
':category' => 'Lists (Views)',
];
$this->drupalGet('admin/structure/block');
$this->clickLink('Place block');
$elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The test block appears in the category for its base table.');
// Duplicate the block before changing the category.
$this->drupalPostForm('admin/structure/views/view/' . $edit['id'] . '/edit/block_1', [], t('Duplicate @display_title', ['@display_title' => 'Block']));
$this->assertUrl('admin/structure/views/view/' . $edit['id'] . '/edit/block_2');
// Change the block category to a random string.
$this->drupalGet('admin/structure/views/view/' . $edit['id'] . '/edit/block_1');
$link = $this->xpath('//a[@id="views-block-1-block-category" and normalize-space(text())=:category]', $arguments);
$this->assertTrue(!empty($link));
$this->clickLink(t('Lists (Views)'));
$category = $this->randomString();
$this->drupalPostForm(NULL, ['block_category' => $category], t('Apply'));
// Duplicate the block after changing the category.
$this->drupalPostForm(NULL, [], t('Duplicate @display_title', ['@display_title' => 'Block']));
$this->assertUrl('admin/structure/views/view/' . $edit['id'] . '/edit/block_3');
$this->drupalPostForm(NULL, [], t('Save'));
// Test that the blocks are listed under the correct categories.
$arguments[':category'] = $category;
$this->drupalGet('admin/structure/block');
$this->clickLink('Place block');
$elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The test block appears in the custom category.');
$arguments = [
':href' => \Drupal::Url('block.admin_add', [
'plugin_id' => 'views_block:' . $edit['id'] . '-block_2',
'theme' => 'classy',
]),
':category' => 'Lists (Views)',
];
$elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The first duplicated test block remains in the original category.');
$arguments = [
':href' => \Drupal::Url('block.admin_add', [
'plugin_id' => 'views_block:' . $edit['id'] . '-block_3',
'theme' => 'classy',
]),
':category' => $category,
];
$elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The second duplicated test block appears in the custom category.');
}
/**
* Tests removing a block display.
*/
public function testDeleteBlockDisplay() {
// To test all combinations possible we first place create two instances
// of the block display of the first view.
$block_1 = $this->drupalPlaceBlock('views_block:test_view_block-block_1', ['label' => 'test_view_block-block_1:1']);
$block_2 = $this->drupalPlaceBlock('views_block:test_view_block-block_1', ['label' => 'test_view_block-block_1:2']);
// Then we add one instance of blocks for each of the two displays of the
// second view.
$block_3 = $this->drupalPlaceBlock('views_block:test_view_block2-block_1', ['label' => 'test_view_block2-block_1']);
$block_4 = $this->drupalPlaceBlock('views_block:test_view_block2-block_2', ['label' => 'test_view_block2-block_2']);
$this->drupalGet('test-page');
$this->assertBlockAppears($block_1);
$this->assertBlockAppears($block_2);
$this->assertBlockAppears($block_3);
$this->assertBlockAppears($block_4);
$block_storage = $this->container->get('entity_type.manager')->getStorage('block');
// Remove the block display, so both block entities from the first view
// should both disappear.
$view = Views::getView('test_view_block');
$view->initDisplay();
$view->displayHandlers->remove('block_1');
$view->storage->save();
$this->assertFalse($block_storage->load($block_1->id()), 'The block for this display was removed.');
$this->assertFalse($block_storage->load($block_2->id()), 'The block for this display was removed.');
$this->assertTrue($block_storage->load($block_3->id()), 'A block from another view was unaffected.');
$this->assertTrue($block_storage->load($block_4->id()), 'A block from another view was unaffected.');
$this->drupalGet('test-page');
$this->assertNoBlockAppears($block_1);
$this->assertNoBlockAppears($block_2);
$this->assertBlockAppears($block_3);
$this->assertBlockAppears($block_4);
// Remove the first block display of the second view and ensure the block
// instance of the second block display still exists.
$view = Views::getView('test_view_block2');
$view->initDisplay();
$view->displayHandlers->remove('block_1');
$view->storage->save();
$this->assertFalse($block_storage->load($block_3->id()), 'The block for this display was removed.');
$this->assertTrue($block_storage->load($block_4->id()), 'A block from another display on the same view was unaffected.');
$this->drupalGet('test-page');
$this->assertNoBlockAppears($block_3);
$this->assertBlockAppears($block_4);
}
/**
* Test the block form for a Views block.
*/
public function testViewsBlockForm() {
$this->drupalLogin($this->drupalCreateUser(['administer blocks']));
$default_theme = $this->config('system.theme')->get('default');
$this->drupalGet('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme);
$elements = $this->xpath('//input[@name="label"]');
$this->assertTrue(empty($elements), 'The label field is not found for Views blocks.');
// Test that that machine name field is hidden from display and has been
// saved as expected from the default value.
$this->assertNoFieldById('edit-machine-name', 'views_block__test_view_block_1', 'The machine name is hidden on the views block form.');
// Save the block.
$edit = ['region' => 'content'];
$this->drupalPostForm(NULL, $edit, t('Save block'));
$storage = $this->container->get('entity_type.manager')->getStorage('block');
$block = $storage->load('views_block__test_view_block_block_1');
// This will only return a result if our new block has been created with the
// expected machine name.
$this->assertTrue(!empty($block), 'The expected block was loaded.');
for ($i = 2; $i <= 3; $i++) {
// Place the same block again and make sure we have a new ID.
$this->drupalPostForm('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme, $edit, t('Save block'));
$block = $storage->load('views_block__test_view_block_block_1_' . $i);
// This will only return a result if our new block has been created with the
// expected machine name.
$this->assertTrue(!empty($block), 'The expected block was loaded.');
}
// Tests the override capability of items per page.
$this->drupalGet('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme);
$edit = ['region' => 'content'];
$edit['settings[override][items_per_page]'] = 10;
$this->drupalPostForm('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme, $edit, t('Save block'));
$block = $storage->load('views_block__test_view_block_block_1_4');
$config = $block->getPlugin()->getConfiguration();
$this->assertEqual(10, $config['items_per_page'], "'Items per page' is properly saved.");
$edit['settings[override][items_per_page]'] = 5;
$this->drupalPostForm('admin/structure/block/manage/views_block__test_view_block_block_1_4', $edit, t('Save block'));
$block = $storage->load('views_block__test_view_block_block_1_4');
$config = $block->getPlugin()->getConfiguration();
$this->assertEqual(5, $config['items_per_page'], "'Items per page' is properly saved.");
// Tests the override of the label capability.
$edit = ['region' => 'content'];
$edit['settings[views_label_checkbox]'] = 1;
$edit['settings[views_label]'] = 'Custom title';
$this->drupalPostForm('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme, $edit, t('Save block'));
$block = $storage->load('views_block__test_view_block_block_1_5');
$config = $block->getPlugin()->getConfiguration();
$this->assertEqual('Custom title', $config['views_label'], "'Label' is properly saved.");
}
/**
* Tests the actual rendering of the views block.
*/
public function testBlockRendering() {
// Create a block and set a custom title.
$block = $this->drupalPlaceBlock('views_block:test_view_block-block_1', ['label' => 'test_view_block-block_1:1', 'views_label' => 'Custom title']);
$this->drupalGet('');
$result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
$this->assertEqual($result[0]->getText(), 'Custom title');
// Don't override the title anymore.
$plugin = $block->getPlugin();
$plugin->setConfigurationValue('views_label', '');
$block->save();
$this->drupalGet('');
$result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
$this->assertEqual($result[0]->getText(), 'test_view_block');
// Hide the title.
$block->getPlugin()->setConfigurationValue('label_display', FALSE);
$block->save();
$this->drupalGet('');
$result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
$this->assertTrue(empty($result), 'The title is not visible.');
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:system.site', 'config:views.view.test_view_block', 'http_response', 'rendered']));
}
/**
* Tests the various testcases of empty block rendering.
*/
public function testBlockEmptyRendering() {
$url = new Url('test_page_test.test_page');
// Remove all views_test_data entries.
\Drupal::database()->truncate('views_test_data')->execute();
/** @var \Drupal\views\ViewEntityInterface $view */
$view = View::load('test_view_block');
$view->invalidateCaches();
$block = $this->drupalPlaceBlock('views_block:test_view_block-block_1', ['label' => 'test_view_block-block_1:1', 'views_label' => 'Custom title']);
$this->drupalGet('');
$this->assertEqual(1, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
$display = &$view->getDisplay('block_1');
$display['display_options']['block_hide_empty'] = TRUE;
$view->save();
$this->drupalGet($url);
$this->assertEqual(0, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
// Ensure that the view cacheability metadata is propagated even, for an
// empty block.
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block', 'http_response', 'rendered']));
$this->assertCacheContexts(['url.query_args:_wrapper_format']);
// Add a header displayed on empty result.
$display = &$view->getDisplay('block_1');
$display['display_options']['defaults']['header'] = FALSE;
$display['display_options']['header']['example'] = [
'field' => 'area_text_custom',
'id' => 'area_text_custom',
'table' => 'views',
'plugin_id' => 'text_custom',
'content' => 'test header',
'empty' => TRUE,
];
$view->save();
$this->drupalGet($url);
$this->assertEqual(1, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block', 'http_response', 'rendered']));
$this->assertCacheContexts(['url.query_args:_wrapper_format']);
// Hide the header on empty results.
$display = &$view->getDisplay('block_1');
$display['display_options']['defaults']['header'] = FALSE;
$display['display_options']['header']['example'] = [
'field' => 'area_text_custom',
'id' => 'area_text_custom',
'table' => 'views',
'plugin_id' => 'text_custom',
'content' => 'test header',
'empty' => FALSE,
];
$view->save();
$this->drupalGet($url);
$this->assertEqual(0, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block', 'http_response', 'rendered']));
$this->assertCacheContexts(['url.query_args:_wrapper_format']);
// Add an empty text.
$display = &$view->getDisplay('block_1');
$display['display_options']['defaults']['empty'] = FALSE;
$display['display_options']['empty']['example'] = [
'field' => 'area_text_custom',
'id' => 'area_text_custom',
'table' => 'views',
'plugin_id' => 'text_custom',
'content' => 'test empty',
];
$view->save();
$this->drupalGet($url);
$this->assertEqual(1, count($this->xpath('//div[contains(@class, "block-views-blocktest-view-block-block-1")]')));
$this->assertCacheTags(array_merge($block->getCacheTags(), ['block_view', 'config:block_list', 'config:views.view.test_view_block', 'http_response', 'rendered']));
$this->assertCacheContexts(['url.query_args:_wrapper_format']);
}
/**
* Tests the contextual links on a Views block.
*/
public function testBlockContextualLinks() {
$this->drupalLogin($this->drupalCreateUser(['administer views', 'access contextual links', 'administer blocks']));
$block = $this->drupalPlaceBlock('views_block:test_view_block-block_1');
$cached_block = $this->drupalPlaceBlock('views_block:test_view_block-block_1');
$this->drupalGet('test-page');
$id = 'block:block=' . $block->id() . ':langcode=en|entity.view.edit_form:view=test_view_block:location=block&name=test_view_block&display_id=block_1&langcode=en';
$id_token = Crypt::hmacBase64($id, Settings::getHashSalt() . $this->container->get('private_key')->get());
$cached_id = 'block:block=' . $cached_block->id() . ':langcode=en|entity.view.edit_form:view=test_view_block:location=block&name=test_view_block&display_id=block_1&langcode=en';
$cached_id_token = Crypt::hmacBase64($cached_id, Settings::getHashSalt() . $this->container->get('private_key')->get());
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder()
$this->assertRaw('<div' . new Attribute(['data-contextual-id' => $id, 'data-contextual-token' => $id_token]) . '></div>', format_string('Contextual link placeholder with id @id exists.', ['@id' => $id]));
$this->assertRaw('<div' . new Attribute(['data-contextual-id' => $cached_id, 'data-contextual-token' => $cached_id_token]) . '></div>', format_string('Contextual link placeholder with id @id exists.', ['@id' => $cached_id]));
// Get server-rendered contextual links.
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:renderContextualLinks()
$post = ['ids[0]' => $id, 'ids[1]' => $cached_id, 'tokens[0]' => $id_token, 'tokens[1]' => $cached_id_token];
$url = 'contextual/render?_format=json,destination=test-page';
$this->getSession()->getDriver()->getClient()->request('POST', $url, $post);
$this->assertResponse(200);
$json = Json::decode($this->getSession()->getPage()->getContent());
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '">Configure block</a></li><li class="entityviewedit-form"><a href="' . base_path() . 'admin/structure/views/view/test_view_block/edit/block_1">Edit view</a></li></ul>');
$this->assertIdentical($json[$cached_id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $cached_block->id() . '">Configure block</a></li><li class="entityviewedit-form"><a href="' . base_path() . 'admin/structure/views/view/test_view_block/edit/block_1">Edit view</a></li></ul>');
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace Drupal\Tests\block\FunctionalJavascript;
use Behat\Mink\Element\NodeElement;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests the JavaScript functionality of the block add filter.
*
* @group block
*/
class BlockFilterTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['user', 'block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$admin_user = $this->drupalCreateUser([
'administer blocks',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests block filter.
*/
public function testBlockFilter() {
$this->drupalGet('admin/structure/block');
$assertSession = $this->assertSession();
$session = $this->getSession();
$page = $session->getPage();
// Find the block filter field on the add-block dialog.
$page->find('css', '#edit-blocks-region-header-title')->click();
$filter = $assertSession->waitForElement('css', '.block-filter-text');
// Get all block rows, for assertions later.
$block_rows = $page->findAll('css', '.block-add-table tbody tr');
// Test block filter reduces the number of visible rows.
$filter->setValue('ad');
$session->wait(10000, 'jQuery("#drupal-live-announce").html().indexOf("blocks are available") > -1');
$visible_rows = $this->filterVisibleElements($block_rows);
if (count($block_rows) > 0) {
$this->assertNotEquals(count($block_rows), count($visible_rows));
}
// Test Drupal.announce() message when multiple matches are expected.
$expected_message = count($visible_rows) . ' blocks are available in the modified list.';
$assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
// Test Drupal.announce() message when only one match is expected.
$filter->setValue('Powered by');
$session->wait(10000, 'jQuery("#drupal-live-announce").html().indexOf("block is available") > -1');
$visible_rows = $this->filterVisibleElements($block_rows);
$this->assertEquals(1, count($visible_rows));
$expected_message = '1 block is available in the modified list.';
$assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
// Test Drupal.announce() message when no matches are expected.
$filter->setValue('Pan-Galactic Gargle Blaster');
$session->wait(10000, 'jQuery("#drupal-live-announce").html().indexOf("0 blocks are available") > -1');
$visible_rows = $this->filterVisibleElements($block_rows);
$this->assertEquals(0, count($visible_rows));
$expected_message = '0 blocks are available in the modified list.';
$assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
}
/**
* Removes any non-visible elements from the passed array.
*
* @param \Behat\Mink\Element\NodeElement[] $elements
* An array of node elements.
*
* @return \Behat\Mink\Element\NodeElement[]
*/
protected function filterVisibleElements(array $elements) {
$elements = array_filter($elements, function (NodeElement $element) {
return $element->isVisible();
});
return $elements;
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace Drupal\Tests\block\Kernel;
use Drupal\block\Entity\Block;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the block config schema.
*
* @group block
*/
class BlockConfigSchemaTest extends KernelTestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'block',
'aggregator',
'book',
'block_content',
'comment',
'forum',
'node',
'statistics',
// BlockManager->getModuleName() calls system_get_info().
'system',
'taxonomy',
'user',
'text',
];
/**
* The typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfig;
/**
* The block manager.
*
* @var \Drupal\Core\Block\BlockManagerInterface
*/
protected $blockManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->typedConfig = \Drupal::service('config.typed');
$this->blockManager = \Drupal::service('plugin.manager.block');
$this->installEntitySchema('block_content');
$this->installEntitySchema('taxonomy_term');
$this->installEntitySchema('node');
$this->installSchema('book', ['book']);
}
/**
* Tests the block config schema for block plugins.
*/
public function testBlockConfigSchema() {
foreach ($this->blockManager->getDefinitions() as $block_id => $definition) {
$id = strtolower($this->randomMachineName());
$block = Block::create([
'id' => $id,
'theme' => 'classy',
'weight' => 00,
'status' => TRUE,
'region' => 'content',
'plugin' => $block_id,
'settings' => [
'label' => $this->randomMachineName(),
'provider' => 'system',
'label_display' => FALSE,
],
'visibility' => [],
]);
$block->save();
$config = $this->config("block.block.$id");
$this->assertEqual($config->get('id'), $id);
$this->assertConfigSchema($this->typedConfig, $config->getName(), $config->get());
}
}
}

View file

@ -0,0 +1,103 @@
<?php
namespace Drupal\Tests\block\Kernel;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Form\FormState;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests that the block plugin can work properly without a supporting entity.
*
* @group block
*/
class BlockInterfaceTest extends KernelTestBase {
public static $modules = ['system', 'block', 'block_test', 'user'];
/**
* Test configuration and subsequent form() and build() method calls.
*
* This test is attempting to test the existing block plugin api and all
* functionality that is expected to remain consistent. The arrays that are
* used for comparison can change, but only to include elements that are
* contained within BlockBase or the plugin being tested. Likely these
* comparison arrays should get smaller, not larger, as more form/build
* elements are moved into a more suitably responsible class.
*
* Instantiation of the plugin is the primary element being tested here. The
* subsequent method calls are just attempting to cause a failure if a
* dependency outside of the plugin configuration is required.
*/
public function testBlockInterface() {
$manager = $this->container->get('plugin.manager.block');
$configuration = [
'label' => 'Custom Display Message',
];
$expected_configuration = [
'id' => 'test_block_instantiation',
'label' => 'Custom Display Message',
'provider' => 'block_test',
'label_display' => BlockPluginInterface::BLOCK_LABEL_VISIBLE,
'display_message' => 'no message set',
];
// Initial configuration of the block at construction time.
/** @var $display_block \Drupal\Core\Block\BlockPluginInterface */
$display_block = $manager->createInstance('test_block_instantiation', $configuration);
$this->assertIdentical($display_block->getConfiguration(), $expected_configuration, 'The block was configured correctly.');
// Updating an element of the configuration.
$display_block->setConfigurationValue('display_message', 'My custom display message.');
$expected_configuration['display_message'] = 'My custom display message.';
$this->assertIdentical($display_block->getConfiguration(), $expected_configuration, 'The block configuration was updated correctly.');
$definition = $display_block->getPluginDefinition();
$expected_form = [
'provider' => [
'#type' => 'value',
'#value' => 'block_test',
],
'admin_label' => [
'#type' => 'item',
'#title' => t('Block description'),
'#plain_text' => $definition['admin_label'],
],
'label' => [
'#type' => 'textfield',
'#title' => 'Title',
'#maxlength' => 255,
'#default_value' => 'Custom Display Message',
'#required' => TRUE,
],
'label_display' => [
'#type' => 'checkbox',
'#title' => 'Display title',
'#default_value' => TRUE,
'#return_value' => 'visible',
],
'context_mapping' => [],
'display_message' => [
'#type' => 'textfield',
'#title' => t('Display message'),
'#default_value' => 'My custom display message.',
],
];
$form_state = new FormState();
// Ensure there are no form elements that do not belong to the plugin.
$actual_form = $display_block->buildConfigurationForm([], $form_state);
// Remove the visibility sections, as that just tests condition plugins.
unset($actual_form['visibility'], $actual_form['visibility_tabs']);
$this->assertIdentical($this->castSafeStrings($actual_form), $this->castSafeStrings($expected_form), 'Only the expected form elements were present.');
$expected_build = [
'#children' => 'My custom display message.',
];
// Ensure the build array is proper.
$this->assertIdentical($display_block->build(), $expected_build, 'The plugin returned the appropriate build array.');
// Ensure the machine name suggestion is correct. In truth, this is actually
// testing BlockBase's implementation, not the interface itself.
$this->assertIdentical($display_block->getMachineNameSuggestion(), 'displaymessage', 'The plugin returned the expected machine name suggestion.');
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace Drupal\Tests\block\Kernel;
use Drupal\block\Entity\Block;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\KernelTests\KernelTestBase;
use Drupal\simpletest\BlockCreationTrait;
/**
* Tests block_rebuild().
*
* @group block
*/
class BlockRebuildTest extends KernelTestBase {
use BlockCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['block', 'system'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container->get('theme_installer')->install(['stable', 'classy']);
$this->container->get('config.factory')->getEditable('system.theme')->set('default', 'classy')->save();
}
/**
* {@inheritdoc}
*/
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
// @todo Once block_rebuild() is refactored to auto-loadable code, remove
// this require statement.
require_once static::getDrupalRoot() . '/core/modules/block/block.module';
}
/**
* @covers ::block_rebuild
*/
public function testRebuildNoBlocks() {
block_rebuild();
$messages = \Drupal::messenger()->all();
\Drupal::messenger()->deleteAll();
$this->assertEquals([], $messages);
}
/**
* @covers ::block_rebuild
*/
public function testRebuildNoInvalidBlocks() {
$this->placeBlock('system_powered_by_block', ['region' => 'content']);
block_rebuild();
$messages = \Drupal::messenger()->all();
\Drupal::messenger()->deleteAll();
$this->assertEquals([], $messages);
}
/**
* @covers ::block_rebuild
*/
public function testRebuildInvalidBlocks() {
$this->placeBlock('system_powered_by_block', ['region' => 'content']);
$block1 = $this->placeBlock('system_powered_by_block');
$block2 = $this->placeBlock('system_powered_by_block');
$block2->disable()->save();
// Use the config API directly to bypass Block::preSave().
\Drupal::configFactory()->getEditable('block.block.' . $block1->id())->set('region', 'INVALID')->save();
\Drupal::configFactory()->getEditable('block.block.' . $block2->id())->set('region', 'INVALID')->save();
// Reload block entities.
$block1 = Block::load($block1->id());
$block2 = Block::load($block2->id());
$this->assertSame('INVALID', $block1->getRegion());
$this->assertTrue($block1->status());
$this->assertSame('INVALID', $block2->getRegion());
$this->assertFalse($block2->status());
block_rebuild();
// Reload block entities.
$block1 = Block::load($block1->id());
$block2 = Block::load($block2->id());
$messages = \Drupal::messenger()->all();
\Drupal::messenger()->deleteAll();
$expected = ['warning' => [new TranslatableMarkup('The block %info was assigned to the invalid region %region and has been disabled.', ['%info' => $block1->id(), '%region' => 'INVALID'])]];
$this->assertEquals($expected, $messages);
$default_region = system_default_region('classy');
$this->assertSame($default_region, $block1->getRegion());
$this->assertFalse($block1->status());
$this->assertSame($default_region, $block2->getRegion());
$this->assertFalse($block2->status());
}
}

View file

@ -0,0 +1,158 @@
<?php
namespace Drupal\Tests\block\Kernel;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\KernelTests\KernelTestBase;
use Drupal\block_test\Plugin\Block\TestHtmlBlock;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\block\Entity\Block;
/**
* Tests the storage of blocks.
*
* @group block
*/
class BlockStorageUnitTest extends KernelTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_test', 'system'];
/**
* The block storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
*/
protected $controller;
protected function setUp() {
parent::setUp();
$this->controller = $this->container->get('entity_type.manager')->getStorage('block');
$this->container->get('theme_installer')->install(['stark']);
}
/**
* Tests CRUD operations.
*/
public function testBlockCRUD() {
$this->assertTrue($this->controller instanceof ConfigEntityStorage, 'The block storage is loaded.');
// Run each test method in the same installation.
$this->createTests();
$this->loadTests();
$this->deleteTests();
}
/**
* Tests the creation of blocks.
*/
protected function createTests() {
// Attempt to create a block without a plugin.
try {
$entity = $this->controller->create([]);
$entity->getPlugin();
$this->fail('A block without a plugin was created with no exception thrown.');
}
catch (PluginException $e) {
$this->assertEqual('The block \'\' did not specify a plugin.', $e->getMessage(), 'An exception was thrown when a block was created without a plugin.');
}
// Create a block with only required values.
$entity = $this->controller->create([
'id' => 'test_block',
'theme' => 'stark',
'region' => 'content',
'plugin' => 'test_html',
]);
$entity->save();
$this->assertTrue($entity instanceof Block, 'The newly created entity is a Block.');
// Verify all of the block properties.
$actual_properties = $this->config('block.block.test_block')->get();
$this->assertTrue(!empty($actual_properties['uuid']), 'The block UUID is set.');
unset($actual_properties['uuid']);
// Ensure that default values are filled in.
$expected_properties = [
'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
'status' => TRUE,
'dependencies' => ['module' => ['block_test'], 'theme' => ['stark']],
'id' => 'test_block',
'theme' => 'stark',
'region' => 'content',
'weight' => NULL,
'provider' => NULL,
'plugin' => 'test_html',
'settings' => [
'id' => 'test_html',
'label' => '',
'provider' => 'block_test',
'label_display' => BlockPluginInterface::BLOCK_LABEL_VISIBLE,
],
'visibility' => [],
];
$this->assertIdentical($actual_properties, $expected_properties);
$this->assertTrue($entity->getPlugin() instanceof TestHtmlBlock, 'The entity has an instance of the correct block plugin.');
}
/**
* Tests the loading of blocks.
*/
protected function loadTests() {
$entity = $this->controller->load('test_block');
$this->assertTrue($entity instanceof Block, 'The loaded entity is a Block.');
// Verify several properties of the block.
$this->assertSame('content', $entity->getRegion());
$this->assertTrue($entity->status());
$this->assertEqual($entity->getTheme(), 'stark');
$this->assertTrue($entity->uuid());
}
/**
* Tests the deleting of blocks.
*/
protected function deleteTests() {
$entity = $this->controller->load('test_block');
// Ensure that the storage isn't currently empty.
$config_storage = $this->container->get('config.storage');
$config = $config_storage->listAll('block.block.');
$this->assertFalse(empty($config), 'There are blocks in config storage.');
// Delete the block.
$entity->delete();
// Ensure that the storage is now empty.
$config = $config_storage->listAll('block.block.');
$this->assertTrue(empty($config), 'There are no blocks in config storage.');
}
/**
* Tests the installation of default blocks.
*/
public function testDefaultBlocks() {
\Drupal::service('theme_handler')->install(['classy']);
$entities = $this->controller->loadMultiple();
$this->assertTrue(empty($entities), 'There are no blocks initially.');
// Install the block_test.module, so that its default config is installed.
$this->installConfig(['block_test']);
$entities = $this->controller->loadMultiple();
$entity = reset($entities);
$this->assertEqual($entity->id(), 'test_block', 'The default test block was loaded.');
}
}

View file

@ -0,0 +1,328 @@
<?php
namespace Drupal\Tests\block\Kernel;
use Drupal\Component\Utility\Html;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Language\LanguageInterface;
use Drupal\KernelTests\KernelTestBase;
use Drupal\block\Entity\Block;
/**
* Tests the block view builder.
*
* @group block
*/
class BlockViewBuilderTest extends KernelTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'block_test', 'system', 'user'];
/**
* The block being tested.
*
* @var \Drupal\block\Entity\BlockInterface
*/
protected $block;
/**
* The block storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
*/
protected $controller;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->controller = $this->container
->get('entity_type.manager')
->getStorage('block');
\Drupal::state()->set('block_test.content', 'Llamas &gt; unicorns!');
// Create a block with only required values.
$this->block = $this->controller->create([
'id' => 'test_block',
'theme' => 'stark',
'plugin' => 'test_cache',
]);
$this->block->save();
$this->container->get('cache.render')->deleteAll();
$this->renderer = $this->container->get('renderer');
}
/**
* Tests the rendering of blocks.
*/
public function testBasicRendering() {
\Drupal::state()->set('block_test.content', '');
$entity = $this->controller->create([
'id' => 'test_block1',
'theme' => 'stark',
'plugin' => 'test_html',
]);
$entity->save();
// Test the rendering of a block.
$entity = Block::load('test_block1');
$output = entity_view($entity, 'block');
$expected = [];
$expected[] = '<div id="block-test-block1">';
$expected[] = ' ';
$expected[] = ' ';
$expected[] = ' ';
$expected[] = ' </div>';
$expected[] = '';
$expected_output = implode("\n", $expected);
$this->assertEqual($this->renderer->renderRoot($output), $expected_output);
// Reset the HTML IDs so that the next render is not affected.
Html::resetSeenIds();
// Test the rendering of a block with a given title.
$entity = $this->controller->create([
'id' => 'test_block2',
'theme' => 'stark',
'plugin' => 'test_html',
'settings' => [
'label' => 'Powered by Bananas',
],
]);
$entity->save();
$output = entity_view($entity, 'block');
$expected = [];
$expected[] = '<div id="block-test-block2">';
$expected[] = ' ';
$expected[] = ' <h2>Powered by Bananas</h2>';
$expected[] = ' ';
$expected[] = ' ';
$expected[] = ' </div>';
$expected[] = '';
$expected_output = implode("\n", $expected);
$this->assertEqual($this->renderer->renderRoot($output), $expected_output);
}
/**
* Tests block render cache handling.
*/
public function testBlockViewBuilderCache() {
// Verify cache handling for a non-empty block.
$this->verifyRenderCacheHandling();
// Create an empty block.
$this->block = $this->controller->create([
'id' => 'test_block',
'theme' => 'stark',
'plugin' => 'test_cache',
]);
$this->block->save();
\Drupal::state()->set('block_test.content', NULL);
// Verify cache handling for an empty block.
$this->verifyRenderCacheHandling();
}
/**
* Verifies render cache handling of the block being tested.
*
* @see ::testBlockViewBuilderCache()
*/
protected function verifyRenderCacheHandling() {
// Force a request via GET so we can test the render cache.
$request = \Drupal::request();
$request_method = $request->server->get('REQUEST_METHOD');
$request->setMethod('GET');
// Test that a cache entry is created.
$build = $this->getBlockRenderArray();
$cid = 'entity_view:block:test_block:' . implode(':', \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
$this->renderer->renderRoot($build);
$this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
// Re-save the block and check that the cache entry has been deleted.
$this->block->save();
$this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was saved.');
// Rebuild the render array (creating a new cache entry in the process) and
// delete the block to check the cache entry is deleted.
unset($build['#printed']);
// Re-add the block because \Drupal\block\BlockViewBuilder::buildBlock()
// removes it.
$build['#block'] = $this->block;
$this->renderer->renderRoot($build);
$this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
$this->block->delete();
$this->assertFalse($this->container->get('cache.render')->get($cid), 'The block render cache entry has been cleared when the block was deleted.');
// Restore the previous request method.
$request->setMethod($request_method);
}
/**
* Tests block view altering.
*
* @see hook_block_view_alter()
* @see hook_block_view_BASE_BLOCK_ID_alter()
*/
public function testBlockViewBuilderViewAlter() {
// Establish baseline.
$build = $this->getBlockRenderArray();
$this->setRawContent((string) $this->renderer->renderRoot($build));
$this->assertIdentical(trim((string) $this->cssSelect('div')[0]), 'Llamas > unicorns!');
// Enable the block view alter hook that adds a foo=bar attribute.
\Drupal::state()->set('block_test_view_alter_suffix', TRUE);
Cache::invalidateTags($this->block->getCacheTagsToInvalidate());
$build = $this->getBlockRenderArray();
$this->setRawContent((string) $this->renderer->renderRoot($build));
$this->assertIdentical(trim((string) $this->cssSelect('[foo=bar]')[0]), 'Llamas > unicorns!');
\Drupal::state()->set('block_test_view_alter_suffix', FALSE);
\Drupal::state()->set('block_test.content', NULL);
Cache::invalidateTags($this->block->getCacheTagsToInvalidate());
// Advanced: cached block, but an alter hook adds a #pre_render callback to
// alter the eventual content.
\Drupal::state()->set('block_test_view_alter_append_pre_render_prefix', TRUE);
$build = $this->getBlockRenderArray();
$this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before rendering.');
$this->assertIdentical((string) $this->renderer->renderRoot($build), 'Hiya!<br>');
$this->assertTrue(isset($build['#prefix']) && $build['#prefix'] === 'Hiya!<br>', 'A cached block without content is altered.');
}
/**
* Tests block build altering.
*
* @see hook_block_build_alter()
* @see hook_block_build_BASE_BLOCK_ID_alter()
*/
public function testBlockViewBuilderBuildAlter() {
// Force a request via GET so we can test the render cache.
$request = \Drupal::request();
$request_method = $request->server->get('REQUEST_METHOD');
$request->setMethod('GET');
$default_keys = ['entity_view', 'block', 'test_block'];
$default_contexts = [];
$default_tags = ['block_view', 'config:block.block.test_block'];
$default_max_age = Cache::PERMANENT;
// hook_block_build_alter() adds an additional cache key.
$alter_add_key = $this->randomMachineName();
\Drupal::state()->set('block_test_block_alter_cache_key', $alter_add_key);
$this->assertBlockRenderedWithExpectedCacheability(array_merge($default_keys, [$alter_add_key]), $default_contexts, $default_tags, $default_max_age);
\Drupal::state()->set('block_test_block_alter_cache_key', NULL);
// hook_block_build_alter() adds an additional cache context.
$alter_add_context = 'url.query_args:' . $this->randomMachineName();
\Drupal::state()->set('block_test_block_alter_cache_context', $alter_add_context);
$this->assertBlockRenderedWithExpectedCacheability($default_keys, Cache::mergeContexts($default_contexts, [$alter_add_context]), $default_tags, $default_max_age);
\Drupal::state()->set('block_test_block_alter_cache_context', NULL);
// hook_block_build_alter() adds an additional cache tag.
$alter_add_tag = $this->randomMachineName();
\Drupal::state()->set('block_test_block_alter_cache_tag', $alter_add_tag);
$this->assertBlockRenderedWithExpectedCacheability($default_keys, $default_contexts, Cache::mergeTags($default_tags, [$alter_add_tag]), $default_max_age);
\Drupal::state()->set('block_test_block_alter_cache_tag', NULL);
// hook_block_build_alter() alters the max-age.
$alter_max_age = 300;
\Drupal::state()->set('block_test_block_alter_cache_max_age', $alter_max_age);
$this->assertBlockRenderedWithExpectedCacheability($default_keys, $default_contexts, $default_tags, $alter_max_age);
\Drupal::state()->set('block_test_block_alter_cache_max_age', NULL);
// hook_block_build_alter() alters cache keys, contexts, tags and max-age.
\Drupal::state()->set('block_test_block_alter_cache_key', $alter_add_key);
\Drupal::state()->set('block_test_block_alter_cache_context', $alter_add_context);
\Drupal::state()->set('block_test_block_alter_cache_tag', $alter_add_tag);
\Drupal::state()->set('block_test_block_alter_cache_max_age', $alter_max_age);
$this->assertBlockRenderedWithExpectedCacheability(array_merge($default_keys, [$alter_add_key]), Cache::mergeContexts($default_contexts, [$alter_add_context]), Cache::mergeTags($default_tags, [$alter_add_tag]), $alter_max_age);
\Drupal::state()->set('block_test_block_alter_cache_key', NULL);
\Drupal::state()->set('block_test_block_alter_cache_context', NULL);
\Drupal::state()->set('block_test_block_alter_cache_tag', NULL);
\Drupal::state()->set('block_test_block_alter_cache_max_age', NULL);
// hook_block_build_alter() sets #create_placeholder.
foreach ([TRUE, FALSE] as $value) {
\Drupal::state()->set('block_test_block_alter_create_placeholder', $value);
$build = $this->getBlockRenderArray();
$this->assertTrue(isset($build['#create_placeholder']));
$this->assertIdentical($value, $build['#create_placeholder']);
}
\Drupal::state()->set('block_test_block_alter_create_placeholder', NULL);
// Restore the previous request method.
$request->setMethod($request_method);
}
/**
* Asserts that a block is built/rendered/cached with expected cacheability.
*
* @param string[] $expected_keys
* The expected cache keys.
* @param string[] $expected_contexts
* The expected cache contexts.
* @param string[] $expected_tags
* The expected cache tags.
* @param int $expected_max_age
* The expected max-age.
*/
protected function assertBlockRenderedWithExpectedCacheability(array $expected_keys, array $expected_contexts, array $expected_tags, $expected_max_age) {
$required_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
// Check that the expected cacheability metadata is present in:
// - the built render array;
$this->pass('Built render array');
$build = $this->getBlockRenderArray();
$this->assertIdentical($expected_keys, $build['#cache']['keys']);
$this->assertIdentical($expected_contexts, $build['#cache']['contexts']);
$this->assertIdentical($expected_tags, $build['#cache']['tags']);
$this->assertIdentical($expected_max_age, $build['#cache']['max-age']);
$this->assertFalse(isset($build['#create_placeholder']));
// - the rendered render array;
$this->pass('Rendered render array');
$this->renderer->renderRoot($build);
// - the render cache item.
$this->pass('Render cache item');
$final_cache_contexts = Cache::mergeContexts($expected_contexts, $required_cache_contexts);
$cid = implode(':', $expected_keys) . ':' . implode(':', \Drupal::service('cache_contexts_manager')->convertTokensToKeys($final_cache_contexts)->getKeys());
$cache_item = $this->container->get('cache.render')->get($cid);
$this->assertTrue($cache_item, 'The block render element has been cached with the expected cache ID.');
$this->assertIdentical(Cache::mergeTags($expected_tags, ['rendered']), $cache_item->tags);
$this->assertIdentical($final_cache_contexts, $cache_item->data['#cache']['contexts']);
$this->assertIdentical($expected_tags, $cache_item->data['#cache']['tags']);
$this->assertIdentical($expected_max_age, $cache_item->data['#cache']['max-age']);
$this->container->get('cache.render')->delete($cid);
}
/**
* Get a fully built render array for a block.
*
* @return array
* The render array.
*/
protected function getBlockRenderArray() {
return $this->container->get('entity_type.manager')->getViewBuilder('block')->view($this->block, 'block');
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace Drupal\Tests\block\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Tests migration of i18n block translations.
*
* @group migrate_drupal_6
*/
class MigrateBlockContentTranslationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'aggregator',
'book',
'block',
'comment',
'forum',
'views',
'block_content',
'content_translation',
'language',
'statistics',
'taxonomy',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['block']);
$this->installConfig(['block_content']);
$this->installEntitySchema('block_content');
$this->executeMigrations([
'd6_filter_format',
'block_content_type',
'block_content_body_field',
'd6_custom_block',
'd6_user_role',
'd6_block',
'd6_block_translation',
]);
block_rebuild();
}
/**
* Tests the migration of block title translation.
*/
public function testBlockContentTranslation() {
/** @var \Drupal\language\ConfigurableLanguageManagerInterface $language_manager */
$language_manager = $this->container->get('language_manager');
$config = $language_manager->getLanguageConfigOverride('zu', 'block.block.user_1');
$this->assertSame('zu - Navigation', $config->get('settings.label'));
}
}

View file

@ -0,0 +1,307 @@
<?php
namespace Drupal\Tests\block\Kernel\Migrate\d6;
use Drupal\block\Entity\Block;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Tests migration of blocks to configuration entities.
*
* @group migrate_drupal_6
*/
class MigrateBlockTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'block',
'views',
'comment',
'menu_ui',
'block_content',
'taxonomy',
'node',
'aggregator',
'book',
'forum',
'statistics',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Install the themes used for this test.
$this->container->get('theme_installer')->install(['bartik', 'test_theme']);
$this->installConfig(['block_content']);
$this->installEntitySchema('block_content');
// Set Bartik as the default public theme.
$config = $this->config('system.theme');
$config->set('default', 'bartik');
$config->save();
$this->executeMigrations([
'd6_filter_format',
'block_content_type',
'block_content_body_field',
'd6_custom_block',
'd6_user_role',
'd6_block',
]);
block_rebuild();
}
/**
* Asserts various aspects of a block.
*
* @param string $id
* The block ID.
* @param array $visibility
* The block visibility settings.
* @param string $region
* The display region.
* @param string $theme
* The theme.
* @param string $weight
* The block weight.
* @param array $settings
* (optional) The block settings.
* @param bool $status
* Whether the block is expected to be enabled or disabled.
*/
public function assertEntity($id, $visibility, $region, $theme, $weight, array $settings = NULL, $status = TRUE) {
$block = Block::load($id);
$this->assertTrue($block instanceof Block);
$this->assertSame($visibility, $block->getVisibility());
$this->assertSame($region, $block->getRegion());
$this->assertSame($theme, $block->getTheme());
$this->assertSame($weight, $block->getWeight());
$this->assertSame($status, $block->status());
if ($settings) {
$block_settings = $block->get('settings');
$block_settings['id'] = current(explode(':', $block_settings['id']));
$this->assertEquals($settings, $block_settings);
}
}
/**
* Tests the block migration.
*/
public function testBlockMigration() {
$blocks = Block::loadMultiple();
$this->assertCount(14, $blocks);
// Check user blocks.
$visibility = [
'request_path' => [
'id' => 'request_path',
'negate' => TRUE,
'pages' => "<front>\n/node/1\n/blog/*",
],
];
$settings = [
'id' => 'user_login_block',
'label' => '',
'provider' => 'user',
'label_display' => '0',
];
$this->assertEntity('user', $visibility, 'sidebar_first', 'bartik', -10, $settings);
$visibility = [];
$settings = [
'id' => 'system_menu_block',
'label' => 'zu - Navigation',
'provider' => 'system',
'label_display' => 'visible',
'level' => 1,
'depth' => 0,
];
$this->assertEntity('user_1', $visibility, 'sidebar_first', 'bartik', -11, $settings);
$visibility = [
'user_role' => [
'id' => 'user_role',
'roles' => [
'authenticated' => 'authenticated',
],
'context_mapping' => [
'user' => '@user.current_user_context:current_user',
],
'negate' => FALSE,
],
];
$settings = [
'id' => 'broken',
'label' => '',
'provider' => 'core',
'label_display' => '0',
'items_per_page' => '5',
];
$this->assertEntity('user_2', $visibility, 'sidebar_second', 'bartik', -11, $settings);
$visibility = [
'user_role' => [
'id' => 'user_role',
'roles' => [
'migrate_test_role_1' => 'migrate_test_role_1',
],
'context_mapping' => [
'user' => '@user.current_user_context:current_user',
],
'negate' => FALSE,
],
];
$settings = [
'id' => 'broken',
'label' => '',
'provider' => 'core',
'label_display' => '0',
'items_per_page' => '10',
];
$this->assertEntity('user_3', $visibility, 'sidebar_second', 'bartik', -10, $settings);
// Check system block.
$visibility = [
'request_path' => [
'id' => 'request_path',
'negate' => TRUE,
'pages' => '/node/1',
],
];
$settings = [
'id' => 'system_powered_by_block',
'label' => '',
'provider' => 'system',
'label_display' => '0',
];
$this->assertEntity('system', $visibility, 'footer_fifth', 'bartik', -5, $settings);
// Check menu blocks.
$settings = [
'id' => 'broken',
'label' => '',
'provider' => 'core',
'label_display' => '0',
];
$this->assertEntity('menu', [], 'header', 'bartik', -5, $settings);
// Check aggregator block.
$settings = [
'id' => 'aggregator_feed_block',
'label' => '',
'provider' => 'aggregator',
'label_display' => '0',
'block_count' => 7,
'feed' => '5',
];
$this->assertEntity('aggregator', [], 'sidebar_second', 'bartik', -2, $settings);
// Check book block.
$settings = [
'id' => 'book_navigation',
'label' => '',
'provider' => 'book',
'label_display' => '0',
'block_mode' => 'book pages',
];
$this->assertEntity('book', [], 'sidebar_second', 'bartik', -4, $settings);
// Check forum block settings.
$settings = [
'id' => 'forum_active_block',
'label' => '',
'provider' => 'forum',
'label_display' => '0',
'block_count' => 3,
'properties' => [
'administrative' => '1',
],
];
$this->assertEntity('forum', [], 'sidebar_first', 'bartik', -8, $settings);
$settings = [
'id' => 'forum_new_block',
'label' => '',
'provider' => 'forum',
'label_display' => '0',
'block_count' => 4,
'properties' => [
'administrative' => '1',
],
];
$this->assertEntity('forum_1', [], 'sidebar_first', 'bartik', -9, $settings);
// Check statistic block settings.
$settings = [
'id' => 'broken',
'label' => '',
'provider' => 'core',
'label_display' => '0',
'top_day_num' => 7,
'top_all_num' => 8,
'top_last_num' => 9,
];
$this->assertEntity('statistics', [], 'sidebar_second', 'bartik', 0, $settings);
// Check custom blocks.
$visibility = [
'request_path' => [
'id' => 'request_path',
'negate' => FALSE,
'pages' => '<front>',
],
];
$settings = [
'id' => 'block_content',
'label' => 'Static Block',
'provider' => 'block_content',
'label_display' => 'visible',
'status' => TRUE,
'info' => '',
'view_mode' => 'full',
];
$this->assertEntity('block', $visibility, 'content', 'bartik', 0, $settings);
$visibility = [
'request_path' => [
'id' => 'request_path',
'negate' => FALSE,
'pages' => '/node',
],
];
$settings = [
'id' => 'block_content',
'label' => 'Another Static Block',
'provider' => 'block_content',
'label_display' => 'visible',
'status' => TRUE,
'info' => '',
'view_mode' => 'full',
];
// We expect this block to be disabled because '' is not a valid region,
// and block_rebuild() will disable any block in an invalid region.
$this->assertEntity('block_1', $visibility, '', 'bluemarine', -4, $settings, FALSE);
$settings = [
'id' => 'block_content',
'label' => '',
'provider' => 'block_content',
'label_display' => '0',
'status' => TRUE,
'info' => '',
'view_mode' => 'full',
];
$this->assertEntity('block_2', [], 'right', 'test_theme', -7, $settings);
// Custom block with php code is not migrated.
$block = Block::load('block_3');
$this->assertFalse($block instanceof Block);
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\block\Kernel\Migrate\d7;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migration of i18n block translations.
*
* @group migrate_drupal_7
*/
class MigrateBlockContentTranslationTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'node',
'text',
'aggregator',
'book',
'block',
'comment',
'forum',
'views',
'block_content',
'config_translation',
'content_translation',
'language',
'statistics',
'taxonomy',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['block']);
$this->installConfig(['block_content']);
$this->installEntitySchema('block_content');
$this->executeMigrations([
'language',
'd7_filter_format',
'block_content_type',
'block_content_body_field',
'd7_custom_block',
'd7_user_role',
'd7_block',
'd7_block_translation',
]);
block_rebuild();
}
/**
* Tests the migration of block title translation.
*/
public function testBlockContentTranslation() {
/** @var \Drupal\language\ConfigurableLanguageManagerInterface $language_manager */
$language_manager = $this->container->get('language_manager');
$config = $language_manager->getLanguageConfigOverride('fr', 'block.block.bartik_user_login');
$this->assertSame('fr - User login title', $config->get('settings.label'));
}
}

View file

@ -0,0 +1,170 @@
<?php
namespace Drupal\Tests\block\Kernel\Migrate\d7;
use Drupal\block\Entity\Block;
use Drupal\block_content\Entity\BlockContent;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migration of blocks to configuration entities.
*
* @group block
*/
class MigrateBlockTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'block',
'views',
'comment',
'menu_ui',
'block_content',
'node',
'text',
'filter',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Install the themes used for this test.
$this->container->get('theme_installer')->install(['bartik', 'seven']);
$this->installConfig(static::$modules);
$this->installEntitySchema('block_content');
// Set Bartik and Seven as the default public and admin theme.
$config = $this->config('system.theme');
$config->set('default', 'bartik');
$config->set('admin', 'seven');
$config->save();
$this->executeMigrations([
'd7_filter_format',
'd7_user_role',
'block_content_type',
'block_content_body_field',
'd7_custom_block',
'd7_block',
]);
block_rebuild();
}
/**
* Asserts various aspects of a block.
*
* @param string $id
* The block ID.
* @param string $plugin_id
* The block's plugin ID.
* @param array $roles
* Role IDs the block is expected to have.
* @param string $pages
* The list of pages on which the block should appear.
* @param string $region
* The display region.
* @param string $theme
* The theme.
* @param string $weight
* The block weight.
* @param string $label
* The block label.
* @param string $label_display
* The block label display setting.
* @param bool $status
* Whether the block is expected to be enabled or disabled.
*/
public function assertEntity($id, $plugin_id, array $roles, $pages, $region, $theme, $weight, $label, $label_display, $status = TRUE) {
$block = Block::load($id);
$this->assertTrue($block instanceof Block);
/** @var \Drupal\block\BlockInterface $block */
$this->assertSame($plugin_id, $block->getPluginId());
$visibility = $block->getVisibility();
if ($roles) {
$this->assertSame($roles, array_values($visibility['user_role']['roles']));
$this->assertSame('@user.current_user_context:current_user', $visibility['user_role']['context_mapping']['user']);
}
if ($pages) {
$this->assertSame($pages, $visibility['request_path']['pages']);
}
$this->assertSame($region, $block->getRegion());
$this->assertSame($theme, $block->getTheme());
$this->assertSame($weight, $block->getWeight());
$this->assertSame($status, $block->status());
$config = $this->config('block.block.' . $id);
$this->assertSame($label, $config->get('settings.label'));
$this->assertSame($label_display, $config->get('settings.label_display'));
}
/**
* Tests the block migration.
*/
public function testBlockMigration() {
$this->assertEntity('bartik_system_main', 'system_main_block', [], '', 'content', 'bartik', 0, '', '0');
$this->assertEntity('bartik_search_form', 'search_form_block', [], '', 'sidebar_first', 'bartik', -1, '', '0');
$this->assertEntity('bartik_user_login', 'user_login_block', [], '', 'sidebar_first', 'bartik', 0, 'User login title', 'visible');
$this->assertEntity('bartik_system_powered_by', 'system_powered_by_block', [], '', 'footer_fifth', 'bartik', 10, '', '0');
$this->assertEntity('seven_system_main', 'system_main_block', [], '', 'content', 'seven', 0, '', '0');
$this->assertEntity('seven_user_login', 'user_login_block', [], '', 'content', 'seven', 10, 'User login title', 'visible');
// The d7_custom_block migration should have migrated a block containing a
// mildly amusing limerick. We'll need its UUID to determine
// bartik_block_1's plugin ID.
$uuid = BlockContent::load(1)->uuid();
$this->assertEntity('bartik_block_1', 'block_content:' . $uuid, ['authenticated'], '', 'highlighted', 'bartik', 0, 'Mildly amusing limerick of the day', 'visible');
// Assert that disabled blocks (or enabled blocks whose plugin IDs could
// be resolved) did not migrate.
$non_existent_blocks = [
'bartik_system_navigation',
'bartik_system_help',
'seven_user_new',
'seven_search_form',
'bartik_comment_recent',
'bartik_node_syndicate',
'bartik_node_recent',
'bartik_shortcut_shortcuts',
'bartik_system_management',
'bartik_system_user-menu',
'bartik_system_main-menu',
'bartik_user_new',
'bartik_user_online',
'seven_comment_recent',
'seven_node_syndicate',
'seven_shortcut_shortcuts',
'seven_system_powered-by',
'seven_system_navigation',
'seven_system_management',
'seven_system_user-menu',
'seven_system_main-menu',
'seven_user_online',
'bartik_blog_recent',
'bartik_book_navigation',
'bartik_locale_language',
'bartik_forum_active',
'bartik_forum_new',
'seven_blog_recent',
'seven_book_navigation',
'seven_locale_language',
'seven_forum_active',
'seven_forum_new',
'bartik_menu_menu-test-menu',
'bartik_statistics_popular',
'seven_menu_menu-test-menu',
'seven_statistics_popular',
'seven_block_1',
];
$this->assertTrue(empty(Block::loadMultiple($non_existent_blocks)));
}
}

View file

@ -0,0 +1,127 @@
<?php
namespace Drupal\Tests\block\Kernel\Plugin\migrate\source;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests block source plugin.
*
* @covers \Drupal\block\Plugin\migrate\source\Block
* @group block
*/
class BlockTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['blocks'] = [
[
'bid' => 1,
'module' => 'block',
'delta' => '1',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'left',
'visibility' => 0,
'pages' => '',
'title' => 'Test Title 01',
'cache' => -1,
],
[
'bid' => 2,
'module' => 'block',
'delta' => '2',
'theme' => 'garland',
'status' => 1,
'weight' => 5,
'region' => 'right',
'visibility' => 0,
'pages' => '<front>',
'title' => 'Test Title 02',
'cache' => -1,
],
];
$tests[0]['source_data']['blocks_roles'] = [
[
'module' => 'block',
'delta' => 1,
'rid' => 2,
],
[
'module' => 'block',
'delta' => 2,
'rid' => 2,
],
[
'module' => 'block',
'delta' => 2,
'rid' => 100,
],
];
$tests[0]['source_data']['role'] = [
[
'rid' => 2,
'name' => 'authenticated user',
],
];
$tests[0]['source_data']['system'] = [
[
'filename' => 'modules/system/system.module',
'name' => 'system',
'type' => 'module',
'owner' => '',
'status' => '1',
'throttle' => '0',
'bootstrap' => '0',
'schema_version' => '6055',
'weight' => '0',
'info' => 'a:0:{}',
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
'bid' => 1,
'module' => 'block',
'delta' => '1',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'left',
'visibility' => 0,
'pages' => '',
'title' => 'Test Title 01',
'cache' => -1,
'roles' => [2],
],
[
'bid' => 2,
'module' => 'block',
'delta' => '2',
'theme' => 'garland',
'status' => 1,
'weight' => 5,
'region' => 'right',
'visibility' => 0,
'pages' => '<front>',
'title' => 'Test Title 02',
'cache' => -1,
'roles' => [2],
],
];
return $tests;
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace Drupal\Tests\block\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\block\Kernel\Plugin\migrate\source\BlockTest;
/**
* Tests i18n block source plugin.
*
* @covers \Drupal\block\Plugin\migrate\source\d6\BlockTranslation
*
* @group content_translation
*/
class BlockTranslationTest extends BlockTest {
/**
* {@inheritdoc}
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
public function providerSource() {
// Test data is the same as BlockTest, but with the addition of i18n_blocks.
$tests = parent::providerSource();
// The source data.
$tests[0]['source_data']['i18n_blocks'] = [
[
'ibid' => 1,
'module' => 'block',
'delta' => '1',
'type' => 0,
'language' => 'fr',
],
[
'ibid' => 2,
'module' => 'block',
'delta' => '2',
'type' => 0,
'language' => 'zu',
],
];
$tests[0]['source_data']['variables'] = [
[
'name' => 'default_theme',
'value' => 's:7:"garland";',
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
'bid' => 1,
'module' => 'block',
'delta' => '1',
'title' => 'Test Title 01',
'ibid' => 1,
'type' => '0',
'language' => 'fr',
'default_theme' => 'Garland',
],
[
'bid' => 2,
'module' => 'block',
'delta' => '2',
'theme' => 'garland',
'title' => 'Test Title 02',
'ibid' => 2,
'type' => '0',
'language' => 'zu',
'default_theme' => 'Garland',
],
];
return $tests;
}
}

View file

@ -0,0 +1,147 @@
<?php
namespace Drupal\Tests\block\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests i18n block source plugin.
*
* @covers \Drupal\block\Plugin\migrate\source\d7\BlockTranslation
*
* @group content_translation
*/
class BlockTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
// The source data.
$tests[0]['source_data']['block'] = [
[
'bid' => 1,
'module' => 'system',
'delta' => 'main',
'theme' => 'bartik',
'status' => 1,
'weight' => 0,
'region' => 'content',
'custom' => '0',
'visibility' => 0,
'pages' => '',
'title' => '',
'cache' => -1,
'i18n_mode' => 0,
],
[
'bid' => 2,
'module' => 'system',
'delta' => 'navigation',
'theme' => 'bartik',
'status' => 1,
'weight' => 0,
'region' => 'sidebar_first',
'custom' => '0',
'visibility' => 0,
'pages' => '',
'title' => 'Navigation',
'cache' => -1,
'i18n_mode' => 1,
],
];
$tests[0]['source_data']['block_role'] = [
[
'module' => 'block',
'delta' => 1,
'rid' => 2,
],
[
'module' => 'block',
'delta' => 2,
'rid' => 2,
],
[
'module' => 'block',
'delta' => 2,
'rid' => 100,
],
];
$tests[0]['source_data']['i18n_string'] = [
[
'lid' => 1,
'textgroup' => 'block',
'context' => '1',
'objectid' => 'navigation',
'type' => 'system',
'property' => 'title',
'objectindex' => 0,
'format' => '',
],
];
$tests[0]['source_data']['locales_target'] = [
[
'lid' => 1,
'translation' => 'fr - Navigation',
'language' => 'fr',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
];
$tests[0]['source_data']['role'] = [
[
'rid' => 2,
'name' => 'authenticated user',
],
];
$tests[0]['source_data']['system'] = [
[
'filename' => 'modules/system/system.module',
'name' => 'system',
'type' => 'module',
'owner' => '',
'status' => '1',
'throttle' => '0',
'bootstrap' => '0',
'schema_version' => '7055',
'weight' => '0',
'info' => 'a:0:{}',
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
'bid' => 2,
'module' => 'system',
'delta' => 'navigation',
'theme' => 'bartik',
'status' => 1,
'weight' => 0,
'region' => 'sidebar_first',
'custom' => '0',
'visibility' => 0,
'pages' => '',
'title' => 'Navigation',
'cache' => -1,
'i18n_mode' => 1,
'lid' => 1,
'translation' => 'fr - Navigation',
'language' => 'fr',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
];
return $tests;
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace Drupal\Tests\block\Traits;
use Drupal\block\Entity\Block;
/**
* Provides methods to create and place block with default settings.
*
* This trait is meant to be used only by test classes.
*/
trait BlockCreationTrait {
/**
* Creates a block instance based on default settings.
*
* @param string $plugin_id
* The plugin ID of the block type for this block instance.
* @param array $settings
* (optional) An associative array of settings for the block entity.
* Override the defaults by specifying the key and value in the array, for
* example:
* @code
* $this->drupalPlaceBlock('system_powered_by_block', array(
* 'label' => t('Hello, world!'),
* ));
* @endcode
* The following defaults are provided:
* - label: Random string.
* - ID: Random string.
* - region: 'sidebar_first'.
* - theme: The default theme.
* - visibility: Empty array.
*
* @return \Drupal\block\Entity\Block
* The block entity.
*
* @todo
* Add support for creating custom block instances.
*/
protected function placeBlock($plugin_id, array $settings = []) {
$config = \Drupal::configFactory();
$settings += [
'plugin' => $plugin_id,
'region' => 'sidebar_first',
'id' => strtolower($this->randomMachineName(8)),
'theme' => $config->get('system.theme')->get('default'),
'label' => $this->randomMachineName(8),
'visibility' => [],
'weight' => 0,
];
$values = [];
foreach (['region', 'id', 'theme', 'plugin', 'weight', 'visibility'] as $key) {
$values[$key] = $settings[$key];
// Remove extra values that do not belong in the settings array.
unset($settings[$key]);
}
foreach ($values['visibility'] as $id => $visibility) {
$values['visibility'][$id]['id'] = $id;
}
$values['settings'] = $settings;
$block = Block::create($values);
$block->save();
return $block;
}
}

View file

@ -0,0 +1,104 @@
<?php
namespace Drupal\Tests\block\Unit;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\Core\Plugin\Fixtures\TestConfigurablePlugin;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\block\Entity\Block
* @group block
*/
class BlockConfigEntityUnitTest extends UnitTestCase {
/**
* The entity type used for testing.
*
* @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityType;
/**
* The entity type manager used for testing.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityTypeManager;
/**
* The ID of the type of the entity under test.
*
* @var string
*/
protected $entityTypeId;
/**
* The UUID generator used for testing.
*
* @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $uuid;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->entityTypeId = $this->randomMachineName();
$this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
$this->entityType->expects($this->any())
->method('getProvider')
->will($this->returnValue('block'));
$this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
$this->entityTypeManager->expects($this->any())
->method('getDefinition')
->with($this->entityTypeId)
->will($this->returnValue($this->entityType));
$this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface');
$container = new ContainerBuilder();
$container->set('entity_type.manager', $this->entityTypeManager);
$container->set('uuid', $this->uuid);
\Drupal::setContainer($container);
}
/**
* @covers ::calculateDependencies
*/
public function testCalculateDependencies() {
$values = ['theme' => 'stark'];
// Mock the entity under test so that we can mock getPluginCollections().
$entity = $this->getMockBuilder('\Drupal\block\Entity\Block')
->setConstructorArgs([$values, $this->entityTypeId])
->setMethods(['getPluginCollections'])
->getMock();
// Create a configurable plugin that would add a dependency.
$instance_id = $this->randomMachineName();
$instance = new TestConfigurablePlugin([], $instance_id, ['provider' => 'test']);
// Create a plugin collection to contain the instance.
$plugin_collection = $this->getMockBuilder('\Drupal\Core\Plugin\DefaultLazyPluginCollection')
->disableOriginalConstructor()
->setMethods(['get'])
->getMock();
$plugin_collection->expects($this->atLeastOnce())
->method('get')
->with($instance_id)
->will($this->returnValue($instance));
$plugin_collection->addInstanceId($instance_id);
// Return the mocked plugin collection.
$entity->expects($this->once())
->method('getPluginCollections')
->will($this->returnValue([$plugin_collection]));
$dependencies = $entity->calculateDependencies()->getDependencies();
$this->assertContains('test', $dependencies['module']);
$this->assertContains('stark', $dependencies['theme']);
}
}

View file

@ -0,0 +1,157 @@
<?php
namespace Drupal\Tests\block\Unit;
use Drupal\block\BlockForm;
use Drupal\block\Entity\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\PluginFormFactoryInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\block\BlockForm
* @group block
*/
class BlockFormTest extends UnitTestCase {
/**
* The condition plugin manager.
*
* @var \Drupal\Core\Executable\ExecutableManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $conditionManager;
/**
* The block storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $storage;
/**
* The language manager service.
*
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $language;
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $themeHandler;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityManager;
/**
* The mocked context repository.
*
* @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $contextRepository;
/**
* The plugin form manager.
*
* @var \Drupal\Core\Plugin\PluginFormFactoryInterface|\Prophecy\Prophecy\ProphecyInterface
*/
protected $pluginFormFactory;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->conditionManager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
$this->language = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
$this->contextRepository = $this->getMock('Drupal\Core\Plugin\Context\ContextRepositoryInterface');
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$this->storage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
$this->themeHandler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
$this->entityManager->expects($this->any())
->method('getStorage')
->will($this->returnValue($this->storage));
$this->pluginFormFactory = $this->prophesize(PluginFormFactoryInterface::class);
}
/**
* Mocks a block with a block plugin.
*
* @param string $machine_name
* The machine name of the block plugin.
*
* @return \Drupal\block\BlockInterface|\PHPUnit_Framework_MockObject_MockObject
* The mocked block.
*/
protected function getBlockMockWithMachineName($machine_name) {
$plugin = $this->getMockBuilder(BlockBase::class)
->disableOriginalConstructor()
->getMock();
$plugin->expects($this->any())
->method('getMachineNameSuggestion')
->will($this->returnValue($machine_name));
$block = $this->getMockBuilder(Block::class)
->disableOriginalConstructor()
->getMock();
$block->expects($this->any())
->method('getPlugin')
->will($this->returnValue($plugin));
return $block;
}
/**
* Tests the unique machine name generator.
*
* @see \Drupal\block\BlockForm::getUniqueMachineName()
*/
public function testGetUniqueMachineName() {
$blocks = [];
$blocks['test'] = $this->getBlockMockWithMachineName('test');
$blocks['other_test'] = $this->getBlockMockWithMachineName('other_test');
$blocks['other_test_1'] = $this->getBlockMockWithMachineName('other_test');
$blocks['other_test_2'] = $this->getBlockMockWithMachineName('other_test');
$query = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
$query->expects($this->exactly(5))
->method('condition')
->will($this->returnValue($query));
$query->expects($this->exactly(5))
->method('execute')
->will($this->returnValue(['test', 'other_test', 'other_test_1', 'other_test_2']));
$this->storage->expects($this->exactly(5))
->method('getQuery')
->will($this->returnValue($query));
$block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->contextRepository, $this->language, $this->themeHandler, $this->pluginFormFactory->reveal());
// Ensure that the block with just one other instance gets the next available
// name suggestion.
$this->assertEquals('test_2', $block_form_controller->getUniqueMachineName($blocks['test']));
// Ensure that the block with already three instances (_0, _1, _2) gets the
// 4th available name.
$this->assertEquals('other_test_3', $block_form_controller->getUniqueMachineName($blocks['other_test']));
$this->assertEquals('other_test_3', $block_form_controller->getUniqueMachineName($blocks['other_test_1']));
$this->assertEquals('other_test_3', $block_form_controller->getUniqueMachineName($blocks['other_test_2']));
// Ensure that a block without an instance yet gets the suggestion as
// unique machine name.
$last_block = $this->getBlockMockWithMachineName('last_test');
$this->assertEquals('last_test', $block_form_controller->getUniqueMachineName($last_block));
}
}

View file

@ -0,0 +1,194 @@
<?php
/**
* @file
* Contains \Drupal\Tests\block\Unit\BlockRepositoryTest.
*/
namespace Drupal\Tests\block\Unit;
use Drupal\block\BlockRepository;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\block\BlockRepository
* @group block
*/
class BlockRepositoryTest extends UnitTestCase {
/**
* @var \Drupal\block\BlockRepository
*/
protected $blockRepository;
/**
* @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $blockStorage;
/**
* @var string
*/
protected $theme;
/**
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $contextHandler;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$active_theme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
->disableOriginalConstructor()
->getMock();
$this->theme = $this->randomMachineName();
$active_theme->expects($this->atLeastOnce())
->method('getName')
->willReturn($this->theme);
$active_theme->expects($this->atLeastOnce())
->method('getRegions')
->willReturn([
'top',
'center',
'bottom',
]);
$theme_manager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
$theme_manager->expects($this->atLeastOnce())
->method('getActiveTheme')
->will($this->returnValue($active_theme));
$this->contextHandler = $this->getMock('Drupal\Core\Plugin\Context\ContextHandlerInterface');
$this->blockStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$entity_manager->expects($this->any())
->method('getStorage')
->willReturn($this->blockStorage);
$this->blockRepository = new BlockRepository($entity_manager, $theme_manager, $this->contextHandler);
}
/**
* Tests the retrieval of block entities.
*
* @covers ::getVisibleBlocksPerRegion
*
* @dataProvider providerBlocksConfig
*/
public function testGetVisibleBlocksPerRegion(array $blocks_config, array $expected_blocks) {
$blocks = [];
foreach ($blocks_config as $block_id => $block_config) {
$block = $this->getMock('Drupal\block\BlockInterface');
$block->expects($this->once())
->method('access')
->will($this->returnValue($block_config[0]));
$block->expects($block_config[0] ? $this->atLeastOnce() : $this->never())
->method('getRegion')
->willReturn($block_config[1]);
$block->expects($this->any())
->method('label')
->willReturn($block_id);
$block->expects($this->any())
->method('getWeight')
->willReturn($block_config[2]);
$blocks[$block_id] = $block;
}
$this->blockStorage->expects($this->once())
->method('loadByProperties')
->with(['theme' => $this->theme])
->willReturn($blocks);
$result = [];
$cacheable_metadata = [];
foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata) as $region => $resulting_blocks) {
$result[$region] = [];
foreach ($resulting_blocks as $plugin_id => $block) {
$result[$region][] = $plugin_id;
}
}
$this->assertEquals($expected_blocks, $result);
}
public function providerBlocksConfig() {
$blocks_config = [
'block1' => [
AccessResult::allowed(), 'top', 0,
],
// Test a block without access.
'block2' => [
AccessResult::forbidden(), 'bottom', 0,
],
// Test some blocks in the same region with specific weight.
'block4' => [
AccessResult::allowed(), 'bottom', 5,
],
'block3' => [
AccessResult::allowed(), 'bottom', 5,
],
'block5' => [
AccessResult::allowed(), 'bottom', -5,
],
];
$test_cases = [];
$test_cases[] = [$blocks_config,
[
'top' => ['block1'],
'center' => [],
'bottom' => ['block5', 'block3', 'block4'],
],
];
return $test_cases;
}
/**
* Tests the retrieval of block entities that are context-aware.
*
* @covers ::getVisibleBlocksPerRegion
*/
public function testGetVisibleBlocksPerRegionWithContext() {
$block = $this->getMock('Drupal\block\BlockInterface');
$block->expects($this->once())
->method('access')
->willReturn(AccessResult::allowed()->addCacheTags(['config:block.block.block_id']));
$block->expects($this->once())
->method('getRegion')
->willReturn('top');
$blocks['block_id'] = $block;
$this->blockStorage->expects($this->once())
->method('loadByProperties')
->with(['theme' => $this->theme])
->willReturn($blocks);
$result = [];
$cacheable_metadata = [];
foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata) as $region => $resulting_blocks) {
$result[$region] = [];
foreach ($resulting_blocks as $plugin_id => $block) {
$result[$region][] = $plugin_id;
}
}
$expected = [
'top' => [
'block_id',
],
'center' => [],
'bottom' => [],
];
$this->assertSame($expected, $result);
// Assert that the cacheable metadata from the block access results was
// collected.
$this->assertEquals(['config:block.block.block_id'], $cacheable_metadata['top']->getCacheTags());
}
}
interface TestContextAwareBlockInterface extends BlockPluginInterface, ContextAwarePluginInterface {
}

View file

@ -0,0 +1,85 @@
<?php
namespace Drupal\Tests\block\Unit;
use Drupal\Component\Utility\Html;
use Drupal\block\Controller\CategoryAutocompleteController;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
/**
* @coversDefaultClass \Drupal\block\Controller\CategoryAutocompleteController
* @group block
*/
class CategoryAutocompleteTest extends UnitTestCase {
/**
* The autocomplete controller.
*
* @var \Drupal\block\Controller\CategoryAutocompleteController
*/
protected $autocompleteController;
protected function setUp() {
$block_manager = $this->getMock('Drupal\Core\Block\BlockManagerInterface');
$block_manager->expects($this->any())
->method('getCategories')
->will($this->returnValue(['Comment', 'Node', 'None & Such', 'User']));
$this->autocompleteController = new CategoryAutocompleteController($block_manager);
}
/**
* Tests the autocomplete method.
*
* @param string $string
* The string entered into the autocomplete.
* @param array $suggestions
* The array of expected suggestions.
*
* @see \Drupal\block\Controller\CategoryAutocompleteController::autocomplete()
*
* @dataProvider providerTestAutocompleteSuggestions
*/
public function testAutocompleteSuggestions($string, $suggestions) {
$suggestions = array_map(function ($suggestion) {
return ['value' => $suggestion, 'label' => Html::escape($suggestion)];
}, $suggestions);
$result = $this->autocompleteController->autocomplete(new Request(['q' => $string]));
$this->assertSame($suggestions, json_decode($result->getContent(), TRUE));
}
/**
* Data provider for testAutocompleteSuggestions().
*
* @return array
*/
public function providerTestAutocompleteSuggestions() {
$test_parameters = [];
$test_parameters[] = [
'string' => 'Com',
'suggestions' => [
'Comment',
],
];
$test_parameters[] = [
'string' => 'No',
'suggestions' => [
'Node',
'None & Such',
],
];
$test_parameters[] = [
'string' => 'us',
'suggestions' => [
'User',
],
];
$test_parameters[] = [
'string' => 'Banana',
'suggestions' => [],
];
return $test_parameters;
}
}

View file

@ -0,0 +1,88 @@
<?php
namespace Drupal\Tests\block\Unit\Menu;
use Drupal\Tests\Core\Menu\LocalTaskIntegrationTestBase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Tests block local tasks.
*
* @group block
*/
class BlockLocalTasksTest extends LocalTaskIntegrationTestBase {
protected function setUp() {
$this->directoryList = ['block' => 'core/modules/block'];
parent::setUp();
$config_factory = $this->getConfigFactoryStub([
'system.theme' => ['default' => 'test_c'],
]);
$themes = [];
$themes['test_a'] = (object) [
'status' => 1,
'info' => [
'name' => 'test_a',
'hidden' => TRUE,
],
];
$themes['test_b'] = (object) [
'status' => 1,
'info' => [
'name' => 'test_b',
],
];
$themes['test_c'] = (object) [
'status' => 1,
'info' => [
'name' => 'test_c',
],
];
$theme_handler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
$theme_handler->expects($this->any())
->method('listInfo')
->will($this->returnValue($themes));
$theme_handler->expects($this->any())
->method('hasUi')
->willReturnMap([
['test_a', FALSE],
['test_b', TRUE],
['test_c', TRUE],
]);
$container = new ContainerBuilder();
$container->set('config.factory', $config_factory);
$container->set('theme_handler', $theme_handler);
$container->set('app.root', $this->root);
\Drupal::setContainer($container);
}
/**
* Tests the admin edit local task.
*/
public function testBlockAdminLocalTasks() {
$this->assertLocalTasks('entity.block.edit_form', [['entity.block.edit_form']]);
}
/**
* Tests the block admin display local tasks.
*
* @dataProvider providerTestBlockAdminDisplay
*/
public function testBlockAdminDisplay($route, $expected) {
$this->assertLocalTasks($route, $expected);
}
/**
* Provides a list of routes to test.
*/
public function providerTestBlockAdminDisplay() {
return [
['block.admin_display', [['block.admin_display'], ['block.admin_display_theme:test_b', 'block.admin_display_theme:test_c']]],
['block.admin_display_theme', [['block.admin_display'], ['block.admin_display_theme:test_b', 'block.admin_display_theme:test_c']]],
];
}
}

View file

@ -0,0 +1,262 @@
<?php
namespace Drupal\Tests\block\Unit\Plugin\DisplayVariant;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\DependencyInjection\Container;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\block\Plugin\DisplayVariant\BlockPageVariant
* @group block
*/
class BlockPageVariantTest extends UnitTestCase {
/**
* The block repository.
*
* @var \Drupal\block\BlockRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $blockRepository;
/**
* The block view builder.
*
* @var \Drupal\Core\Entity\EntityViewBuilderInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $blockViewBuilder;
/**
* The plugin context handler.
*
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $contextHandler;
/**
* Sets up a display variant plugin for testing.
*
* @param array $configuration
* An array of plugin configuration.
* @param array $definition
* The plugin definition array.
*
* @return \Drupal\block\Plugin\DisplayVariant\BlockPageVariant|\PHPUnit_Framework_MockObject_MockObject
* A mocked display variant plugin.
*/
public function setUpDisplayVariant($configuration = [], $definition = []) {
$container = new Container();
$cache_context_manager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
->disableOriginalConstructor()
->setMethods(['assertValidTokens'])
->getMock();
$container->set('cache_contexts_manager', $cache_context_manager);
$cache_context_manager->expects($this->any())
->method('assertValidTokens')
->willReturn(TRUE);
\Drupal::setContainer($container);
$this->blockRepository = $this->getMock('Drupal\block\BlockRepositoryInterface');
$this->blockViewBuilder = $this->getMock('Drupal\Core\Entity\EntityViewBuilderInterface');
return $this->getMockBuilder('Drupal\block\Plugin\DisplayVariant\BlockPageVariant')
->setConstructorArgs([$configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, ['config:block_list']])
->setMethods(['getRegionNames'])
->getMock();
}
public function providerBuild() {
$blocks_config = [
'block1' => [
// region, is main content block, is messages block, is title block
'top', FALSE, FALSE, FALSE,
],
// Test multiple blocks in the same region.
'block2' => [
'bottom', FALSE, FALSE, FALSE,
],
'block3' => [
'bottom', FALSE, FALSE, FALSE,
],
// Test a block implementing MainContentBlockPluginInterface.
'block4' => [
'center', TRUE, FALSE, FALSE,
],
// Test a block implementing MessagesBlockPluginInterface.
'block5' => [
'center', FALSE, TRUE, FALSE,
],
// Test a block implementing TitleBlockPluginInterface.
'block6' => [
'center', FALSE, FALSE, TRUE,
],
];
$test_cases = [];
$test_cases[] = [$blocks_config, 6,
[
'#cache' => [
'tags' => [
'config:block_list',
'route',
],
'contexts' => [],
'max-age' => -1,
],
'top' => [
'block1' => [],
'#sorted' => TRUE,
],
// The main content was rendered via a block.
'center' => [
'block4' => [],
'block5' => [],
'block6' => [],
'#sorted' => TRUE,
],
'bottom' => [
'block2' => [],
'block3' => [],
'#sorted' => TRUE,
],
],
];
unset($blocks_config['block5']);
$test_cases[] = [$blocks_config, 5,
[
'#cache' => [
'tags' => [
'config:block_list',
'route',
],
'contexts' => [],
'max-age' => -1,
],
'top' => [
'block1' => [],
'#sorted' => TRUE,
],
'center' => [
'block4' => [],
'block6' => [],
'#sorted' => TRUE,
],
'bottom' => [
'block2' => [],
'block3' => [],
'#sorted' => TRUE,
],
// The messages are rendered via the fallback in case there is no block
// rendering the main content.
'content' => [
'messages' => [
'#weight' => -1000,
'#type' => 'status_messages',
],
],
],
];
unset($blocks_config['block4']);
unset($blocks_config['block6']);
$test_cases[] = [$blocks_config, 3,
[
'#cache' => [
'tags' => [
'config:block_list',
'route',
],
'contexts' => [],
'max-age' => -1,
],
'top' => [
'block1' => [],
'#sorted' => TRUE,
],
'bottom' => [
'block2' => [],
'block3' => [],
'#sorted' => TRUE,
],
// The main content & messages are rendered via the fallback in case
// there are no blocks rendering them.
'content' => [
'system_main' => ['#markup' => 'Hello kittens!'],
'messages' => [
'#weight' => -1000,
'#type' => 'status_messages',
],
],
],
];
return $test_cases;
}
/**
* Tests the building of a full page variant.
*
* @covers ::build
*
* @dataProvider providerBuild
*/
public function testBuild(array $blocks_config, $visible_block_count, array $expected_render_array) {
$display_variant = $this->setUpDisplayVariant();
$display_variant->setMainContent(['#markup' => 'Hello kittens!']);
$blocks = ['top' => [], 'center' => [], 'bottom' => []];
$block_plugin = $this->getMock('Drupal\Core\Block\BlockPluginInterface');
$main_content_block_plugin = $this->getMock('Drupal\Core\Block\MainContentBlockPluginInterface');
$messages_block_plugin = $this->getMock('Drupal\Core\Block\MessagesBlockPluginInterface');
$title_block_plugin = $this->getMock('Drupal\Core\Block\TitleBlockPluginInterface');
foreach ($blocks_config as $block_id => $block_config) {
$block = $this->getMock('Drupal\block\BlockInterface');
$block->expects($this->atLeastOnce())
->method('getPlugin')
->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : ($block_config[3] ? $title_block_plugin : $block_plugin)));
$blocks[$block_config[0]][$block_id] = $block;
}
$this->blockViewBuilder->expects($this->exactly($visible_block_count))
->method('view')
->will($this->returnValue([]));
$this->blockRepository->expects($this->once())
->method('getVisibleBlocksPerRegion')
->willReturnCallback(function (&$cacheable_metadata) use ($blocks) {
$cacheable_metadata['top'] = (new CacheableMetadata())->addCacheTags(['route']);
return $blocks;
});
$value = $display_variant->build();
$this->assertSame($expected_render_array, $value);
}
/**
* Tests the building of a full page variant with no main content set.
*
* @covers ::build
*/
public function testBuildWithoutMainContent() {
$display_variant = $this->setUpDisplayVariant();
$this->blockRepository->expects($this->once())
->method('getVisibleBlocksPerRegion')
->willReturn([]);
$expected = [
'#cache' => [
'tags' => [
'config:block_list',
],
'contexts' => [],
'max-age' => -1,
],
'content' => [
'system_main' => [],
'messages' => [
'#weight' => -1000,
'#type' => 'status_messages',
],
],
];
$this->assertSame($expected, $display_variant->build());
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace Drupal\Tests\block\Unit\Plugin\migrate\process;
use Drupal\block\Plugin\migrate\process\BlockRegion;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\block\Plugin\migrate\process\BlockRegion
* @group block
*/
class BlockRegionTest extends UnitTestCase {
/**
* Transforms a value through the block_region plugin.
*
* @param array $value
* The value to transform.
* @param \Drupal\migrate\Row|null $row
* (optional) The mocked row.
*
* @return array|string
* The transformed value.
*/
protected function transform(array $value, Row $row = NULL) {
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
if (empty($row)) {
$row = $this->prophesize(Row::class)->reveal();
}
$configuration = [
'map' => [
'bartik' => [
'bartik' => [
'triptych_first' => 'triptych_first',
'triptych_middle' => 'triptych_second',
'triptych_last' => 'triptych_third',
],
],
],
'default_value' => 'content',
];
$plugin = new BlockRegion($configuration, 'block_region', [], $configuration['map']['bartik']['bartik']);
return $plugin->transform($value, $executable, $row, 'foo');
}
/**
* If the source and destination themes are identical, the region should only
* be passed through if it actually exists in the destination theme.
*
* @covers ::transform
*/
public function testTransformSameThemeRegionExists() {
$this->assertSame('triptych_second', $this->transform(['bartik', 'bartik', 'triptych_middle']));
}
/**
* If the source and destination themes are identical, the region should be
* changed to 'content' if it doesn't exist in the destination theme.
*
* @covers ::transform
*/
public function testTransformSameThemeRegionNotExists() {
$this->assertSame('content', $this->transform(['bartik', 'bartik', 'footer']));
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace Drupal\Tests\block\Unit\Plugin\migrate\process;
use Drupal\block\Plugin\migrate\process\BlockVisibility;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\Plugin\MigrateProcessInterface;
use Drupal\Tests\migrate\Unit\process\MigrateProcessTestCase;
/**
* Tests the block_visibility process plugin.
*
* @coversDefaultClass \Drupal\block\Plugin\migrate\process\BlockVisibility
* @group block
*/
class BlockVisibilityTest extends MigrateProcessTestCase {
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
$migration_plugin = $this->prophesize(MigrateProcessInterface::class);
$this->plugin = new BlockVisibility([], 'block_visibility_pages', [], $this->moduleHandler->reveal(), $migration_plugin->reveal());
}
/**
* @covers ::transform
*/
public function testTransformNoData() {
$transformed_value = $this->plugin->transform([0, '', []], $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertEmpty($transformed_value);
}
/**
* @covers ::transform
*/
public function testTransformSinglePageWithFront() {
$visibility = $this->plugin->transform([0, '<front>', []], $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame('request_path', $visibility['request_path']['id']);
$this->assertTrue($visibility['request_path']['negate']);
$this->assertSame('<front>', $visibility['request_path']['pages']);
}
/**
* @covers ::transform
*/
public function testTransformMultiplePagesWithFront() {
$visibility = $this->plugin->transform([1, "foo\n/bar\rbaz\r\n<front>", []], $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame('request_path', $visibility['request_path']['id']);
$this->assertFalse($visibility['request_path']['negate']);
$this->assertSame("/foo\n/bar\n/baz\n<front>", $visibility['request_path']['pages']);
}
/**
* @covers ::transform
*/
public function testTransformPhpEnabled() {
$this->moduleHandler->moduleExists('php')->willReturn(TRUE);
$visibility = $this->plugin->transform([2, '<?php', []], $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame('php', $visibility['php']['id']);
$this->assertFalse($visibility['php']['negate']);
$this->assertSame('<?php', $visibility['php']['php']);
}
/**
* @covers ::transform
*/
public function testTransformPhpDisabled() {
$this->moduleHandler->moduleExists('php')->willReturn(FALSE);
$transformed_value = $this->plugin->transform([2, '<?php', []], $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertEmpty($transformed_value);
}
/**
* @covers ::transform
*/
public function testTransformException() {
$this->moduleHandler->moduleExists('php')->willReturn(FALSE);
$migration_plugin = $this->prophesize(MigrateProcessInterface::class);
$this->row = $this->getMockBuilder('Drupal\migrate\Row')
->disableOriginalConstructor()
->setMethods(['getSourceProperty'])
->getMock();
$this->row->expects($this->exactly(2))
->method('getSourceProperty')
->willReturnMap([['bid', 99], ['module', 'foobar']]);
$this->plugin = new BlockVisibility(['skip_php' => TRUE], 'block_visibility_pages', [], $this->moduleHandler->reveal(), $migration_plugin->reveal());
$this->setExpectedException(MigrateSkipRowException::class, "The block with bid '99' from module 'foobar' will have no PHP or request_path visibility configuration.");
$this->plugin->transform([2, '<?php', []], $this->migrateExecutable, $this->row, 'destinationproperty');
}
}