Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
44
web/core/modules/layout_builder/tests/fixtures/update/layout-builder-enable.php
vendored
Normal file
44
web/core/modules/layout_builder/tests/fixtures/update/layout-builder-enable.php
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
// Add a layout plugin to an existing entity view display without explicitly
|
||||
// enabling Layout Builder for this display.
|
||||
$display = $connection->select('config')
|
||||
->fields('config', ['data'])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.entity_view_display.block_content.basic.default')
|
||||
->execute()
|
||||
->fetchField();
|
||||
$display = unserialize($display);
|
||||
$display['third_party_settings']['layout_builder']['sections'][] = [
|
||||
'layout_id' => 'layout_onecol',
|
||||
'layout_settings' => [],
|
||||
'components' => [
|
||||
'some-uuid' => [
|
||||
'uuid' => 'some-uuid',
|
||||
'region' => 'content',
|
||||
'configuration' => [
|
||||
'id' => 'system_powered_by_block',
|
||||
],
|
||||
'additional' => [],
|
||||
'weight' => 0,
|
||||
],
|
||||
],
|
||||
];
|
||||
$connection->update('config')
|
||||
->fields([
|
||||
'data' => serialize($display),
|
||||
'collection' => '',
|
||||
'name' => 'core.entity_view_display.block_content.basic.default',
|
||||
])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.entity_view_display.block_content.basic.default')
|
||||
->execute();
|
29
web/core/modules/layout_builder/tests/fixtures/update/layout-builder-extra.php
vendored
Normal file
29
web/core/modules/layout_builder/tests/fixtures/update/layout-builder-extra.php
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
// Enable Layout Builder on an existing entity view display.
|
||||
$display = $connection->select('config')
|
||||
->fields('config', ['data'])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.entity_view_display.node.article.default')
|
||||
->execute()
|
||||
->fetchField();
|
||||
$display = unserialize($display);
|
||||
$display['third_party_settings']['layout_builder']['enabled'] = TRUE;
|
||||
$connection->update('config')
|
||||
->fields([
|
||||
'data' => serialize($display),
|
||||
'collection' => '',
|
||||
'name' => 'core.entity_view_display.node.article.default',
|
||||
])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.entity_view_display.node.article.default')
|
||||
->execute();
|
42
web/core/modules/layout_builder/tests/fixtures/update/layout-builder.php
vendored
Normal file
42
web/core/modules/layout_builder/tests/fixtures/update/layout-builder.php
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
// Set the schema version.
|
||||
$connection->merge('key_value')
|
||||
->fields([
|
||||
'value' => 'i:8000;',
|
||||
'name' => 'layout_builder',
|
||||
'collection' => 'system.schema',
|
||||
])
|
||||
->condition('collection', 'system.schema')
|
||||
->condition('name', 'layout_builder')
|
||||
->execute();
|
||||
|
||||
// Update core.extension.
|
||||
$extensions = $connection->select('config')
|
||||
->fields('config', ['data'])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.extension')
|
||||
->execute()
|
||||
->fetchField();
|
||||
$extensions = unserialize($extensions);
|
||||
$extensions['module']['layout_builder'] = 0;
|
||||
$extensions['module']['layout_discovery'] = 0;
|
||||
$extensions['module']['layout_test'] = 0;
|
||||
$connection->update('config')
|
||||
->fields([
|
||||
'data' => serialize($extensions),
|
||||
'collection' => '',
|
||||
'name' => 'core.extension',
|
||||
])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.extension')
|
||||
->execute();
|
33
web/core/modules/layout_builder/tests/fixtures/update/section-dependencies.php
vendored
Normal file
33
web/core/modules/layout_builder/tests/fixtures/update/section-dependencies.php
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
// Add a layout plugin with a dependency to an existing entity view display.
|
||||
$display = $connection->select('config')
|
||||
->fields('config', ['data'])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.entity_view_display.node.article.teaser')
|
||||
->execute()
|
||||
->fetchField();
|
||||
$display = unserialize($display);
|
||||
$display['third_party_settings']['layout_builder']['sections'][] = [
|
||||
'layout_id' => 'layout_test_dependencies_plugin',
|
||||
'layout_settings' => [],
|
||||
'components' => [],
|
||||
];
|
||||
$connection->update('config')
|
||||
->fields([
|
||||
'data' => serialize($display),
|
||||
'collection' => '',
|
||||
'name' => 'core.entity_view_display.node.article.teaser',
|
||||
])
|
||||
->condition('collection', '')
|
||||
->condition('name', 'core.entity_view_display.node.article.teaser')
|
||||
->execute();
|
|
@ -0,0 +1,6 @@
|
|||
name: 'Layout Builder test'
|
||||
type: module
|
||||
description: 'Support module for testing layout building.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides hook implementations for Layout Builder tests.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_plugin_filter_TYPE__CONSUMER_alter().
|
||||
*/
|
||||
function layout_builder_test_plugin_filter_block__layout_builder_alter(array &$definitions, array $extra) {
|
||||
// Explicitly remove the "Help" blocks from the list.
|
||||
unset($definitions['help_block']);
|
||||
|
||||
// Explicitly remove the "Sticky at top of lists field_block".
|
||||
$disallowed_fields = [
|
||||
'sticky',
|
||||
];
|
||||
|
||||
// Remove "Changed" field if this is the first section.
|
||||
if ($extra['delta'] === 0) {
|
||||
$disallowed_fields[] = 'changed';
|
||||
}
|
||||
|
||||
foreach ($definitions as $plugin_id => $definition) {
|
||||
// Field block IDs are in the form 'field_block:{entity}:{bundle}:{name}',
|
||||
// for example 'field_block:node:article:revision_timestamp'.
|
||||
preg_match('/field_block:.*:.*:(.*)/', $plugin_id, $parts);
|
||||
if (isset($parts[1]) && in_array($parts[1], $disallowed_fields, TRUE)) {
|
||||
// Unset any field blocks that match our predefined list.
|
||||
unset($definitions[$plugin_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_extra_field_info().
|
||||
*/
|
||||
function layout_builder_test_entity_extra_field_info() {
|
||||
$extra['node']['bundle_with_section_field']['display']['layout_builder_test'] = [
|
||||
'label' => t('Extra label'),
|
||||
'description' => t('Extra description'),
|
||||
'weight' => 0,
|
||||
];
|
||||
return $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_node_view().
|
||||
*/
|
||||
function layout_builder_test_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
|
||||
if ($display->getComponent('layout_builder_test')) {
|
||||
$build['layout_builder_test'] = [
|
||||
'#markup' => 'Extra, Extra read all about it.',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a 'TestAjax' block.
|
||||
*
|
||||
* @Block(
|
||||
* id = "layout_builder_test_testajax",
|
||||
* admin_label = @Translation("TestAjax"),
|
||||
* category = @Translation("Test")
|
||||
* )
|
||||
*/
|
||||
class TestAjaxBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function blockForm($form, FormStateInterface $form_state) {
|
||||
$form['ajax_test'] = [
|
||||
'#type' => 'radios',
|
||||
'#options' => [
|
||||
1 => $this->t('Ajax test option 1'),
|
||||
2 => $this->t('Ajax test option 2'),
|
||||
],
|
||||
'#prefix' => '<div id="test-ajax-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#title' => $this->t('Time in this ajax test is @time', [
|
||||
'@time' => time(),
|
||||
]),
|
||||
'#ajax' => [
|
||||
'wrapper' => 'test-ajax-wrapper',
|
||||
'callback' => [$this, 'ajaxCallback'],
|
||||
],
|
||||
];
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback.
|
||||
*/
|
||||
public function ajaxCallback($form, $form_state) {
|
||||
return $form['settings']['ajax_test'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
$build['content'] = [
|
||||
'#markup' => $this->t('Every word is like an unnecessary stain on silence and nothingness.'),
|
||||
];
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_block_view
|
||||
label: 'Test Block View'
|
||||
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
|
||||
settings:
|
||||
link_to_entity: true
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
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: true
|
||||
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
|
||||
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
|
||||
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 Block View'
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
block_1:
|
||||
display_plugin: block
|
||||
id: block_1
|
||||
display_title: Block
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -0,0 +1,8 @@
|
|||
name: 'Layout Builder Views Test'
|
||||
type: module
|
||||
description: 'Support module for testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- views
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Remove all transitions for testing.
|
||||
*/
|
||||
* {
|
||||
/* CSS transitions. */
|
||||
-o-transition-property: none !important;
|
||||
-moz-transition-property: none !important;
|
||||
-ms-transition-property: none !important;
|
||||
-webkit-transition-property: none !important;
|
||||
transition-property: none !important;
|
||||
/* CSS transforms. */
|
||||
-o-transform: none !important;
|
||||
-moz-transform: none !important;
|
||||
-ms-transform: none !important;
|
||||
-webkit-transform: none !important;
|
||||
transform: none !important;
|
||||
/* CSS animations. */
|
||||
-webkit-animation: none !important;
|
||||
-moz-animation: none !important;
|
||||
-o-animation: none !important;
|
||||
-ms-animation: none !important;
|
||||
animation: none !important;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
name: 'CSS Test fix'
|
||||
type: module
|
||||
description: 'Provides CSS fixes for tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,5 @@
|
|||
drupal.css_fix:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/css_fix.theme.css: {}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Module for attaching CSS during tests.
|
||||
*
|
||||
* CSS pointer-events properties cause testing errors.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_page_attachments().
|
||||
*/
|
||||
function settings_tray_test_css_page_attachments(array &$attachments) {
|
||||
// Unconditionally attach an asset to the page.
|
||||
$attachments['#attached']['library'][] = 'settings_tray_test_css/drupal.css_fix';
|
||||
}
|
|
@ -0,0 +1,526 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Tests the Layout Builder UI.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutBuilderTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'views',
|
||||
'layout_builder',
|
||||
'layout_builder_views_test',
|
||||
'layout_test',
|
||||
'block',
|
||||
'node',
|
||||
'layout_builder_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// @todo The Layout Builder UI relies on local tasks; fix in
|
||||
// https://www.drupal.org/project/drupal/issues/2917777.
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
// Create two nodes.
|
||||
$this->createContentType(['type' => 'bundle_with_section_field']);
|
||||
$this->createNode([
|
||||
'type' => 'bundle_with_section_field',
|
||||
'title' => 'The first node title',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'The first node body',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$this->createNode([
|
||||
'type' => 'bundle_with_section_field',
|
||||
'title' => 'The second node title',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'The second node body',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testLayoutBuilderUi() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The first node body');
|
||||
$assert_session->pageTextNotContains('Powered by Drupal');
|
||||
$assert_session->linkNotExists('Layout');
|
||||
|
||||
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
|
||||
// From the manage display page, go to manage the layout.
|
||||
$this->drupalGet("$field_ui_prefix/display/default");
|
||||
$assert_session->linkNotExists('Manage layout');
|
||||
$assert_session->fieldDisabled('layout[allow_custom]');
|
||||
|
||||
$this->drupalPostForm(NULL, ['layout[enabled]' => TRUE], 'Save');
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$this->clickLink('Manage layout');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display-layout/default");
|
||||
// The body field is only present once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
// The extra field is only present once.
|
||||
$this->assertTextAppearsOnce('Placeholder for the "Extra label" field');
|
||||
// Save the defaults.
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display/default");
|
||||
|
||||
// Load the default layouts again after saving to confirm fields are only
|
||||
// added on new layouts.
|
||||
$this->drupalGet("$field_ui_prefix/display/default");
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$this->clickLink('Manage layout');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display-layout/default");
|
||||
// The body field is only present once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
// The extra field is only present once.
|
||||
$this->assertTextAppearsOnce('Placeholder for the "Extra label" field');
|
||||
|
||||
// Add a new block.
|
||||
$assert_session->linkExists('Add Block');
|
||||
$this->clickLink('Add Block');
|
||||
$assert_session->linkExists('Powered by Drupal');
|
||||
$this->clickLink('Powered by Drupal');
|
||||
$page->fillField('settings[label]', 'This is the label');
|
||||
$page->checkField('settings[label_display]');
|
||||
$page->pressButton('Add Block');
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
$assert_session->pageTextContains('This is the label');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display-layout/default");
|
||||
|
||||
// Save the defaults.
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$assert_session->pageTextContains('The layout has been saved.');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display/default");
|
||||
|
||||
// The node uses the defaults, no overrides available.
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The first node body');
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
$assert_session->pageTextContains('Extra, Extra read all about it.');
|
||||
$assert_session->pageTextNotContains('Placeholder for the "Extra label" field');
|
||||
$assert_session->linkNotExists('Layout');
|
||||
|
||||
// Enable overrides.
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[allow_custom]' => TRUE], 'Save');
|
||||
$this->drupalGet('node/1');
|
||||
|
||||
// Remove the section from the defaults.
|
||||
$assert_session->linkExists('Layout');
|
||||
$this->clickLink('Layout');
|
||||
$assert_session->pageTextContains('Placeholder for the "Extra label" field');
|
||||
$assert_session->linkExists('Remove section');
|
||||
$this->clickLink('Remove section');
|
||||
$page->pressButton('Remove');
|
||||
|
||||
// Add a new section.
|
||||
$this->clickLink('Add Section');
|
||||
$assert_session->linkExists('Two column');
|
||||
$this->clickLink('Two column');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$assert_session->pageTextNotContains('The first node body');
|
||||
$assert_session->pageTextNotContains('Powered by Drupal');
|
||||
$assert_session->pageTextNotContains('Extra, Extra read all about it.');
|
||||
$assert_session->pageTextNotContains('Placeholder for the "Extra label" field');
|
||||
|
||||
// Assert that overrides cannot be turned off while overrides exist.
|
||||
$this->drupalGet("$field_ui_prefix/display/default");
|
||||
$assert_session->checkboxChecked('layout[allow_custom]');
|
||||
$assert_session->fieldDisabled('layout[allow_custom]');
|
||||
|
||||
// Alter the defaults.
|
||||
$this->drupalGet("$field_ui_prefix/display-layout/default");
|
||||
$assert_session->linkExists('Add Block');
|
||||
$this->clickLink('Add Block');
|
||||
$assert_session->linkExists('Title');
|
||||
$this->clickLink('Title');
|
||||
$page->pressButton('Add Block');
|
||||
// The title field is present.
|
||||
$assert_session->elementExists('css', '.field--name-title');
|
||||
$this->clickLink('Save Layout');
|
||||
|
||||
// View the other node, which is still using the defaults.
|
||||
$this->drupalGet('node/2');
|
||||
$assert_session->pageTextContains('The second node title');
|
||||
$assert_session->pageTextContains('The second node body');
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
$assert_session->pageTextContains('Extra, Extra read all about it.');
|
||||
$assert_session->pageTextNotContains('Placeholder for the "Extra label" field');
|
||||
|
||||
// The overridden node does not pick up the changes to defaults.
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->elementNotExists('css', '.field--name-title');
|
||||
$assert_session->pageTextNotContains('The first node body');
|
||||
$assert_session->pageTextNotContains('Powered by Drupal');
|
||||
$assert_session->pageTextNotContains('Extra, Extra read all about it.');
|
||||
$assert_session->pageTextNotContains('Placeholder for the "Extra label" field');
|
||||
$assert_session->linkExists('Layout');
|
||||
|
||||
// Reverting the override returns it to the defaults.
|
||||
$this->clickLink('Layout');
|
||||
$assert_session->linkExists('Add Block');
|
||||
$this->clickLink('Add Block');
|
||||
$assert_session->linkExists('ID');
|
||||
$this->clickLink('ID');
|
||||
$page->pressButton('Add Block');
|
||||
// The title field is present.
|
||||
$assert_session->elementExists('css', '.field--name-nid');
|
||||
$assert_session->pageTextContains('ID');
|
||||
$assert_session->pageTextContains('1');
|
||||
$assert_session->linkExists('Revert to defaults');
|
||||
$this->clickLink('Revert to defaults');
|
||||
$page->pressButton('Revert');
|
||||
$assert_session->pageTextContains('The layout has been reverted back to defaults.');
|
||||
$assert_session->elementExists('css', '.field--name-title');
|
||||
$assert_session->elementNotExists('css', '.field--name-nid');
|
||||
$assert_session->pageTextContains('The first node body');
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
$assert_session->pageTextContains('Placeholder for the "Extra label" field');
|
||||
|
||||
// Assert that overrides can be turned off now that all overrides are gone.
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[allow_custom]' => FALSE], 'Save');
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->linkNotExists('Layout');
|
||||
|
||||
// Add a new field.
|
||||
$edit = [
|
||||
'new_storage_type' => 'string',
|
||||
'label' => 'My text field',
|
||||
'field_name' => 'my_text',
|
||||
];
|
||||
$this->drupalPostForm("$field_ui_prefix/fields/add-field", $edit, 'Save and continue');
|
||||
$page->pressButton('Save field settings');
|
||||
$page->pressButton('Save settings');
|
||||
$this->drupalGet("$field_ui_prefix/display-layout/default");
|
||||
$assert_session->pageTextContains('My text field');
|
||||
$assert_session->elementExists('css', '.field--name-field-my-text');
|
||||
|
||||
// Delete the field.
|
||||
$this->drupalPostForm("$field_ui_prefix/fields/node.bundle_with_section_field.field_my_text/delete", [], 'Delete');
|
||||
$this->drupalGet("$field_ui_prefix/display-layout/default");
|
||||
$assert_session->pageTextNotContains('My text field');
|
||||
$assert_session->elementNotExists('css', '.field--name-field-my-text');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a non-default view mode works as expected.
|
||||
*/
|
||||
public function testNonDefaultViewMode() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
]));
|
||||
|
||||
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
// Allow overrides for the layout.
|
||||
$this->drupalGet("$field_ui_prefix/display/default");
|
||||
$page->checkField('layout[enabled]');
|
||||
$page->pressButton('Save');
|
||||
$page->checkField('layout[allow_custom]');
|
||||
$page->pressButton('Save');
|
||||
|
||||
$this->clickLink('Manage layout');
|
||||
// Confirm the body field only is shown once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
$this->clickLink('Cancel Layout');
|
||||
|
||||
$this->clickLink('Teaser');
|
||||
// Enabling Layout Builder for the default mode does not affect the teaser.
|
||||
$assert_session->addressEquals("$field_ui_prefix/display/teaser");
|
||||
$assert_session->elementNotExists('css', '#layout-builder__layout');
|
||||
$assert_session->checkboxNotChecked('layout[enabled]');
|
||||
$page->checkField('layout[enabled]');
|
||||
$page->pressButton('Save');
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$page->clickLink('Manage layout');
|
||||
// Confirm the body field only is shown once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
|
||||
// Enable a disabled view mode.
|
||||
$page->clickLink('Cancel Layout');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display/teaser");
|
||||
$page->clickLink('Default');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display");
|
||||
$assert_session->linkNotExists('Full content');
|
||||
$page->checkField('display_modes_custom[full]');
|
||||
$page->pressButton('Save');
|
||||
|
||||
$assert_session->linkExists('Full content');
|
||||
$page->clickLink('Full content');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display/full");
|
||||
$page->checkField('layout[enabled]');
|
||||
$page->pressButton('Save');
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$page->clickLink('Manage layout');
|
||||
// Confirm the body field only is shown once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that component's dependencies are respected during removal.
|
||||
*/
|
||||
public function testPluginDependencies() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->container->get('module_installer')->install(['menu_ui']);
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer menu',
|
||||
]));
|
||||
|
||||
// Create a new menu.
|
||||
$this->drupalGet('admin/structure/menu/add');
|
||||
$page->fillField('label', 'My Menu');
|
||||
$page->fillField('id', 'mymenu');
|
||||
$page->pressButton('Save');
|
||||
$this->drupalGet('admin/structure/menu/add');
|
||||
$page->fillField('label', 'My Menu');
|
||||
$page->fillField('id', 'myothermenu');
|
||||
$page->pressButton('Save');
|
||||
|
||||
$this->drupalPostForm('admin/structure/types/manage/bundle_with_section_field/display', ['layout[enabled]' => TRUE], 'Save');
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$this->clickLink('Manage layout');
|
||||
$assert_session->linkExists('Add Section');
|
||||
$this->clickLink('Add Section');
|
||||
$assert_session->linkExists('Layout plugin (with dependencies)');
|
||||
$this->clickLink('Layout plugin (with dependencies)');
|
||||
$assert_session->elementExists('css', '.layout--layout-test-dependencies-plugin');
|
||||
$assert_session->elementExists('css', '.field--name-body');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$this->drupalPostForm('admin/structure/menu/manage/myothermenu/delete', [], 'Delete');
|
||||
$this->drupalGet('admin/structure/types/manage/bundle_with_section_field/display-layout/default');
|
||||
$assert_session->elementNotExists('css', '.layout--layout-test-dependencies-plugin');
|
||||
$assert_session->elementExists('css', '.field--name-body');
|
||||
|
||||
// Add a menu block.
|
||||
$assert_session->linkExists('Add Block');
|
||||
$this->clickLink('Add Block');
|
||||
$assert_session->linkExists('My Menu');
|
||||
$this->clickLink('My Menu');
|
||||
$page->pressButton('Add Block');
|
||||
|
||||
// Add another block alongside the menu.
|
||||
$assert_session->linkExists('Add Block');
|
||||
$this->clickLink('Add Block');
|
||||
$assert_session->linkExists('Powered by Drupal');
|
||||
$this->clickLink('Powered by Drupal');
|
||||
$page->pressButton('Add Block');
|
||||
|
||||
// Assert that the blocks are visible, and save the layout.
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
$assert_session->pageTextContains('My Menu');
|
||||
$assert_session->elementExists('css', '.block.menu--mymenu');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
|
||||
// Delete the menu.
|
||||
$this->drupalPostForm('admin/structure/menu/manage/mymenu/delete', [], 'Delete');
|
||||
|
||||
// Ensure that the menu block is gone, but that the other block remains.
|
||||
$this->drupalGet('admin/structure/types/manage/bundle_with_section_field/display-layout/default');
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
$assert_session->pageTextNotContains('My Menu');
|
||||
$assert_session->elementNotExists('css', '.block.menu--mymenu');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the interaction between full and default view modes.
|
||||
*
|
||||
* @see \Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::getDefaultSectionStorage()
|
||||
*/
|
||||
public function testLayoutBuilderUiFullViewMode() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
|
||||
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
// Allow overrides for the layout.
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[enabled]' => TRUE], 'Save');
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[allow_custom]' => TRUE], 'Save');
|
||||
|
||||
// Customize the default view mode.
|
||||
$this->drupalGet("$field_ui_prefix/display-layout/default");
|
||||
$this->clickLink('Add Block');
|
||||
$this->clickLink('Powered by Drupal');
|
||||
$page->fillField('settings[label]', 'This is the default view mode');
|
||||
$page->checkField('settings[label_display]');
|
||||
$page->pressButton('Add Block');
|
||||
$assert_session->pageTextContains('This is the default view mode');
|
||||
$this->clickLink('Save Layout');
|
||||
|
||||
// The default view mode is used for both the node display and layout UI.
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('This is the default view mode');
|
||||
$this->drupalGet('node/1/layout');
|
||||
$assert_session->pageTextContains('This is the default view mode');
|
||||
$this->clickLink('Cancel Layout');
|
||||
|
||||
// Enable the full view mode and customize it.
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['display_modes_custom[full]' => TRUE], 'Save');
|
||||
$this->drupalPostForm("$field_ui_prefix/display/full", ['layout[enabled]' => TRUE], 'Save');
|
||||
$this->drupalGet("$field_ui_prefix/display-layout/full");
|
||||
$this->clickLink('Add Block');
|
||||
$this->clickLink('Powered by Drupal');
|
||||
$page->fillField('settings[label]', 'This is the full view mode');
|
||||
$page->checkField('settings[label_display]');
|
||||
$page->pressButton('Add Block');
|
||||
$assert_session->pageTextContains('This is the full view mode');
|
||||
$this->clickLink('Save Layout');
|
||||
|
||||
// The full view mode is now used for both the node display and layout UI.
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('This is the full view mode');
|
||||
$this->drupalGet('node/1/layout');
|
||||
$assert_session->pageTextContains('This is the full view mode');
|
||||
$this->clickLink('Cancel Layout');
|
||||
|
||||
// Disable the full view mode, the default should be used again.
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['display_modes_custom[full]' => FALSE], 'Save');
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('This is the default view mode');
|
||||
$this->drupalGet('node/1/layout');
|
||||
$assert_session->pageTextContains('This is the default view mode');
|
||||
$this->clickLink('Cancel Layout');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testLayoutBuilderChooseBlocksAlter() {
|
||||
// See layout_builder_test_plugin_filter_block__layout_builder_alter().
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
|
||||
// From the manage display page, go to manage the layout.
|
||||
$this->drupalPostForm('admin/structure/types/manage/bundle_with_section_field/display/default', ['layout[enabled]' => TRUE], 'Save');
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$this->clickLink('Manage layout');
|
||||
|
||||
// Add a new block.
|
||||
$this->clickLink('Add Block');
|
||||
|
||||
// Verify that blocks not modified are present.
|
||||
$assert_session->linkExists('Powered by Drupal');
|
||||
$assert_session->linkExists('Default revision');
|
||||
|
||||
// Verify that blocks explicitly removed are not present.
|
||||
$assert_session->linkNotExists('Help');
|
||||
$assert_session->linkNotExists('Sticky at top of lists');
|
||||
|
||||
// Verify that Changed block is not present on first section.
|
||||
$assert_session->linkNotExists('Changed');
|
||||
|
||||
// Go back to Manage layout.
|
||||
$this->drupalGet('admin/structure/types/manage/bundle_with_section_field/display/default');
|
||||
$this->clickLink('Manage layout');
|
||||
|
||||
// Add a new section.
|
||||
$this->clickLink('Add Section', 1);
|
||||
$assert_session->linkExists('Two column');
|
||||
$this->clickLink('Two column');
|
||||
|
||||
// Add a new block to second section.
|
||||
$this->clickLink('Add Block', 1);
|
||||
|
||||
// Verify that Changed block is present on second section.
|
||||
$assert_session->linkExists('Changed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that deleting a View block used in Layout Builder works.
|
||||
*/
|
||||
public function testDeletedView() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
]));
|
||||
|
||||
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
// Enable overrides.
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[enabled]' => TRUE], 'Save');
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[allow_custom]' => TRUE], 'Save');
|
||||
$this->drupalGet('node/1');
|
||||
|
||||
$assert_session->linkExists('Layout');
|
||||
$this->clickLink('Layout');
|
||||
$this->clickLink('Add Block');
|
||||
$this->clickLink('Test Block View');
|
||||
$page->pressButton('Add Block');
|
||||
|
||||
$assert_session->pageTextContains('Test Block View');
|
||||
$assert_session->elementExists('css', '.block-views-blocktest-block-view-block-1');
|
||||
$this->clickLink('Save Layout');
|
||||
$assert_session->pageTextContains('Test Block View');
|
||||
$assert_session->elementExists('css', '.block-views-blocktest-block-view-block-1');
|
||||
|
||||
View::load('test_block_view')->delete();
|
||||
$this->drupalGet('node/1');
|
||||
// Node can be loaded after deleting the View.
|
||||
$assert_session->pageTextContains(Node::load(1)->getTitle());
|
||||
$assert_session->pageTextNotContains('Test Block View');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a text string only appears once on the page.
|
||||
*
|
||||
* @param string $needle
|
||||
* The string to look for.
|
||||
*/
|
||||
protected function assertTextAppearsOnce($needle) {
|
||||
$this->assertEquals(1, substr_count($this->getSession()->getPage()->getContent(), $needle), "'$needle' only appears once on the page.");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests functionality of the entity view display with regard to Layout Builder.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutDisplayTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['field_ui', 'layout_builder', 'block', 'node'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// @todo The Layout Builder UI relies on local tasks; fix in
|
||||
// https://www.drupal.org/project/drupal/issues/2917777.
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
$this->createContentType([
|
||||
'type' => 'bundle_with_section_field',
|
||||
]);
|
||||
$this->createNode(['type' => 'bundle_with_section_field']);
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer display modes',
|
||||
], 'foobar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the interaction between multiple view modes.
|
||||
*/
|
||||
public function testMultipleViewModes() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field/display';
|
||||
|
||||
// Enable Layout Builder for the default view modes, and overrides.
|
||||
$this->drupalGet("$field_ui_prefix/default");
|
||||
$page->checkField('layout[enabled]');
|
||||
$page->pressButton('Save');
|
||||
$page->checkField('layout[allow_custom]');
|
||||
$page->pressButton('Save');
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextNotContains('Powered by Drupal');
|
||||
|
||||
$assert_session->linkExists('Layout');
|
||||
$this->clickLink('Layout');
|
||||
$assert_session->linkExists('Add Block');
|
||||
$this->clickLink('Add Block');
|
||||
$assert_session->linkExists('Powered by Drupal');
|
||||
$this->clickLink('Powered by Drupal');
|
||||
$page->pressButton('Add Block');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
|
||||
// Add a new view mode.
|
||||
$this->drupalGet('admin/structure/display-modes/view/add/node');
|
||||
$page->fillField('label', 'New');
|
||||
$page->fillField('id', 'new');
|
||||
$page->pressButton('Save');
|
||||
|
||||
// Enable the new view mode.
|
||||
$this->drupalGet("$field_ui_prefix/default");
|
||||
$page->checkField('display_modes_custom[new]');
|
||||
$page->pressButton('Save');
|
||||
|
||||
// Enable and disable Layout Builder for the new view mode.
|
||||
$this->drupalGet("$field_ui_prefix/new");
|
||||
$page->checkField('layout[enabled]');
|
||||
$page->pressButton('Save');
|
||||
$page->uncheckField('layout[enabled]');
|
||||
$page->pressButton('Save');
|
||||
$page->pressButton('Confirm');
|
||||
|
||||
// The node using the default view mode still contains its overrides.
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,380 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Functional;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the rendering of a layout section field.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutSectionTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['field_ui', 'layout_builder', 'node', 'block_test'];
|
||||
|
||||
/**
|
||||
* The name of the layout section field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'layout_builder__layout';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->createContentType([
|
||||
'type' => 'bundle_without_section_field',
|
||||
]);
|
||||
$this->createContentType([
|
||||
'type' => 'bundle_with_section_field',
|
||||
]);
|
||||
|
||||
LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default')
|
||||
->enableLayoutBuilder()
|
||||
->setOverridable()
|
||||
->save();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
'administer content types',
|
||||
], 'foobar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for ::testLayoutSectionFormatter().
|
||||
*/
|
||||
public function providerTestLayoutSectionFormatter() {
|
||||
$data = [];
|
||||
$data['block_with_global_context'] = [
|
||||
[
|
||||
[
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'test_context_aware',
|
||||
'context_mapping' => [
|
||||
'user' => '@user.current_user_context:current_user',
|
||||
],
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
[
|
||||
'.layout--onecol',
|
||||
'#test_context_aware--username',
|
||||
],
|
||||
[
|
||||
'foobar',
|
||||
],
|
||||
'user',
|
||||
'user:2',
|
||||
'UNCACHEABLE',
|
||||
];
|
||||
$data['block_with_entity_context'] = [
|
||||
[
|
||||
[
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'field_block:node:bundle_with_section_field:body',
|
||||
'context_mapping' => [
|
||||
'entity' => 'layout_builder.entity',
|
||||
],
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
[
|
||||
'.layout--onecol',
|
||||
'.field--name-body',
|
||||
],
|
||||
[
|
||||
'Body',
|
||||
'The node body',
|
||||
],
|
||||
'',
|
||||
'',
|
||||
'MISS',
|
||||
];
|
||||
$data['single_section_single_block'] = [
|
||||
[
|
||||
[
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'system_powered_by_block',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
'.layout--onecol',
|
||||
'Powered by',
|
||||
'',
|
||||
'',
|
||||
'MISS',
|
||||
];
|
||||
$data['multiple_sections'] = [
|
||||
[
|
||||
[
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'system_powered_by_block',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
[
|
||||
'section' => new Section('layout_twocol', [], [
|
||||
'foo' => new SectionComponent('foo', 'first', [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'foo text',
|
||||
]),
|
||||
'bar' => new SectionComponent('bar', 'second', [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'bar text',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
[
|
||||
'.layout--onecol',
|
||||
'.layout--twocol',
|
||||
],
|
||||
[
|
||||
'Powered by',
|
||||
'foo text',
|
||||
'bar text',
|
||||
],
|
||||
'user.permissions',
|
||||
'',
|
||||
'MISS',
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests layout_section formatter output.
|
||||
*
|
||||
* @dataProvider providerTestLayoutSectionFormatter
|
||||
*/
|
||||
public function testLayoutSectionFormatter($layout_data, $expected_selector, $expected_content, $expected_cache_contexts, $expected_cache_tags, $expected_dynamic_cache) {
|
||||
$node = $this->createSectionNode($layout_data);
|
||||
|
||||
$canonical_url = $node->toUrl('canonical');
|
||||
$this->drupalGet($canonical_url);
|
||||
$this->assertLayoutSection($expected_selector, $expected_content, $expected_cache_contexts, $expected_cache_tags, $expected_dynamic_cache);
|
||||
|
||||
$this->drupalGet($canonical_url->toString() . '/layout');
|
||||
$this->assertLayoutSection($expected_selector, $expected_content, $expected_cache_contexts, $expected_cache_tags, 'UNCACHEABLE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the access checking of the section formatter.
|
||||
*/
|
||||
public function testLayoutSectionFormatterAccess() {
|
||||
$node = $this->createSectionNode([
|
||||
[
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'test_access',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
]);
|
||||
|
||||
// Restrict access to the block.
|
||||
$this->container->get('state')->set('test_block_access', FALSE);
|
||||
|
||||
$this->drupalGet($node->toUrl('canonical'));
|
||||
$this->assertLayoutSection('.layout--onecol', NULL, '', '', 'UNCACHEABLE');
|
||||
// Ensure the block was not rendered.
|
||||
$this->assertSession()->pageTextNotContains('Hello test world');
|
||||
|
||||
// Grant access to the block, and ensure it was rendered.
|
||||
$this->container->get('state')->set('test_block_access', TRUE);
|
||||
$this->drupalGet($node->toUrl('canonical'));
|
||||
$this->assertLayoutSection('.layout--onecol', 'Hello test world', '', '', 'UNCACHEABLE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the multilingual support of the section formatter.
|
||||
*/
|
||||
public function testMultilingualLayoutSectionFormatter() {
|
||||
$this->container->get('module_installer')->install(['content_translation']);
|
||||
$this->rebuildContainer();
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('es')->save();
|
||||
$this->container->get('content_translation.manager')->setEnabled('node', 'bundle_with_section_field', TRUE);
|
||||
|
||||
$entity = $this->createSectionNode([
|
||||
[
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'system_powered_by_block',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
]);
|
||||
$entity->addTranslation('es', [
|
||||
'title' => 'Translated node title',
|
||||
$this->fieldName => [
|
||||
[
|
||||
'section' => new Section('layout_twocol', [], [
|
||||
'foo' => new SectionComponent('foo', 'first', [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'foo text',
|
||||
]),
|
||||
'bar' => new SectionComponent('bar', 'second', [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'bar text',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
]);
|
||||
$entity->save();
|
||||
|
||||
$this->drupalGet($entity->toUrl('canonical'));
|
||||
$this->assertLayoutSection('.layout--onecol', 'Powered by');
|
||||
$this->drupalGet($entity->toUrl('canonical')->setOption('prefix', 'es/'));
|
||||
$this->assertLayoutSection('.layout--twocol', ['foo text', 'bar text']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the entity title is displayed.
|
||||
*/
|
||||
public function testLayoutPageTitle() {
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
$node = $this->createSectionNode([]);
|
||||
|
||||
$this->drupalGet($node->toUrl('canonical')->toString() . '/layout');
|
||||
$this->assertSession()->titleEquals('Edit layout for The node title | Drupal');
|
||||
$this->assertEquals('Edit layout for The node title', $this->cssSelect('h1.page-title')[0]->getText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that no Layout link shows without a section field.
|
||||
*/
|
||||
public function testLayoutUrlNoSectionField() {
|
||||
$node = $this->createNode([
|
||||
'type' => 'bundle_without_section_field',
|
||||
'title' => 'The node title',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'The node body',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
$this->drupalGet($node->toUrl('canonical')->toString() . '/layout');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that deleting a field removes it from the layout.
|
||||
*/
|
||||
public function testLayoutDeletingField() {
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
$this->drupalGet('/admin/structure/types/manage/bundle_with_section_field/display-layout/default');
|
||||
$assert_session->statusCodeEquals(200);
|
||||
$assert_session->elementExists('css', '.field--name-body');
|
||||
|
||||
// Delete the field from both bundles.
|
||||
$this->drupalGet('/admin/structure/types/manage/bundle_without_section_field/fields/node.bundle_without_section_field.body/delete');
|
||||
$this->submitForm([], 'Delete');
|
||||
$this->drupalGet('/admin/structure/types/manage/bundle_with_section_field/display-layout/default');
|
||||
$assert_session->statusCodeEquals(200);
|
||||
$assert_session->elementExists('css', '.field--name-body');
|
||||
|
||||
$this->drupalGet('/admin/structure/types/manage/bundle_with_section_field/fields/node.bundle_with_section_field.body/delete');
|
||||
$this->submitForm([], 'Delete');
|
||||
$this->drupalGet('/admin/structure/types/manage/bundle_with_section_field/display-layout/default');
|
||||
$assert_session->statusCodeEquals(200);
|
||||
$assert_session->elementNotExists('css', '.field--name-body');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that deleting a bundle removes the layout.
|
||||
*/
|
||||
public function testLayoutDeletingBundle() {
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
$display = LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default');
|
||||
$this->assertInstanceOf(LayoutBuilderEntityViewDisplay::class, $display);
|
||||
|
||||
$this->drupalPostForm('/admin/structure/types/manage/bundle_with_section_field/delete', [], 'Delete');
|
||||
$assert_session->statusCodeEquals(200);
|
||||
|
||||
$display = LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default');
|
||||
$this->assertNull($display);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the output of a layout section.
|
||||
*
|
||||
* @param string|array $expected_selector
|
||||
* A selector or list of CSS selectors to find.
|
||||
* @param string|array $expected_content
|
||||
* A string or list of strings to find.
|
||||
* @param string $expected_cache_contexts
|
||||
* A string of cache contexts to be found in the header.
|
||||
* @param string $expected_cache_tags
|
||||
* A string of cache tags to be found in the header.
|
||||
* @param string $expected_dynamic_cache
|
||||
* The expected dynamic cache header. Either 'HIT', 'MISS' or 'UNCACHEABLE'.
|
||||
*/
|
||||
protected function assertLayoutSection($expected_selector, $expected_content, $expected_cache_contexts = '', $expected_cache_tags = '', $expected_dynamic_cache = 'MISS') {
|
||||
$assert_session = $this->assertSession();
|
||||
// Find the given selector.
|
||||
foreach ((array) $expected_selector as $selector) {
|
||||
$element = $this->cssSelect($selector);
|
||||
$this->assertNotEmpty($element);
|
||||
}
|
||||
|
||||
// Find the given content.
|
||||
foreach ((array) $expected_content as $content) {
|
||||
$assert_session->pageTextContains($content);
|
||||
}
|
||||
if ($expected_cache_contexts) {
|
||||
$assert_session->responseHeaderContains('X-Drupal-Cache-Contexts', $expected_cache_contexts);
|
||||
}
|
||||
if ($expected_cache_tags) {
|
||||
$assert_session->responseHeaderContains('X-Drupal-Cache-Tags', $expected_cache_tags);
|
||||
}
|
||||
$assert_session->responseHeaderEquals('X-Drupal-Dynamic-Cache', $expected_dynamic_cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a node with a section field.
|
||||
*
|
||||
* @param array $section_values
|
||||
* An array of values for a section field.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The node object.
|
||||
*/
|
||||
protected function createSectionNode(array $section_values) {
|
||||
return $this->createNode([
|
||||
'type' => 'bundle_with_section_field',
|
||||
'title' => 'The node title',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'The node body',
|
||||
],
|
||||
],
|
||||
$this->fieldName => $section_values,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Functional\Update;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests the upgrade path for Layout Builder extra fields.
|
||||
*
|
||||
* @group layout_builder
|
||||
* @group legacy
|
||||
*/
|
||||
class ExtraFieldUpdatePathTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/layout-builder.php',
|
||||
__DIR__ . '/../../../fixtures/update/layout-builder-extra.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the upgrade path for Layout Builder extra fields.
|
||||
*/
|
||||
public function testRunUpdates() {
|
||||
// The default view mode has Layout Builder enabled.
|
||||
$data = EntityViewDisplay::load('node.article.default')->toArray();
|
||||
$this->assertArrayHasKey('third_party_settings', $data);
|
||||
$this->assertArrayNotHasKey('sections', $data['third_party_settings']['layout_builder']);
|
||||
|
||||
// The teaser view mode does not have Layout Builder enabled.
|
||||
$data = EntityViewDisplay::load('node.article.teaser')->toArray();
|
||||
$this->assertArrayNotHasKey('third_party_settings', $data);
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
// The extra links have been added.
|
||||
$data = EntityViewDisplay::load('node.article.default')->toArray();
|
||||
$components = $data['third_party_settings']['layout_builder']['sections'][0]->getComponents();
|
||||
$component = reset($components);
|
||||
$this->assertSame('extra_field_block:node:article:links', $component->getPluginId());
|
||||
|
||||
// No extra links have been added.
|
||||
$data = EntityViewDisplay::load('node.article.teaser')->toArray();
|
||||
$this->assertArrayNotHasKey('third_party_settings', $data);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests the upgrade path for enabling Layout Builder.
|
||||
*
|
||||
* @see layout_builder_update_8601()
|
||||
*
|
||||
* @group layout_builder
|
||||
* @group legacy
|
||||
*/
|
||||
class LayoutBuilderEnableUpdatePathTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/layout-builder.php',
|
||||
__DIR__ . '/../../../fixtures/update/layout-builder-enable.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the upgrade path for enabling Layout Builder.
|
||||
*/
|
||||
public function testRunUpdates() {
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
$expected = [
|
||||
'sections' => [
|
||||
[
|
||||
'layout_id' => 'layout_onecol',
|
||||
'layout_settings' => [],
|
||||
'components' => [
|
||||
'some-uuid' => [
|
||||
'uuid' => 'some-uuid',
|
||||
'region' => 'content',
|
||||
'configuration' => [
|
||||
'id' => 'system_powered_by_block',
|
||||
],
|
||||
'additional' => [],
|
||||
'weight' => 0,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertLayoutBuilderSettings($expected, 'block_content', 'basic', 'default');
|
||||
$this->assertLayoutBuilderSettings(NULL, 'node', 'page', 'default');
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
// The display with existing sections is enabled while the other is not.
|
||||
$expected['enabled'] = TRUE;
|
||||
$this->assertLayoutBuilderSettings($expected, 'block_content', 'basic', 'default');
|
||||
$this->assertLayoutBuilderSettings(NULL, 'node', 'page', 'default');
|
||||
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->drupalGet('admin/structure/block/block-content/manage/basic/display');
|
||||
$assert_session->checkboxChecked('layout[enabled]');
|
||||
$this->drupalGet('admin/structure/types/manage/page/display');
|
||||
$assert_session->checkboxNotChecked('layout[enabled]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the Layout Builder settings for a given display.
|
||||
*
|
||||
* @param mixed $expected
|
||||
* The expected value.
|
||||
* @param string $entity_type_id
|
||||
* The entity type ID.
|
||||
* @param string $bundle
|
||||
* The bundle.
|
||||
* @param string $view_mode
|
||||
* The view mode.
|
||||
*/
|
||||
protected function assertLayoutBuilderSettings($expected, $entity_type_id, $bundle, $view_mode) {
|
||||
$this->assertEquals($expected, \Drupal::config("core.entity_view_display.$entity_type_id.$bundle.$view_mode")->get('third_party_settings.layout_builder'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Functional\Update;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests the upgrade path for Layout Builder section dependencies.
|
||||
*
|
||||
* @group layout_builder
|
||||
* @group legacy
|
||||
*/
|
||||
class SectionDependenciesUpdatePathTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/layout-builder.php',
|
||||
__DIR__ . '/../../../fixtures/update/section-dependencies.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the upgrade path for Layout Builder section dependencies.
|
||||
*/
|
||||
public function testRunUpdates() {
|
||||
$data = EntityViewDisplay::load('node.article.teaser')->toArray();
|
||||
$this->assertNotContains('system.menu.myothermenu', $data['dependencies']['config']);
|
||||
$this->assertNotContains('layout_builder', $data['dependencies']['module']);
|
||||
$this->assertNotContains('layout_test', $data['dependencies']['module']);
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
$data = EntityViewDisplay::load('node.article.teaser')->toArray();
|
||||
$this->assertContains('system.menu.myothermenu', $data['dependencies']['config']);
|
||||
$this->assertContains('layout_builder', $data['dependencies']['module']);
|
||||
$this->assertContains('layout_test', $data['dependencies']['module']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
|
||||
/**
|
||||
* Ajax blocks tests.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class AjaxBlockTest extends WebDriverTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'block',
|
||||
'node',
|
||||
'datetime',
|
||||
'layout_builder',
|
||||
'user',
|
||||
'layout_builder_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$user = $this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]);
|
||||
$user->save();
|
||||
$this->drupalLogin($user);
|
||||
$this->createContentType(['type' => 'bundle_with_section_field']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuring a field block for a user field.
|
||||
*/
|
||||
public function testAddAjaxBlock() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
// Start by creating a node.
|
||||
$node = $this->createNode([
|
||||
'type' => 'bundle_with_section_field',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'The node body',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$node->save();
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The node body');
|
||||
$assert_session->pageTextNotContains('Every word is like an unnecessary stain on silence and nothingness.');
|
||||
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
|
||||
// From the manage display page, go to manage the layout.
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[enabled]' => TRUE], 'Save');
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$this->clickLink('Manage layout');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display-layout/default");
|
||||
// The body field is present.
|
||||
$assert_session->elementExists('css', '.field--name-body');
|
||||
|
||||
// Add a new block.
|
||||
$assert_session->linkExists('Add Block');
|
||||
$this->clickLink('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->linkExists('TestAjax');
|
||||
$this->clickLink('TestAjax');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Find the radio buttons.
|
||||
$name = 'settings[ajax_test]';
|
||||
/** @var \Behat\Mink\Element\NodeElement[] $radios */
|
||||
$radios = $this->cssSelect('input[name="' . $name . '"]');
|
||||
// Click them both a couple of times.
|
||||
foreach ([1, 2] as $rounds) {
|
||||
foreach ($radios as $radio) {
|
||||
$radio->click();
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
}
|
||||
}
|
||||
// Then add the block.
|
||||
$page->pressButton('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$block_elements = $this->cssSelect('.block-layout-builder-test-testajax');
|
||||
// Should be exactly one of these in there.
|
||||
$this->assertEquals(1, count($block_elements));
|
||||
$assert_session->pageTextContains('Every word is like an unnecessary stain on silence and nothingness.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Plugin\Block\FieldBlock
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldBlockTest extends WebDriverTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['block', 'datetime', 'layout_builder', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'field_date',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'datetime',
|
||||
]);
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'user',
|
||||
'label' => 'Date field',
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
$user = $this->drupalCreateUser([
|
||||
'administer blocks',
|
||||
'access administration pages',
|
||||
]);
|
||||
$user->field_date = '1978-11-19T05:00:00';
|
||||
$user->save();
|
||||
$this->drupalLogin($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuring a field block for a user field.
|
||||
*/
|
||||
public function testFieldBlock() {
|
||||
$page = $this->getSession()->getPage();
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
// Assert that the field value is not displayed.
|
||||
$this->drupalGet('admin');
|
||||
$assert_session->pageTextNotContains('Sunday, November 19, 1978 - 16:00');
|
||||
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$this->clickLink('Place block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// Ensure that fields without any formatters are not available.
|
||||
$assert_session->pageTextNotContains('Password');
|
||||
// Ensure that non-display-configurable fields are not available.
|
||||
$assert_session->pageTextNotContains('Initial email');
|
||||
|
||||
$assert_session->pageTextContains('Date field');
|
||||
$block_url = 'admin/structure/block/add/field_block%3Auser%3Auser%3Afield_date/classy';
|
||||
$assert_session->linkByHrefExists($block_url);
|
||||
|
||||
$this->drupalGet($block_url);
|
||||
$page->fillField('region', 'content');
|
||||
|
||||
// Assert the default formatter configuration.
|
||||
$assert_session->fieldValueEquals('settings[formatter][type]', 'datetime_default');
|
||||
$assert_session->fieldValueEquals('settings[formatter][settings][format_type]', 'medium');
|
||||
|
||||
// Change the formatter.
|
||||
$page->selectFieldOption('settings[formatter][type]', 'datetime_time_ago');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Changing the formatter removes the old settings and introduces new ones.
|
||||
$assert_session->fieldNotExists('settings[formatter][settings][format_type]');
|
||||
$assert_session->fieldExists('settings[formatter][settings][granularity]');
|
||||
$page->pressButton('Save block');
|
||||
$assert_session->pageTextContains('The block configuration has been saved.');
|
||||
|
||||
// Configure the block and change the formatter again.
|
||||
$this->clickLink('Configure');
|
||||
$page->selectFieldOption('settings[formatter][type]', 'datetime_default');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->fieldValueEquals('settings[formatter][settings][format_type]', 'medium');
|
||||
$page->selectFieldOption('settings[formatter][settings][format_type]', 'long');
|
||||
|
||||
$page->pressButton('Save block');
|
||||
$assert_session->pageTextContains('The block configuration has been saved.');
|
||||
|
||||
// Assert that the field value is updated.
|
||||
$this->clickLink('Configure');
|
||||
$assert_session->fieldValueEquals('settings[formatter][settings][format_type]', 'long');
|
||||
|
||||
// Assert that the field block is configured as expected.
|
||||
$expected = [
|
||||
'label' => 'above',
|
||||
'type' => 'datetime_default',
|
||||
'settings' => [
|
||||
'format_type' => 'long',
|
||||
'timezone_override' => '',
|
||||
],
|
||||
'third_party_settings' => [],
|
||||
];
|
||||
$config = $this->container->get('config.factory')->get('block.block.datefield');
|
||||
$this->assertEquals($expected, $config->get('settings.formatter'));
|
||||
$this->assertEquals(['field.field.user.user.field_date'], $config->get('dependencies.config'));
|
||||
|
||||
// Assert that the block is displaying the user field.
|
||||
$this->drupalGet('admin');
|
||||
$assert_session->pageTextContains('Sunday, November 19, 1978 - 16:00');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\file\FileInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\file\Functional\FileFieldCreationTrait;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Test access to private files in block fields on the Layout Builder.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class InlineBlockPrivateFilesTest extends InlineBlockTestBase {
|
||||
|
||||
use FileFieldCreationTrait;
|
||||
use TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'file',
|
||||
];
|
||||
|
||||
/**
|
||||
* The file system service.
|
||||
*
|
||||
* @var \Drupal\Core\File\FileSystemInterface
|
||||
*/
|
||||
protected $fileSystem;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Update the test node type to not create new revisions by default. This
|
||||
// allows testing for cases when a new revision is made and when it isn't.
|
||||
$node_type = NodeType::load('bundle_with_section_field');
|
||||
$node_type->setNewRevision(FALSE);
|
||||
$node_type->save();
|
||||
$field_settings = [
|
||||
'file_extensions' => 'txt',
|
||||
'uri_scheme' => 'private',
|
||||
];
|
||||
$this->createFileField('field_file', 'block_content', 'basic', $field_settings);
|
||||
$this->fileSystem = $this->container->get('file_system');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test access to private files added via inline blocks in the layout builder.
|
||||
*/
|
||||
public function testPrivateFiles() {
|
||||
$assert_session = $this->assertSession();
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access contextual links',
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
|
||||
// Enable layout builder and overrides.
|
||||
$this->drupalPostForm(
|
||||
static::FIELD_UI_PREFIX . '/display/default',
|
||||
['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
|
||||
'Save'
|
||||
);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Log in as user you can only configure layouts and access content.
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access contextual links',
|
||||
'configure any layout',
|
||||
'access content',
|
||||
]));
|
||||
$this->drupalGet('node/1/layout');
|
||||
$file = $this->createPrivateFile('drupal.txt');
|
||||
|
||||
$file_real_path = $this->fileSystem->realpath($file->getFileUri());
|
||||
$this->assertFileExists($file_real_path);
|
||||
$this->addInlineFileBlockToLayout('The file', $file);
|
||||
$this->assertSaveLayout();
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$private_href1 = $this->assertFileAccessibleOnNode($file);
|
||||
|
||||
// Remove the inline block with the private file.
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->removeInlineBlockFromLayout();
|
||||
$this->assertSaveLayout();
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextNotContains($file->label());
|
||||
// Try to access file directly after it has been removed. Since a new
|
||||
// revision was not created for the node the inline block is not in the
|
||||
// layout of a previous revision of the node.
|
||||
$this->drupalGet($private_href1);
|
||||
$assert_session->pageTextContains('You are not authorized to access this page');
|
||||
$assert_session->pageTextNotContains($this->getFileSecret($file));
|
||||
$this->assertFileExists($file_real_path);
|
||||
|
||||
$file2 = $this->createPrivateFile('2ndFile.txt');
|
||||
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->addInlineFileBlockToLayout('Number2', $file2);
|
||||
$this->assertSaveLayout();
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$private_href2 = $this->assertFileAccessibleOnNode($file2);
|
||||
|
||||
$this->createNewNodeRevision(1);
|
||||
|
||||
$file3 = $this->createPrivateFile('3rdFile.txt');
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->replaceFileInBlock($file3);
|
||||
$this->assertSaveLayout();
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$private_href3 = $this->assertFileAccessibleOnNode($file3);
|
||||
|
||||
// $file2 is on a previous revision of the block which is on a previous
|
||||
// revision of the node. The user does not have access to view the previous
|
||||
// revision of the node.
|
||||
$this->drupalGet($private_href2);
|
||||
$assert_session->pageTextContains('You are not authorized to access this page');
|
||||
|
||||
$node = Node::load(1);
|
||||
$node->setUnpublished();
|
||||
$node->save();
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('You are not authorized to access this page');
|
||||
$this->drupalGet($private_href3);
|
||||
$assert_session->pageTextNotContains($this->getFileSecret($file3));
|
||||
$assert_session->pageTextContains('You are not authorized to access this page');
|
||||
|
||||
$this->drupalGet('node/2/layout');
|
||||
$file4 = $this->createPrivateFile('drupal.txt');
|
||||
$this->addInlineFileBlockToLayout('The file', $file4);
|
||||
$this->assertSaveLayout();
|
||||
|
||||
$this->drupalGet('node/2');
|
||||
$private_href4 = $this->assertFileAccessibleOnNode($file4);
|
||||
|
||||
$this->createNewNodeRevision(2);
|
||||
|
||||
// Remove the inline block with the private file.
|
||||
// The inline block will still be attached to the previous revision of the
|
||||
// node.
|
||||
$this->drupalGet('node/2/layout');
|
||||
$this->removeInlineBlockFromLayout();
|
||||
$this->assertSaveLayout();
|
||||
|
||||
// Ensure that since the user cannot view the previous revision of the node
|
||||
// they can not view the file which is only used on that revision.
|
||||
$this->drupalGet($private_href4);
|
||||
$assert_session->pageTextContains('You are not authorized to access this page');
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the file in the block with another one.
|
||||
*
|
||||
* @param \Drupal\file\FileInterface $file
|
||||
* The file entity.
|
||||
*/
|
||||
protected function replaceFileInBlock(FileInterface $file) {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->clickContextualLink(static::INLINE_BLOCK_LOCATOR, 'Configure');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$page->pressButton('Remove');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$this->attachFileToBlockForm($file);
|
||||
$page->pressButton('Update');
|
||||
$this->assertDialogClosedAndTextVisible($file->label(), static::INLINE_BLOCK_LOCATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity block with a file.
|
||||
*
|
||||
* @param string $title
|
||||
* The title field value.
|
||||
* @param \Drupal\file\Entity\File $file
|
||||
* The file entity.
|
||||
*/
|
||||
protected function addInlineFileBlockToLayout($title, File $file) {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$page->clickLink('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '.block-categories details:contains(Create new block)'));
|
||||
$this->clickLink('Basic block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->fieldValueEquals('Title', '');
|
||||
$page->findField('Title')->setValue($title);
|
||||
$this->attachFileToBlockForm($file);
|
||||
$page->pressButton('Add Block');
|
||||
$this->assertDialogClosedAndTextVisible($file->label(), static::INLINE_BLOCK_LOCATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a private file.
|
||||
*
|
||||
* @param string $file_name
|
||||
* The file name.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|\Drupal\file\Entity\File
|
||||
* The file entity.
|
||||
*/
|
||||
protected function createPrivateFile($file_name) {
|
||||
// Create a new file entity.
|
||||
$file = File::create([
|
||||
'uid' => 1,
|
||||
'filename' => $file_name,
|
||||
'uri' => "private://$file_name",
|
||||
'filemime' => 'text/plain',
|
||||
'status' => FILE_STATUS_PERMANENT,
|
||||
]);
|
||||
file_put_contents($file->getFileUri(), $this->getFileSecret($file));
|
||||
$file->save();
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts a file is accessible on the page.
|
||||
*
|
||||
* @param \Drupal\file\FileInterface $file
|
||||
* The file entity.
|
||||
*
|
||||
* @return string
|
||||
* The file href.
|
||||
*/
|
||||
protected function assertFileAccessibleOnNode(FileInterface $file) {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$assert_session->linkExists($file->label());
|
||||
$private_href = $page->findLink($file->label())->getAttribute('href');
|
||||
$page->clickLink($file->label());
|
||||
$assert_session->pageTextContains($this->getFileSecret($file));
|
||||
|
||||
// Access file directly.
|
||||
$this->drupalGet($private_href);
|
||||
$assert_session->pageTextContains($this->getFileSecret($file));
|
||||
return $private_href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text secret for a file.
|
||||
*
|
||||
* @param \Drupal\file\FileInterface $file
|
||||
* The file entity.
|
||||
*
|
||||
* @return string
|
||||
* The text secret.
|
||||
*/
|
||||
protected function getFileSecret(FileInterface $file) {
|
||||
return "The secret in {$file->label()}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a file to the block edit form.
|
||||
*
|
||||
* @param \Drupal\file\FileInterface $file
|
||||
* The file to be attached.
|
||||
*/
|
||||
protected function attachFileToBlockForm(FileInterface $file) {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$page->attachFileToField("files[settings_block_form_field_file_0]", $this->fileSystem->realpath($file->getFileUri()));
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$this->assertNotEmpty($assert_session->waitForLink($file->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new revision of the node.
|
||||
*
|
||||
* @param int $node_id
|
||||
* The node id.
|
||||
*/
|
||||
protected function createNewNodeRevision($node_id) {
|
||||
$node = Node::load($node_id);
|
||||
$node->setTitle('Update node');
|
||||
$node->setNewRevision();
|
||||
$node->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,431 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests that the inline block feature works correctly.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class InlineBlockTest extends InlineBlockTestBase {
|
||||
|
||||
/**
|
||||
* Tests adding and editing of inline blocks.
|
||||
*/
|
||||
public function testInlineBlocks() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access contextual links',
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
|
||||
// Enable layout builder.
|
||||
$this->drupalPostForm(
|
||||
static::FIELD_UI_PREFIX . '/display/default',
|
||||
['layout[enabled]' => TRUE],
|
||||
'Save'
|
||||
);
|
||||
$this->clickLink('Manage layout');
|
||||
$assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default');
|
||||
// Add a basic block with the body field set.
|
||||
$this->addInlineBlockToLayout('Block title', 'The DEFAULT block body');
|
||||
$this->assertSaveLayout();
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
$this->drupalGet('node/2');
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
|
||||
// Enable overrides.
|
||||
$this->drupalPostForm(static::FIELD_UI_PREFIX . '/display/default', ['layout[allow_custom]' => TRUE], 'Save');
|
||||
$this->drupalGet('node/1/layout');
|
||||
|
||||
// Confirm the block can be edited.
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->configureInlineBlock('The DEFAULT block body', 'The NEW block body!');
|
||||
$this->assertSaveLayout();
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The NEW block body');
|
||||
$assert_session->pageTextNotContains('The DEFAULT block body');
|
||||
$this->drupalGet('node/2');
|
||||
// Node 2 should use default layout.
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
$assert_session->pageTextNotContains('The NEW block body');
|
||||
|
||||
// Add a basic block with the body field set.
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->addInlineBlockToLayout('2nd Block title', 'The 2nd block body');
|
||||
$this->assertSaveLayout();
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The NEW block body!');
|
||||
$assert_session->pageTextContains('The 2nd block body');
|
||||
$this->drupalGet('node/2');
|
||||
// Node 2 should use default layout.
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
$assert_session->pageTextNotContains('The NEW block body');
|
||||
$assert_session->pageTextNotContains('The 2nd block body');
|
||||
|
||||
// Confirm the block can be edited.
|
||||
$this->drupalGet('node/1/layout');
|
||||
/* @var \Behat\Mink\Element\NodeElement $inline_block_2 */
|
||||
$inline_block_2 = $page->findAll('css', static::INLINE_BLOCK_LOCATOR)[1];
|
||||
$uuid = $inline_block_2->getAttribute('data-layout-block-uuid');
|
||||
$block_css_locator = static::INLINE_BLOCK_LOCATOR . "[data-layout-block-uuid=\"$uuid\"]";
|
||||
$this->configureInlineBlock('The 2nd block body', 'The 2nd NEW block body!', $block_css_locator);
|
||||
$this->assertSaveLayout();
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The NEW block body!');
|
||||
$assert_session->pageTextContains('The 2nd NEW block body!');
|
||||
$this->drupalGet('node/2');
|
||||
// Node 2 should use default layout.
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
$assert_session->pageTextNotContains('The NEW block body!');
|
||||
$assert_session->pageTextNotContains('The 2nd NEW block body!');
|
||||
|
||||
// The default layout entity block should be changed.
|
||||
$this->drupalGet(static::FIELD_UI_PREFIX . '/display-layout/default');
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
// Confirm default layout still only has 1 entity block.
|
||||
$assert_session->elementsCount('css', static::INLINE_BLOCK_LOCATOR, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding a new entity block and then not saving the layout.
|
||||
*
|
||||
* @dataProvider layoutNoSaveProvider
|
||||
*/
|
||||
public function testNoLayoutSave($operation, $no_save_link_text, $confirm_button_text) {
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access contextual links',
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
]));
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->assertEmpty($this->blockStorage->loadMultiple(), 'No entity blocks exist');
|
||||
// Enable layout builder and overrides.
|
||||
$this->drupalPostForm(
|
||||
static::FIELD_UI_PREFIX . '/display/default',
|
||||
['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
|
||||
'Save'
|
||||
);
|
||||
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->addInlineBlockToLayout('Block title', 'The block body');
|
||||
$this->clickLink($no_save_link_text);
|
||||
if ($confirm_button_text) {
|
||||
$page->pressButton($confirm_button_text);
|
||||
}
|
||||
$this->drupalGet('node/1');
|
||||
$this->assertEmpty($this->blockStorage->loadMultiple(), 'No entity blocks were created when layout is canceled.');
|
||||
$assert_session->pageTextNotContains('The block body');
|
||||
|
||||
$this->drupalGet('node/1/layout');
|
||||
|
||||
$this->addInlineBlockToLayout('Block title', 'The block body');
|
||||
$this->assertSaveLayout();
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The block body');
|
||||
$blocks = $this->blockStorage->loadMultiple();
|
||||
$this->assertEquals(count($blocks), 1);
|
||||
/* @var \Drupal\Core\Entity\ContentEntityBase $block */
|
||||
$block = array_pop($blocks);
|
||||
$revision_id = $block->getRevisionId();
|
||||
|
||||
// Confirm the block can be edited.
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->configureInlineBlock('The block body', 'The block updated body');
|
||||
|
||||
$this->clickLink($no_save_link_text);
|
||||
if ($confirm_button_text) {
|
||||
$page->pressButton($confirm_button_text);
|
||||
}
|
||||
$this->drupalGet('node/1');
|
||||
|
||||
$blocks = $this->blockStorage->loadMultiple();
|
||||
// When reverting or canceling the update block should not be on the page.
|
||||
$assert_session->pageTextNotContains('The block updated body');
|
||||
if ($operation === 'cancel') {
|
||||
// When canceling the original block body should appear.
|
||||
$assert_session->pageTextContains('The block body');
|
||||
|
||||
$this->assertEquals(count($blocks), 1);
|
||||
$block = array_pop($blocks);
|
||||
$this->assertEquals($block->getRevisionId(), $revision_id);
|
||||
$this->assertEquals($block->get('body')->getValue()[0]['value'], 'The block body');
|
||||
}
|
||||
else {
|
||||
// The block should not be visible.
|
||||
// Blocks are currently only deleted when the parent entity is deleted.
|
||||
$assert_session->pageTextNotContains('The block body');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for ::testNoLayoutSave().
|
||||
*/
|
||||
public function layoutNoSaveProvider() {
|
||||
return [
|
||||
'cancel' => [
|
||||
'cancel',
|
||||
'Cancel Layout',
|
||||
NULL,
|
||||
],
|
||||
'revert' => [
|
||||
'revert',
|
||||
'Revert to defaults',
|
||||
'Revert',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity blocks revisioning.
|
||||
*/
|
||||
public function testInlineBlocksRevisioning() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access contextual links',
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
'administer nodes',
|
||||
'bypass node access',
|
||||
]));
|
||||
// Enable layout builder and overrides.
|
||||
$this->drupalPostForm(
|
||||
static::FIELD_UI_PREFIX . '/display/default',
|
||||
['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
|
||||
'Save'
|
||||
);
|
||||
$this->drupalGet('node/1/layout');
|
||||
|
||||
// Add an inline block.
|
||||
$this->addInlineBlockToLayout('Block title', 'The DEFAULT block body');
|
||||
$this->assertSaveLayout();
|
||||
$this->drupalGet('node/1');
|
||||
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
|
||||
/** @var \Drupal\node\NodeStorageInterface $node_storage */
|
||||
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$original_revision_id = $node_storage->getLatestRevisionId(1);
|
||||
|
||||
// Create a new revision.
|
||||
$this->drupalGet('node/1/edit');
|
||||
$page->findField('title[0][value]')->setValue('Node updated');
|
||||
$page->pressButton('Save');
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
|
||||
$assert_session->linkExists('Revisions');
|
||||
|
||||
// Update the block.
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->configureInlineBlock('The DEFAULT block body', 'The NEW block body');
|
||||
$this->assertSaveLayout();
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The NEW block body');
|
||||
$assert_session->pageTextNotContains('The DEFAULT block body');
|
||||
|
||||
$revision_url = "node/1/revisions/$original_revision_id";
|
||||
|
||||
// Ensure viewing the previous revision shows the previous block revision.
|
||||
$this->drupalGet("$revision_url/view");
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
$assert_session->pageTextNotContains('The NEW block body');
|
||||
|
||||
// Revert to first revision.
|
||||
$revision_url = "$revision_url/revert";
|
||||
$this->drupalGet($revision_url);
|
||||
$page->pressButton('Revert');
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
$assert_session->pageTextNotContains('The NEW block body');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that entity blocks deleted correctly.
|
||||
*/
|
||||
public function testDeletion() {
|
||||
/** @var \Drupal\Core\Cron $cron */
|
||||
$cron = \Drupal::service('cron');
|
||||
/** @var \Drupal\layout_builder\InlineBlockUsage $usage */
|
||||
$usage = \Drupal::service('inline_block.usage');
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'administer content types',
|
||||
'access contextual links',
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
'administer nodes',
|
||||
'bypass node access',
|
||||
]));
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Enable layout builder.
|
||||
$this->drupalPostForm(
|
||||
static::FIELD_UI_PREFIX . '/display/default',
|
||||
['layout[enabled]' => TRUE],
|
||||
'Save'
|
||||
);
|
||||
// Add a block to default layout.
|
||||
$this->drupalGet(static::FIELD_UI_PREFIX . '/display/default');
|
||||
$this->clickLink('Manage layout');
|
||||
$assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default');
|
||||
$this->addInlineBlockToLayout('Block title', 'The DEFAULT block body');
|
||||
$this->assertSaveLayout();
|
||||
|
||||
$this->assertCount(1, $this->blockStorage->loadMultiple());
|
||||
$default_block_id = $this->getLatestBlockEntityId();
|
||||
|
||||
// Ensure the block shows up on node pages.
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
$this->drupalGet('node/2');
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
|
||||
// Enable overrides.
|
||||
$this->drupalPostForm(static::FIELD_UI_PREFIX . '/display/default', ['layout[allow_custom]' => TRUE], 'Save');
|
||||
|
||||
// Ensure we have 2 copies of the block in node overrides.
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->assertSaveLayout();
|
||||
$node_1_block_id = $this->getLatestBlockEntityId();
|
||||
|
||||
$this->drupalGet('node/2/layout');
|
||||
$this->assertSaveLayout();
|
||||
$node_2_block_id = $this->getLatestBlockEntityId();
|
||||
$this->assertCount(3, $this->blockStorage->loadMultiple());
|
||||
|
||||
$this->drupalGet(static::FIELD_UI_PREFIX . '/display/default');
|
||||
$this->clickLink('Manage layout');
|
||||
$assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default');
|
||||
|
||||
$this->assertNotEmpty($this->blockStorage->load($default_block_id));
|
||||
$this->assertNotEmpty($usage->getUsage($default_block_id));
|
||||
// Remove block from default.
|
||||
$this->removeInlineBlockFromLayout();
|
||||
$this->assertSaveLayout();
|
||||
// Ensure the block in the default was deleted.
|
||||
$this->blockStorage->resetCache([$default_block_id]);
|
||||
$this->assertEmpty($this->blockStorage->load($default_block_id));
|
||||
// Ensure other blocks still exist.
|
||||
$this->assertCount(2, $this->blockStorage->loadMultiple());
|
||||
$this->assertEmpty($usage->getUsage($default_block_id));
|
||||
|
||||
$this->drupalGet('node/1/layout');
|
||||
$assert_session->pageTextContains('The DEFAULT block body');
|
||||
|
||||
$this->removeInlineBlockFromLayout();
|
||||
$this->assertSaveLayout();
|
||||
$cron->run();
|
||||
// Ensure entity block is not deleted because it is needed in revision.
|
||||
$this->assertNotEmpty($this->blockStorage->load($node_1_block_id));
|
||||
$this->assertCount(2, $this->blockStorage->loadMultiple());
|
||||
|
||||
$this->assertNotEmpty($usage->getUsage($node_1_block_id));
|
||||
// Ensure entity block is deleted when node is deleted.
|
||||
$this->drupalGet('node/1/delete');
|
||||
$page->pressButton('Delete');
|
||||
$this->assertEmpty(Node::load(1));
|
||||
$cron->run();
|
||||
$this->assertEmpty($this->blockStorage->load($node_1_block_id));
|
||||
$this->assertEmpty($usage->getUsage($node_1_block_id));
|
||||
$this->assertCount(1, $this->blockStorage->loadMultiple());
|
||||
|
||||
// Add another block to the default.
|
||||
$this->drupalGet(static::FIELD_UI_PREFIX . '/display/default');
|
||||
$this->clickLink('Manage layout');
|
||||
$assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default');
|
||||
$this->addInlineBlockToLayout('Title 2', 'Body 2');
|
||||
$this->assertSaveLayout();
|
||||
$cron->run();
|
||||
$default_block2_id = $this->getLatestBlockEntityId();
|
||||
$this->assertCount(2, $this->blockStorage->loadMultiple());
|
||||
|
||||
// Delete the other node so bundle can be deleted.
|
||||
$this->assertNotEmpty($usage->getUsage($node_2_block_id));
|
||||
$this->drupalGet('node/2/delete');
|
||||
$page->pressButton('Delete');
|
||||
$this->assertEmpty(Node::load(2));
|
||||
$cron->run();
|
||||
// Ensure entity block was deleted.
|
||||
$this->assertEmpty($this->blockStorage->load($node_2_block_id));
|
||||
$this->assertEmpty($usage->getUsage($node_2_block_id));
|
||||
$this->assertCount(1, $this->blockStorage->loadMultiple());
|
||||
|
||||
// Delete the bundle which has the default layout.
|
||||
$this->assertNotEmpty($usage->getUsage($default_block2_id));
|
||||
$this->drupalGet(static::FIELD_UI_PREFIX . '/delete');
|
||||
$page->pressButton('Delete');
|
||||
$cron->run();
|
||||
|
||||
// Ensure the entity block in default is deleted when bundle is deleted.
|
||||
$this->assertEmpty($this->blockStorage->load($default_block2_id));
|
||||
$this->assertEmpty($usage->getUsage($default_block2_id));
|
||||
$this->assertCount(0, $this->blockStorage->loadMultiple());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access to the block edit form of inline blocks.
|
||||
*
|
||||
* This module does not provide links to these forms but in case the paths are
|
||||
* accessed directly they should accessible by users with the
|
||||
* 'configure any layout' permission.
|
||||
*
|
||||
* @see layout_builder_block_content_access()
|
||||
*/
|
||||
public function testAccess() {
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access contextual links',
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
// Enable layout builder and overrides.
|
||||
$this->drupalPostForm(
|
||||
static::FIELD_UI_PREFIX . '/display/default',
|
||||
['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
|
||||
'Save'
|
||||
);
|
||||
|
||||
// Ensure we have 2 copies of the block in node overrides.
|
||||
$this->drupalGet('node/1/layout');
|
||||
$this->addInlineBlockToLayout('Block title', 'Block body');
|
||||
$this->assertSaveLayout();
|
||||
$node_1_block_id = $this->getLatestBlockEntityId();
|
||||
|
||||
$this->drupalGet("block/$node_1_block_id");
|
||||
$assert_session->pageTextNotContains('You are not authorized to access this page');
|
||||
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'administer nodes',
|
||||
]));
|
||||
|
||||
$this->drupalGet("block/$node_1_block_id");
|
||||
$assert_session->pageTextContains('You are not authorized to access this page');
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
]));
|
||||
$this->drupalGet("block/$node_1_block_id");
|
||||
$assert_session->pageTextNotContains('You are not authorized to access this page');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
|
||||
|
||||
use Drupal\block_content\Entity\BlockContentType;
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
use Drupal\Tests\contextual\FunctionalJavascript\ContextualLinkClickTrait;
|
||||
|
||||
/**
|
||||
* Base class for testing inline blocks.
|
||||
*/
|
||||
abstract class InlineBlockTestBase extends WebDriverTestBase {
|
||||
|
||||
use ContextualLinkClickTrait;
|
||||
|
||||
/**
|
||||
* Locator for inline blocks.
|
||||
*/
|
||||
const INLINE_BLOCK_LOCATOR = '.block-inline-blockbasic';
|
||||
|
||||
/**
|
||||
* Path prefix for the field UI for the test bundle.
|
||||
*/
|
||||
const FIELD_UI_PREFIX = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'block_content',
|
||||
'layout_builder',
|
||||
'block',
|
||||
'node',
|
||||
'contextual',
|
||||
// @todo Remove after https://www.drupal.org/project/drupal/issues/2901792.
|
||||
'no_transitions_css',
|
||||
];
|
||||
|
||||
/**
|
||||
* The block storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $blockStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// @todo The Layout Builder UI relies on local tasks; fix in
|
||||
// https://www.drupal.org/project/drupal/issues/2917777.
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
$this->createContentType(['type' => 'bundle_with_section_field', 'new_revision' => TRUE]);
|
||||
$this->createNode([
|
||||
'type' => 'bundle_with_section_field',
|
||||
'title' => 'The node title',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'The node body',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$this->createNode([
|
||||
'type' => 'bundle_with_section_field',
|
||||
'title' => 'The node2 title',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'The node2 body',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$bundle = BlockContentType::create([
|
||||
'id' => 'basic',
|
||||
'label' => 'Basic block',
|
||||
'revision' => 1,
|
||||
]);
|
||||
$bundle->save();
|
||||
block_content_add_body_field($bundle->id());
|
||||
|
||||
$this->blockStorage = $this->container->get('entity_type.manager')->getStorage('block_content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a layout and asserts the message is correct.
|
||||
*/
|
||||
protected function assertSaveLayout() {
|
||||
$assert_session = $this->assertSession();
|
||||
$assert_session->linkExists('Save Layout');
|
||||
// Go to the Save Layout page. Currently there are random test failures if
|
||||
// 'clickLink()' is used.
|
||||
// @todo Convert tests that extend this class to NightWatch tests in
|
||||
// https://www.drupal.org/node/2984161
|
||||
$link = $this->getSession()->getPage()->findLink('Save Layout');
|
||||
$this->drupalGet($link->getAttribute('href'));
|
||||
$this->assertNotEmpty($assert_session->waitForElement('css', '.messages--status'));
|
||||
|
||||
if (stristr($this->getUrl(), 'admin/structure') === FALSE) {
|
||||
$assert_session->pageTextContains('The layout override has been saved.');
|
||||
}
|
||||
else {
|
||||
$assert_session->pageTextContains('The layout has been saved.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latest block entity id.
|
||||
*/
|
||||
protected function getLatestBlockEntityId() {
|
||||
$block_ids = \Drupal::entityQuery('block_content')->sort('id', 'DESC')->range(0, 1)->execute();
|
||||
$block_id = array_pop($block_ids);
|
||||
$this->assertNotEmpty($this->blockStorage->load($block_id));
|
||||
return $block_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an entity block from the layout but does not save the layout.
|
||||
*/
|
||||
protected function removeInlineBlockFromLayout() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$block_text = $page->find('css', static::INLINE_BLOCK_LOCATOR)->getText();
|
||||
$this->assertNotEmpty($block_text);
|
||||
$assert_session->pageTextContains($block_text);
|
||||
$this->clickContextualLink(static::INLINE_BLOCK_LOCATOR, 'Remove block');
|
||||
$assert_session->waitForElement('css', "#drupal-off-canvas input[value='Remove']");
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$page->find('css', '#drupal-off-canvas')->pressButton('Remove');
|
||||
$this->waitForNoElement('#drupal-off-canvas');
|
||||
$this->waitForNoElement(static::INLINE_BLOCK_LOCATOR);
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextNotContains($block_text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity block to the layout.
|
||||
*
|
||||
* @param string $title
|
||||
* The title field value.
|
||||
* @param string $body
|
||||
* The body field value.
|
||||
*/
|
||||
protected function addInlineBlockToLayout($title, $body) {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$page->clickLink('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '.block-categories details:contains(Create new block)'));
|
||||
$this->clickLink('Basic block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$textarea = $assert_session->waitForElement('css', '[name="settings[block_form][body][0][value]"]');
|
||||
$this->assertNotEmpty($textarea);
|
||||
$assert_session->fieldValueEquals('Title', '');
|
||||
$page->findField('Title')->setValue($title);
|
||||
$textarea->setValue($body);
|
||||
$page->pressButton('Add Block');
|
||||
$this->assertDialogClosedAndTextVisible($body, static::INLINE_BLOCK_LOCATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an inline block in the Layout Builder.
|
||||
*
|
||||
* @param string $old_body
|
||||
* The old body field value.
|
||||
* @param string $new_body
|
||||
* The new body field value.
|
||||
* @param string $block_css_locator
|
||||
* The CSS locator to use to select the contextual link.
|
||||
*/
|
||||
protected function configureInlineBlock($old_body, $new_body, $block_css_locator = NULL) {
|
||||
$block_css_locator = $block_css_locator ?: static::INLINE_BLOCK_LOCATOR;
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->clickContextualLink($block_css_locator, 'Configure');
|
||||
$textarea = $assert_session->waitForElementVisible('css', '[name="settings[block_form][body][0][value]"]');
|
||||
$this->assertNotEmpty($textarea);
|
||||
$this->assertSame($old_body, $textarea->getValue());
|
||||
$textarea->setValue($new_body);
|
||||
$page->pressButton('Update');
|
||||
$this->waitForNoElement('#drupal-off-canvas');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$this->assertDialogClosedAndTextVisible($new_body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an element to be removed from the page.
|
||||
*
|
||||
* @param string $selector
|
||||
* CSS selector.
|
||||
* @param int $timeout
|
||||
* (optional) Timeout in milliseconds, defaults to 10000.
|
||||
*
|
||||
* @todo Remove in https://www.drupal.org/node/2892440.
|
||||
*/
|
||||
protected function waitForNoElement($selector, $timeout = 10000) {
|
||||
$condition = "(typeof jQuery !== 'undefined' && jQuery('$selector').length === 0)";
|
||||
$this->assertJsCondition($condition, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the dialog closes and the new text appears on the main canvas.
|
||||
*
|
||||
* @param string $text
|
||||
* The text.
|
||||
* @param string|null $css_locator
|
||||
* The css locator to use inside the main canvas if any.
|
||||
*/
|
||||
protected function assertDialogClosedAndTextVisible($text, $css_locator = NULL) {
|
||||
$assert_session = $this->assertSession();
|
||||
$this->waitForNoElement('#drupal-off-canvas');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->elementNotExists('css', '#drupal-off-canvas');
|
||||
if ($css_locator) {
|
||||
$this->assertNotEmpty($assert_session->waitForElementVisible('css', ".dialog-off-canvas-main-canvas $css_locator:contains('$text')"));
|
||||
}
|
||||
else {
|
||||
$this->assertNotEmpty($assert_session->waitForElementVisible('css', ".dialog-off-canvas-main-canvas:contains('$text')"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
|
||||
/**
|
||||
* Field blocks tests for the override layout.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class ItemLayoutFieldBlockTest extends WebDriverTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'node',
|
||||
'layout_builder',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
|
||||
// We need more then one content type for this test.
|
||||
$this->createContentType(['type' => 'bundle_with_layout_overrides']);
|
||||
$this->createContentType(['type' => 'filler_bundle']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuring a field block for a user field.
|
||||
*/
|
||||
public function testAddAjaxBlock() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Allow overrides for the layout.
|
||||
$this->drupalGet('admin/structure/types/manage/bundle_with_layout_overrides/display/default');
|
||||
$page->checkField('layout[enabled]');
|
||||
$page->checkField('layout[allow_custom]');
|
||||
$page->pressButton('Save');
|
||||
|
||||
// Start by creating a node of type with layout overrides.
|
||||
$node = $this->createNode([
|
||||
'type' => 'bundle_with_layout_overrides',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'The node body',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
// Open single item layout page.
|
||||
$this->drupalGet('node/1/layout');
|
||||
|
||||
// Add a new block.
|
||||
$this->clickLink('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
|
||||
// Validate that only field blocks for layouted bundle are present.
|
||||
$valid_links = $page->findAll('css', 'a[href$="field_block%3Anode%3Abundle_with_layout_overrides%3Abody"]');
|
||||
$this->assertCount(1, $valid_links);
|
||||
$invalid_links = $page->findAll('css', 'a[href$="field_block%3Anode%3Afiller_bundle%3Abody"]');
|
||||
$this->assertCount(0, $invalid_links);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
|
||||
/**
|
||||
* Tests the ability for opting in and out of Layout Builder.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutBuilderOptInTest extends WebDriverTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'node',
|
||||
'field_ui',
|
||||
'block',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// @todo The Layout Builder UI relies on local tasks; fix in
|
||||
// https://www.drupal.org/project/drupal/issues/2917777.
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
// Create one content type before installing Layout Builder and one after.
|
||||
$this->createContentType(['type' => 'before']);
|
||||
$this->container->get('module_installer')->install(['layout_builder']);
|
||||
$this->rebuildAll();
|
||||
$this->createContentType(['type' => 'after']);
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the interaction between the two layout checkboxes.
|
||||
*/
|
||||
public function testCheckboxLogic() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalGet('admin/structure/types/manage/before/display/default');
|
||||
// Both fields are unchecked and allow_custom is disabled and hidden.
|
||||
$assert_session->checkboxNotChecked('layout[enabled]');
|
||||
$assert_session->checkboxNotChecked('layout[allow_custom]');
|
||||
$assert_session->fieldDisabled('layout[allow_custom]');
|
||||
$this->assertFalse($page->findField('layout[allow_custom]')->isVisible());
|
||||
|
||||
// Checking is_enable will show allow_custom.
|
||||
$page->checkField('layout[enabled]');
|
||||
$assert_session->checkboxNotChecked('layout[allow_custom]');
|
||||
$this->assertTrue($page->findField('layout[allow_custom]')->isVisible());
|
||||
$page->pressButton('Save');
|
||||
$assert_session->checkboxChecked('layout[enabled]');
|
||||
$assert_session->checkboxNotChecked('layout[allow_custom]');
|
||||
|
||||
// Check and submit allow_custom.
|
||||
$page->checkField('layout[allow_custom]');
|
||||
$page->pressButton('Save');
|
||||
$assert_session->checkboxChecked('layout[enabled]');
|
||||
$assert_session->checkboxChecked('layout[allow_custom]');
|
||||
|
||||
// Reset the checkboxes.
|
||||
$page->uncheckField('layout[enabled]');
|
||||
$page->pressButton('Save');
|
||||
$page->pressButton('Confirm');
|
||||
$assert_session->checkboxNotChecked('layout[enabled]');
|
||||
$assert_session->checkboxNotChecked('layout[allow_custom]');
|
||||
|
||||
// Check both at the same time.
|
||||
$page->checkField('layout[enabled]');
|
||||
$page->checkField('layout[allow_custom]');
|
||||
$page->pressButton('Save');
|
||||
$assert_session->checkboxChecked('layout[enabled]');
|
||||
$assert_session->checkboxChecked('layout[allow_custom]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the expected default values for enabling Layout Builder.
|
||||
*/
|
||||
public function testDefaultValues() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Both the content type created before and after Layout Builder was
|
||||
// installed is still using the Field UI.
|
||||
$this->drupalGet('admin/structure/types/manage/before/display/default');
|
||||
$assert_session->checkboxNotChecked('layout[enabled]');
|
||||
|
||||
$field_ui_prefix = 'admin/structure/types/manage/after/display/default';
|
||||
$this->drupalGet($field_ui_prefix);
|
||||
$assert_session->checkboxNotChecked('layout[enabled]');
|
||||
$page->checkField('layout[enabled]');
|
||||
$page->pressButton('Save');
|
||||
|
||||
$layout_builder_ui = $this->getPathForFieldBlock('node', 'after', 'default', 'body');
|
||||
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$this->clickLink('Manage layout');
|
||||
// Ensure the body appears once and only once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
|
||||
// Change the body formatter to Trimmed.
|
||||
$this->drupalGet($layout_builder_ui);
|
||||
$assert_session->fieldValueEquals('settings[formatter][type]', 'text_default');
|
||||
$page->selectFieldOption('settings[formatter][type]', 'text_trimmed');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$page->pressButton('Update');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
|
||||
$this->drupalGet($layout_builder_ui);
|
||||
$assert_session->fieldValueEquals('settings[formatter][type]', 'text_trimmed');
|
||||
|
||||
// Disable Layout Builder.
|
||||
$this->drupalPostForm($field_ui_prefix, ['layout[enabled]' => FALSE], 'Save');
|
||||
$page->pressButton('Confirm');
|
||||
|
||||
// The Layout Builder UI is no longer accessible.
|
||||
$this->drupalGet($layout_builder_ui);
|
||||
$assert_session->pageTextContains('You are not authorized to access this page.');
|
||||
|
||||
// The original body formatter is reflected in Field UI.
|
||||
$this->drupalGet($field_ui_prefix);
|
||||
$assert_session->fieldValueEquals('fields[body][type]', 'text_default');
|
||||
|
||||
// Change the body formatter to Summary.
|
||||
$page->selectFieldOption('fields[body][type]', 'text_summary_or_trimmed');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$page->pressButton('Save');
|
||||
$assert_session->fieldValueEquals('fields[body][type]', 'text_summary_or_trimmed');
|
||||
|
||||
// Reactivate Layout Builder.
|
||||
$this->drupalPostForm($field_ui_prefix, ['layout[enabled]' => TRUE], 'Save');
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$this->clickLink('Manage layout');
|
||||
// Ensure the body appears once and only once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
|
||||
// The changed body formatter is reflected in Layout Builder UI.
|
||||
$this->drupalGet($this->getPathForFieldBlock('node', 'after', 'default', 'body'));
|
||||
$assert_session->fieldValueEquals('settings[formatter][type]', 'text_summary_or_trimmed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to update a field block in the UI.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type ID.
|
||||
* @param string $bundle
|
||||
* The bundle.
|
||||
* @param string $view_mode
|
||||
* The view mode.
|
||||
* @param string $field_name
|
||||
* The field name.
|
||||
*
|
||||
* @return string
|
||||
* The path.
|
||||
*/
|
||||
protected function getPathForFieldBlock($entity_type_id, $bundle, $view_mode, $field_name) {
|
||||
$delta = 0;
|
||||
/** @var \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface $display */
|
||||
$display = $this->container->get('entity_type.manager')->getStorage('entity_view_display')->load("$entity_type_id.$bundle.$view_mode");
|
||||
$body_component = NULL;
|
||||
foreach ($display->getSection($delta)->getComponents() as $component) {
|
||||
if ($component->getPluginId() === "field_block:$entity_type_id:$bundle:$field_name") {
|
||||
$body_component = $component;
|
||||
}
|
||||
}
|
||||
$this->assertNotNull($body_component);
|
||||
return 'layout_builder/update/block/defaults/node.after.default/0/content/' . $body_component->getUuid();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FormatterPluginManager;
|
||||
use Drupal\Core\Plugin\Context\EntityContextDefinition;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\layout_builder\Plugin\Block\FieldBlock;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Promise\PromiseInterface;
|
||||
use Prophecy\Promise\ReturnPromise;
|
||||
use Prophecy\Promise\ThrowPromise;
|
||||
use Prophecy\Prophecy\ProphecyInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Plugin\Block\FieldBlock
|
||||
* @group Field
|
||||
*/
|
||||
class FieldBlockTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityFieldManager = $this->prophesize(EntityFieldManagerInterface::class);
|
||||
$this->logger = $this->prophesize(LoggerInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity access.
|
||||
*
|
||||
* @covers ::blockAccess
|
||||
* @dataProvider providerTestBlockAccessNotAllowed
|
||||
*/
|
||||
public function testBlockAccessEntityNotAllowed($expected, $entity_access) {
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$block = $this->getTestBlock($entity);
|
||||
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$entity->access('view', $account->reveal(), TRUE)->willReturn($entity_access);
|
||||
$entity->hasField()->shouldNotBeCalled();
|
||||
|
||||
$access = $block->access($account->reveal(), TRUE);
|
||||
$this->assertSame($expected, $access->isAllowed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for ::testBlockAccessEntityNotAllowed().
|
||||
*/
|
||||
public function providerTestBlockAccessNotAllowed() {
|
||||
$data = [];
|
||||
$data['entity_forbidden'] = [
|
||||
FALSE,
|
||||
AccessResult::forbidden(),
|
||||
];
|
||||
$data['entity_neutral'] = [
|
||||
FALSE,
|
||||
AccessResult::neutral(),
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests unfieldable entity.
|
||||
*
|
||||
* @covers ::blockAccess
|
||||
*/
|
||||
public function testBlockAccessEntityAllowedNotFieldable() {
|
||||
$entity = $this->prophesize(EntityInterface::class);
|
||||
$block = $this->getTestBlock($entity);
|
||||
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$entity->access('view', $account->reveal(), TRUE)->willReturn(AccessResult::allowed());
|
||||
|
||||
$access = $block->access($account->reveal(), TRUE);
|
||||
$this->assertSame(FALSE, $access->isAllowed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests fieldable entity without a particular field.
|
||||
*
|
||||
* @covers ::blockAccess
|
||||
*/
|
||||
public function testBlockAccessEntityAllowedNoField() {
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$block = $this->getTestBlock($entity);
|
||||
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$entity->access('view', $account->reveal(), TRUE)->willReturn(AccessResult::allowed());
|
||||
$entity->hasField('the_field_name')->willReturn(FALSE);
|
||||
$entity->get('the_field_name')->shouldNotBeCalled();
|
||||
|
||||
$access = $block->access($account->reveal(), TRUE);
|
||||
$this->assertSame(FALSE, $access->isAllowed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field access.
|
||||
*
|
||||
* @covers ::blockAccess
|
||||
* @dataProvider providerTestBlockAccessNotAllowed
|
||||
*/
|
||||
public function testBlockAccessEntityAllowedFieldNotAllowed($expected, $field_access) {
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$block = $this->getTestBlock($entity);
|
||||
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$entity->access('view', $account->reveal(), TRUE)->willReturn(AccessResult::allowed());
|
||||
$entity->hasField('the_field_name')->willReturn(TRUE);
|
||||
$field = $this->prophesize(FieldItemListInterface::class);
|
||||
$entity->get('the_field_name')->willReturn($field->reveal());
|
||||
|
||||
$field->access('view', $account->reveal(), TRUE)->willReturn($field_access);
|
||||
$field->isEmpty()->shouldNotBeCalled();
|
||||
|
||||
$access = $block->access($account->reveal(), TRUE);
|
||||
$this->assertSame($expected, $access->isAllowed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests populated vs empty build.
|
||||
*
|
||||
* @covers ::blockAccess
|
||||
* @covers ::build
|
||||
* @dataProvider providerTestBlockAccessEntityAllowedFieldHasValue
|
||||
*/
|
||||
public function testBlockAccessEntityAllowedFieldHasValue($expected, $is_empty) {
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$block = $this->getTestBlock($entity);
|
||||
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$entity->access('view', $account->reveal(), TRUE)->willReturn(AccessResult::allowed());
|
||||
$entity->hasField('the_field_name')->willReturn(TRUE);
|
||||
$field = $this->prophesize(FieldItemListInterface::class);
|
||||
$entity->get('the_field_name')->willReturn($field->reveal());
|
||||
|
||||
$field->access('view', $account->reveal(), TRUE)->willReturn(AccessResult::allowed());
|
||||
$field->isEmpty()->willReturn($is_empty)->shouldBeCalled();
|
||||
|
||||
$access = $block->access($account->reveal(), TRUE);
|
||||
$this->assertSame($expected, $access->isAllowed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for ::testBlockAccessEntityAllowedFieldHasValue().
|
||||
*/
|
||||
public function providerTestBlockAccessEntityAllowedFieldHasValue() {
|
||||
$data = [];
|
||||
$data['empty'] = [
|
||||
FALSE,
|
||||
TRUE,
|
||||
];
|
||||
$data['populated'] = [
|
||||
TRUE,
|
||||
FALSE,
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a block for testing.
|
||||
*
|
||||
* @param \Prophecy\Prophecy\ProphecyInterface $entity_prophecy
|
||||
* An entity prophecy for use as an entity context value.
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param array $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
*
|
||||
* @return \Drupal\layout_builder\Plugin\Block\FieldBlock
|
||||
* The block to test.
|
||||
*/
|
||||
protected function getTestBlock(ProphecyInterface $entity_prophecy, array $configuration = [], array $plugin_definition = []) {
|
||||
$entity_prophecy->getCacheContexts()->willReturn([]);
|
||||
$entity_prophecy->getCacheTags()->willReturn([]);
|
||||
$entity_prophecy->getCacheMaxAge()->willReturn(0);
|
||||
|
||||
$plugin_definition += [
|
||||
'provider' => 'test',
|
||||
'default_formatter' => '',
|
||||
'category' => 'Test',
|
||||
'admin_label' => 'Test Block',
|
||||
'bundles' => ['entity_test'],
|
||||
'context' => [
|
||||
'entity' => EntityContextDefinition::fromEntityTypeId('entity_test')->setLabel('Test'),
|
||||
],
|
||||
];
|
||||
$formatter_manager = $this->prophesize(FormatterPluginManager::class);
|
||||
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||
|
||||
$block = new FieldBlock(
|
||||
$configuration,
|
||||
'field_block:entity_test:entity_test:the_field_name',
|
||||
$plugin_definition,
|
||||
$this->entityFieldManager->reveal(),
|
||||
$formatter_manager->reveal(),
|
||||
$module_handler->reveal(),
|
||||
$this->logger->reveal()
|
||||
);
|
||||
$block->setContextValue('entity', $entity_prophecy->reveal());
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::build
|
||||
* @dataProvider providerTestBuild
|
||||
*/
|
||||
public function testBuild(PromiseInterface $promise, $in_preview, $expected_markup, $log_message = '', $log_arguments = []) {
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$field = $this->prophesize(FieldItemListInterface::class);
|
||||
$entity->get('the_field_name')->willReturn($field->reveal());
|
||||
$entity->in_preview = $in_preview;
|
||||
$field->view(Argument::type('array'))->will($promise);
|
||||
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getLabel()->willReturn('The Field Label');
|
||||
$this->entityFieldManager->getFieldDefinitions('entity_test', 'entity_test')->willReturn(['the_field_name' => $field_definition]);
|
||||
|
||||
if ($log_message) {
|
||||
$this->logger->warning($log_message, $log_arguments)->shouldBeCalled();
|
||||
}
|
||||
else {
|
||||
$this->logger->warning(Argument::cetera())->shouldNotBeCalled();
|
||||
}
|
||||
|
||||
$block = $this->getTestBlock($entity);
|
||||
$expected = [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => 0,
|
||||
],
|
||||
];
|
||||
if ($expected_markup) {
|
||||
$expected['content']['#markup'] = $expected_markup;
|
||||
}
|
||||
|
||||
$actual = $block->build();
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for ::testBuild().
|
||||
*/
|
||||
public function providerTestBuild() {
|
||||
$data = [];
|
||||
$data['array, no preview'] = [
|
||||
new ReturnPromise([['content' => ['#markup' => 'The field value']]]),
|
||||
FALSE,
|
||||
'The field value',
|
||||
];
|
||||
$data['array, preview'] = [
|
||||
new ReturnPromise([['content' => ['#markup' => 'The field value']]]),
|
||||
TRUE,
|
||||
'The field value',
|
||||
];
|
||||
$data['empty array, no preview'] = [
|
||||
new ReturnPromise([[]]),
|
||||
FALSE,
|
||||
'',
|
||||
];
|
||||
$data['empty array, preview'] = [
|
||||
new ReturnPromise([[]]),
|
||||
TRUE,
|
||||
'Placeholder for the "The Field Label" field',
|
||||
];
|
||||
$data['exception, no preview'] = [
|
||||
new ThrowPromise(new \Exception('The exception message')),
|
||||
FALSE,
|
||||
'',
|
||||
'The field "%field" failed to render with the error of "%error".',
|
||||
['%field' => 'the_field_name', '%error' => 'The exception message'],
|
||||
];
|
||||
$data['exception, preview'] = [
|
||||
new ThrowPromise(new \Exception('The exception message')),
|
||||
TRUE,
|
||||
'Placeholder for the "The Field Label" field',
|
||||
'The field "%field" failed to render with the error of "%error".',
|
||||
['%field' => 'the_field_name', '%error' => 'The exception message'],
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests Layout Builder's compatibility with existing systems.
|
||||
*/
|
||||
abstract class LayoutBuilderCompatibilityTestBase extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'layout_discovery',
|
||||
];
|
||||
|
||||
/**
|
||||
* The entity view display.
|
||||
*
|
||||
* @var \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface
|
||||
*/
|
||||
protected $display;
|
||||
|
||||
/**
|
||||
* The entity being rendered.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\FieldableEntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_base_field_display');
|
||||
$this->installConfig(['filter']);
|
||||
$this->installSchema('system', ['key_value_expire']);
|
||||
|
||||
// Set up a non-admin user that is allowed to view test entities.
|
||||
\Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['view test entity']));
|
||||
|
||||
\Drupal::service('theme_handler')->install(['classy']);
|
||||
$this->config('system.theme')->set('default', 'classy')->save();
|
||||
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test_base_field_display',
|
||||
'field_name' => 'test_field_display_configurable',
|
||||
'type' => 'boolean',
|
||||
]);
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test_base_field_display',
|
||||
'label' => 'FieldConfig with configurable display',
|
||||
])->save();
|
||||
|
||||
$this->display = EntityViewDisplay::create([
|
||||
'targetEntityType' => 'entity_test_base_field_display',
|
||||
'bundle' => 'entity_test_base_field_display',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
]);
|
||||
$this->display
|
||||
->setComponent('test_field_display_configurable', ['weight' => 5])
|
||||
->save();
|
||||
|
||||
// Create an entity with fields that are configurable and non-configurable.
|
||||
$entity_storage = $this->container->get('entity_type.manager')->getStorage('entity_test_base_field_display');
|
||||
// @todo Remove langcode workarounds after resolving
|
||||
// https://www.drupal.org/node/2915034.
|
||||
$this->entity = $entity_storage->createWithSampleValues('entity_test_base_field_display', [
|
||||
'langcode' => 'en',
|
||||
'langcode_default' => TRUE,
|
||||
]);
|
||||
$this->entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the Layout Builder.
|
||||
*
|
||||
* Also configures and reloads the entity display.
|
||||
*/
|
||||
protected function installLayoutBuilder() {
|
||||
$this->container->get('module_installer')->install(['layout_builder']);
|
||||
$this->refreshServices();
|
||||
|
||||
$this->display = $this->reloadEntity($this->display);
|
||||
$this->display->enableLayoutBuilder()->save();
|
||||
$this->entity = $this->reloadEntity($this->entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables overrides for the display and reloads the entity.
|
||||
*/
|
||||
protected function enableOverrides() {
|
||||
$this->display->setOverridable()->save();
|
||||
$this->entity = $this->reloadEntity($this->entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the rendered entity has the correct fields.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to render.
|
||||
* @param array $attributes
|
||||
* An array of field attributes to assert.
|
||||
*/
|
||||
protected function assertFieldAttributes(EntityInterface $entity, array $attributes) {
|
||||
$view_builder = $this->container->get('entity_type.manager')->getViewBuilder($entity->getEntityTypeId());
|
||||
$build = $view_builder->view($entity);
|
||||
$this->render($build);
|
||||
|
||||
$actual = array_map(function (\SimpleXMLElement $element) {
|
||||
return (string) $element->attributes();
|
||||
}, $this->cssSelect('.field'));
|
||||
$this->assertSame($attributes, $actual);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\Core\Config\Schema\SchemaIncompleteException;
|
||||
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutBuilderEntityViewDisplayTest extends SectionStorageTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSectionStorage(array $section_data) {
|
||||
$display = LayoutBuilderEntityViewDisplay::create([
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
'third_party_settings' => [
|
||||
'layout_builder' => [
|
||||
'sections' => $section_data,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$display->save();
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that configuration schema enforces valid values.
|
||||
*/
|
||||
public function testInvalidConfiguration() {
|
||||
$this->setExpectedException(SchemaIncompleteException::class);
|
||||
$this->sectionStorage->getSection(0)->getComponent('first-uuid')->setConfiguration(['id' => 'foo', 'bar' => 'baz']);
|
||||
$this->sectionStorage->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\layout_builder\Section;
|
||||
|
||||
/**
|
||||
* Ensures that Layout Builder and Field Layout are compatible with each other.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutBuilderFieldLayoutCompatibilityTest extends LayoutBuilderCompatibilityTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'field_layout',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->display
|
||||
->setLayoutId('layout_twocol')
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the compatibility of Layout Builder and Field Layout.
|
||||
*/
|
||||
public function testCompatibility() {
|
||||
// Ensure that the configurable field is shown in the correct region and
|
||||
// that the non-configurable field is shown outside the layout.
|
||||
$expected_fields = [
|
||||
'field field--name-name field--type-string field--label-hidden field__item',
|
||||
'field field--name-test-field-display-configurable field--type-boolean field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-configurable field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-non-configurable field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-multiple field--type-text field--label-above',
|
||||
];
|
||||
$this->assertFieldAttributes($this->entity, $expected_fields);
|
||||
$this->assertNotEmpty($this->cssSelect('.layout__region--first .field--name-test-display-configurable'));
|
||||
$this->assertNotEmpty($this->cssSelect('.layout__region--first .field--name-test-field-display-configurable'));
|
||||
$this->assertNotEmpty($this->cssSelect('.field--name-test-display-non-configurable'));
|
||||
$this->assertEmpty($this->cssSelect('.layout__region .field--name-test-display-non-configurable'));
|
||||
|
||||
$this->installLayoutBuilder();
|
||||
|
||||
// Without using Layout Builder for an override, the result has not changed.
|
||||
$this->assertFieldAttributes($this->entity, $expected_fields);
|
||||
|
||||
// Add a layout override.
|
||||
$this->enableOverrides();
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $this->entity->get('layout_builder__layout');
|
||||
$field_list->appendSection(new Section('layout_onecol'));
|
||||
$this->entity->save();
|
||||
|
||||
// The rendered entity has now changed. The non-configurable field is shown
|
||||
// outside the layout, the configurable field is not shown at all, and the
|
||||
// layout itself is rendered (but empty).
|
||||
$new_expected_fields = [
|
||||
'field field--name-name field--type-string field--label-hidden field__item',
|
||||
'clearfix text-formatted field field--name-test-display-non-configurable field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-multiple field--type-text field--label-above',
|
||||
];
|
||||
$this->assertFieldAttributes($this->entity, $new_expected_fields);
|
||||
$this->assertNotEmpty($this->cssSelect('.layout--onecol'));
|
||||
|
||||
// Removing the layout restores the original rendering of the entity.
|
||||
$field_list->removeSection(0);
|
||||
$this->entity->save();
|
||||
$this->assertFieldAttributes($this->entity, $expected_fields);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\layout_builder\Section;
|
||||
|
||||
/**
|
||||
* Ensures that Layout Builder and core EntityViewDisplays are compatible.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutBuilderInstallTest extends LayoutBuilderCompatibilityTestBase {
|
||||
|
||||
/**
|
||||
* Tests the compatibility of Layout Builder with existing entity displays.
|
||||
*/
|
||||
public function testCompatibility() {
|
||||
// Ensure that the fields are shown.
|
||||
$expected_fields = [
|
||||
'field field--name-name field--type-string field--label-hidden field__item',
|
||||
'field field--name-test-field-display-configurable field--type-boolean field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-configurable field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-non-configurable field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-multiple field--type-text field--label-above',
|
||||
];
|
||||
$this->assertFieldAttributes($this->entity, $expected_fields);
|
||||
|
||||
$this->installLayoutBuilder();
|
||||
|
||||
// Without using Layout Builder for an override, the result has not changed.
|
||||
$this->assertFieldAttributes($this->entity, $expected_fields);
|
||||
|
||||
// Add a layout override.
|
||||
$this->enableOverrides();
|
||||
$this->entity = $this->reloadEntity($this->entity);
|
||||
$this->entity->get('layout_builder__layout')->appendSection(new Section('layout_onecol'));
|
||||
$this->entity->save();
|
||||
|
||||
// The rendered entity has now changed. The non-configurable field is shown
|
||||
// outside the layout, the configurable field is not shown at all, and the
|
||||
// layout itself is rendered (but empty).
|
||||
$new_expected_fields = [
|
||||
'field field--name-name field--type-string field--label-hidden field__item',
|
||||
'clearfix text-formatted field field--name-test-display-non-configurable field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-multiple field--type-text field--label-above',
|
||||
];
|
||||
$this->assertFieldAttributes($this->entity, $new_expected_fields);
|
||||
$this->assertNotEmpty($this->cssSelect('.layout--onecol'));
|
||||
|
||||
// Removing the layout restores the original rendering of the entity.
|
||||
$this->entity->get('layout_builder__layout')->removeSection(0);
|
||||
$this->entity->save();
|
||||
$this->assertFieldAttributes($this->entity, $expected_fields);
|
||||
|
||||
// Test that adding a new field after Layout Builder has been installed will
|
||||
// add the new field to the default region of the first section.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test_base_field_display',
|
||||
'field_name' => 'test_field_display_post_install',
|
||||
'type' => 'text',
|
||||
]);
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test_base_field_display',
|
||||
'label' => 'FieldConfig with configurable display',
|
||||
])->save();
|
||||
|
||||
$this->entity = $this->reloadEntity($this->entity);
|
||||
$this->entity->test_field_display_post_install = 'Test string';
|
||||
$this->entity->save();
|
||||
|
||||
$this->display = $this->reloadEntity($this->display);
|
||||
$this->display
|
||||
->setComponent('test_field_display_post_install', ['weight' => 50])
|
||||
->save();
|
||||
$new_expected_fields = [
|
||||
'field field--name-name field--type-string field--label-hidden field__item',
|
||||
'field field--name-test-field-display-configurable field--type-boolean field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-configurable field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-field-display-post-install field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-non-configurable field--type-text field--label-above',
|
||||
'clearfix text-formatted field field--name-test-display-multiple field--type-text field--label-above',
|
||||
];
|
||||
$this->assertFieldAttributes($this->entity, $new_expected_fields);
|
||||
$this->assertNotEmpty($this->cssSelect('.layout--onecol'));
|
||||
$this->assertText('Test string');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestBaseFieldDisplay;
|
||||
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
|
||||
|
||||
/**
|
||||
* Tests the field type for Layout Sections.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\layout_builder\Field\LayoutSectionItemList
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutSectionItemListTest extends SectionStorageTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'field',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSectionStorage(array $section_data) {
|
||||
$this->installEntitySchema('entity_test_base_field_display');
|
||||
LayoutBuilderEntityViewDisplay::create([
|
||||
'targetEntityType' => 'entity_test_base_field_display',
|
||||
'bundle' => 'entity_test_base_field_display',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
])
|
||||
->enableLayoutBuilder()
|
||||
->setOverridable()
|
||||
->save();
|
||||
|
||||
array_map(function ($row) {
|
||||
return ['section' => $row];
|
||||
}, $section_data);
|
||||
$entity = EntityTestBaseFieldDisplay::create([
|
||||
'name' => 'The test entity',
|
||||
'layout_builder__layout' => $section_data,
|
||||
]);
|
||||
$entity->save();
|
||||
return $entity->get('layout_builder__layout');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
|
||||
/**
|
||||
* Provides a base class for testing implementations of section storage.
|
||||
*/
|
||||
abstract class SectionStorageTestBase extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'layout_builder',
|
||||
'layout_discovery',
|
||||
'layout_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The section storage implementation.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionStorageInterface
|
||||
*/
|
||||
protected $sectionStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', ['key_value_expire']);
|
||||
|
||||
$section_data = [
|
||||
new Section('layout_test_plugin', [], [
|
||||
'first-uuid' => new SectionComponent('first-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
];
|
||||
$this->sectionStorage = $this->getSectionStorage($section_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the section storage entity.
|
||||
*
|
||||
* @param array $section_data
|
||||
* An array of section data.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* The entity.
|
||||
*/
|
||||
abstract protected function getSectionStorage(array $section_data);
|
||||
|
||||
/**
|
||||
* @covers ::getSections
|
||||
*/
|
||||
public function testGetSections() {
|
||||
$expected = [
|
||||
new Section('layout_test_plugin', [], [
|
||||
'first-uuid' => new SectionComponent('first-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
];
|
||||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getSection
|
||||
*/
|
||||
public function testGetSection() {
|
||||
$this->assertInstanceOf(Section::class, $this->sectionStorage->getSection(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getSection
|
||||
*/
|
||||
public function testGetSectionInvalidDelta() {
|
||||
$this->setExpectedException(\OutOfBoundsException::class, 'Invalid delta "2"');
|
||||
$this->sectionStorage->getSection(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertSection
|
||||
*/
|
||||
public function testInsertSection() {
|
||||
$expected = [
|
||||
new Section('layout_test_plugin', [], [
|
||||
'first-uuid' => new SectionComponent('first-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
new Section('setting_1'),
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
];
|
||||
|
||||
$this->sectionStorage->insertSection(1, new Section('setting_1'));
|
||||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::appendSection
|
||||
*/
|
||||
public function testAppendSection() {
|
||||
$expected = [
|
||||
new Section('layout_test_plugin', [], [
|
||||
'first-uuid' => new SectionComponent('first-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
new Section('foo'),
|
||||
];
|
||||
|
||||
$this->sectionStorage->appendSection(new Section('foo'));
|
||||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::removeSection
|
||||
*/
|
||||
public function testRemoveSection() {
|
||||
$expected = [
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content', ['id' => 'foo']),
|
||||
]),
|
||||
];
|
||||
|
||||
$this->sectionStorage->removeSection(0);
|
||||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the field list has the expected sections.
|
||||
*
|
||||
* @param \Drupal\layout_builder\Section[] $expected
|
||||
* The expected sections.
|
||||
*/
|
||||
protected function assertSections(array $expected) {
|
||||
$result = $this->sectionStorage->getSections();
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertSame(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\block_content\Access\RefinableDependentAccessInterface;
|
||||
use Drupal\Component\Plugin\Context\ContextInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed;
|
||||
use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent;
|
||||
use Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray
|
||||
* @group layout_builder
|
||||
*/
|
||||
class BlockComponentRenderArrayTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The block plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Block\BlockManagerInterface
|
||||
*/
|
||||
protected $blockManager;
|
||||
|
||||
/**
|
||||
* Dataprovider for test functions that should test block types.
|
||||
*/
|
||||
public function providerBlockTypes() {
|
||||
return [
|
||||
[TRUE],
|
||||
[FALSE],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->blockManager = $this->prophesize(BlockManagerInterface::class);
|
||||
$this->account = $this->prophesize(AccountInterface::class);
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('plugin.manager.block', $this->blockManager->reveal());
|
||||
$container->set('context.handler', $this->prophesize(ContextHandlerInterface::class));
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onBuildRender
|
||||
*
|
||||
* @dataProvider providerBlockTypes
|
||||
*/
|
||||
public function testOnBuildRender($refinable_dependent_access) {
|
||||
$contexts = [];
|
||||
if ($refinable_dependent_access) {
|
||||
$block = $this->prophesize(TestBlockPluginWithRefinableDependentAccessInterface::class);
|
||||
$layout_entity = $this->prophesize(EntityInterface::class);
|
||||
$layout_entity = $layout_entity->reveal();
|
||||
$context = $this->prophesize(ContextInterface::class);
|
||||
$context->getContextValue()->willReturn($layout_entity);
|
||||
$contexts['layout_builder.entity'] = $context->reveal();
|
||||
|
||||
$block->setAccessDependency($layout_entity)->shouldBeCalled();
|
||||
}
|
||||
else {
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
}
|
||||
$access_result = AccessResult::allowed();
|
||||
$block->access($this->account->reveal(), TRUE)->willReturn($access_result)->shouldBeCalled();
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn(['test']);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
$block->getConfiguration()->willReturn([]);
|
||||
$block->getPluginId()->willReturn('block_plugin_id');
|
||||
$block->getBaseId()->willReturn('block_plugin_id');
|
||||
$block->getDerivativeId()->willReturn(NULL);
|
||||
|
||||
$block_content = ['#markup' => 'The block content.'];
|
||||
$block->build()->willReturn($block_content);
|
||||
$this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($block->reveal());
|
||||
|
||||
$component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']);
|
||||
$in_preview = FALSE;
|
||||
$event = new SectionComponentBuildRenderArrayEvent($component, $contexts, $in_preview);
|
||||
|
||||
$subscriber = new BlockComponentRenderArray($this->account->reveal());
|
||||
|
||||
$expected_build = [
|
||||
'#theme' => 'block',
|
||||
'#weight' => 0,
|
||||
'#configuration' => [],
|
||||
'#plugin_id' => 'block_plugin_id',
|
||||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => $block_content,
|
||||
];
|
||||
|
||||
$expected_cache = $expected_build + [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['test'],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
|
||||
$subscriber->onBuildRender($event);
|
||||
$result = $event->getBuild();
|
||||
$this->assertEquals($expected_build, $result);
|
||||
$event->getCacheableMetadata()->applyTo($result);
|
||||
$this->assertEquals($expected_cache, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onBuildRender
|
||||
*
|
||||
* @dataProvider providerBlockTypes
|
||||
*/
|
||||
public function testOnBuildRenderDenied($refinable_dependent_access) {
|
||||
$contexts = [];
|
||||
if ($refinable_dependent_access) {
|
||||
$block = $this->prophesize(TestBlockPluginWithRefinableDependentAccessInterface::class);
|
||||
|
||||
$layout_entity = $this->prophesize(EntityInterface::class);
|
||||
$layout_entity = $layout_entity->reveal();
|
||||
$context = $this->prophesize(ContextInterface::class);
|
||||
$context->getContextValue()->willReturn($layout_entity);
|
||||
$contexts['layout_builder.entity'] = $context->reveal();
|
||||
|
||||
$block->setAccessDependency($layout_entity)->shouldBeCalled();
|
||||
}
|
||||
else {
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
}
|
||||
|
||||
$access_result = AccessResult::forbidden();
|
||||
$block->access($this->account->reveal(), TRUE)->willReturn($access_result)->shouldBeCalled();
|
||||
$block->getCacheContexts()->shouldNotBeCalled();
|
||||
$block->getCacheTags()->shouldNotBeCalled();
|
||||
$block->getCacheMaxAge()->shouldNotBeCalled();
|
||||
$block->getConfiguration()->shouldNotBeCalled();
|
||||
$block->getPluginId()->shouldNotBeCalled();
|
||||
$block->getBaseId()->shouldNotBeCalled();
|
||||
$block->getDerivativeId()->shouldNotBeCalled();
|
||||
|
||||
$block_content = ['#markup' => 'The block content.'];
|
||||
$block->build()->willReturn($block_content);
|
||||
$this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($block->reveal());
|
||||
|
||||
$component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']);
|
||||
$in_preview = FALSE;
|
||||
$event = new SectionComponentBuildRenderArrayEvent($component, $contexts, $in_preview);
|
||||
|
||||
$subscriber = new BlockComponentRenderArray($this->account->reveal());
|
||||
|
||||
$expected_build = [];
|
||||
$expected_cache = [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
|
||||
$subscriber->onBuildRender($event);
|
||||
$result = $event->getBuild();
|
||||
$this->assertEquals($expected_build, $result);
|
||||
$event->getCacheableMetadata()->applyTo($result);
|
||||
$this->assertEquals($expected_cache, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onBuildRender
|
||||
*
|
||||
* @dataProvider providerBlockTypes
|
||||
*/
|
||||
public function testOnBuildRenderInPreview($refinable_dependent_access) {
|
||||
$contexts = [];
|
||||
if ($refinable_dependent_access) {
|
||||
$block = $this->prophesize(TestBlockPluginWithRefinableDependentAccessInterface::class);
|
||||
$block->setAccessDependency(new LayoutPreviewAccessAllowed())->shouldBeCalled();
|
||||
|
||||
$layout_entity = $this->prophesize(EntityInterface::class);
|
||||
$layout_entity = $layout_entity->reveal();
|
||||
$layout_entity->in_preview = TRUE;
|
||||
$context = $this->prophesize(ContextInterface::class);
|
||||
$context->getContextValue()->willReturn($layout_entity);
|
||||
$contexts['layout_builder.entity'] = $context->reveal();
|
||||
}
|
||||
else {
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
}
|
||||
|
||||
$block->access($this->account->reveal(), TRUE)->shouldNotBeCalled();
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn(['test']);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
$block->getConfiguration()->willReturn([]);
|
||||
$block->getPluginId()->willReturn('block_plugin_id');
|
||||
$block->getBaseId()->willReturn('block_plugin_id');
|
||||
$block->getDerivativeId()->willReturn(NULL);
|
||||
|
||||
$block_content = ['#markup' => 'The block content.'];
|
||||
$block->build()->willReturn($block_content);
|
||||
$this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($block->reveal());
|
||||
|
||||
$component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']);
|
||||
$in_preview = TRUE;
|
||||
$event = new SectionComponentBuildRenderArrayEvent($component, $contexts, $in_preview);
|
||||
|
||||
$subscriber = new BlockComponentRenderArray($this->account->reveal());
|
||||
|
||||
$expected_build = [
|
||||
'#theme' => 'block',
|
||||
'#weight' => 0,
|
||||
'#configuration' => [],
|
||||
'#plugin_id' => 'block_plugin_id',
|
||||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => $block_content,
|
||||
];
|
||||
|
||||
$expected_cache = $expected_build + [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['test'],
|
||||
'max-age' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
$subscriber->onBuildRender($event);
|
||||
$result = $event->getBuild();
|
||||
$this->assertEquals($expected_build, $result);
|
||||
$event->getCacheableMetadata()->applyTo($result);
|
||||
$this->assertEquals($expected_cache, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onBuildRender
|
||||
*/
|
||||
public function testOnBuildRenderNoBlock() {
|
||||
$this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn(NULL);
|
||||
|
||||
$component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']);
|
||||
$contexts = [];
|
||||
$in_preview = FALSE;
|
||||
$event = new SectionComponentBuildRenderArrayEvent($component, $contexts, $in_preview);
|
||||
|
||||
$subscriber = new BlockComponentRenderArray($this->account->reveal());
|
||||
|
||||
$expected_build = [];
|
||||
$expected_cache = [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
|
||||
$subscriber->onBuildRender($event);
|
||||
$result = $event->getBuild();
|
||||
$this->assertEquals($expected_build, $result);
|
||||
$event->getCacheableMetadata()->applyTo($result);
|
||||
$this->assertEquals($expected_cache, $result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test interface for dependent access block plugins.
|
||||
*/
|
||||
interface TestBlockPluginWithRefinableDependentAccessInterface extends BlockPluginInterface, RefinableDependentAccessInterface {
|
||||
|
||||
}
|
|
@ -0,0 +1,426 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityType;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\layout_builder\Entity\LayoutBuilderSampleEntityGenerator;
|
||||
use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage;
|
||||
use Drupal\layout_builder\SectionStorage\SectionStorageDefinition;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class DefaultsSectionStorageTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The plugin.
|
||||
*
|
||||
* @var \Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
$entity_type_bundle_info = $this->prophesize(EntityTypeBundleInfoInterface::class);
|
||||
$sample_entity_generator = $this->prophesize(LayoutBuilderSampleEntityGenerator::class);
|
||||
|
||||
$definition = new SectionStorageDefinition([
|
||||
'id' => 'defaults',
|
||||
'class' => DefaultsSectionStorage::class,
|
||||
]);
|
||||
$this->plugin = new DefaultsSectionStorage([], '', $definition, $this->entityTypeManager->reveal(), $entity_type_bundle_info->reveal(), $sample_entity_generator->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getThirdPartySetting
|
||||
* @covers ::setThirdPartySetting
|
||||
*/
|
||||
public function testThirdPartySettings() {
|
||||
// Set an initial value on the section list.
|
||||
$section_list = $this->prophesize(LayoutEntityDisplayInterface::class);
|
||||
$section_list->getThirdPartySetting('the_module', 'the_key', NULL)->willReturn('value 1');
|
||||
$this->plugin->setSectionList($section_list->reveal());
|
||||
|
||||
// The plugin returns the initial value.
|
||||
$this->assertSame('value 1', $this->plugin->getThirdPartySetting('the_module', 'the_key'));
|
||||
|
||||
// When the section list is updated, also update the result returned.
|
||||
$section_list->setThirdPartySetting('the_module', 'the_key', 'value 2')->shouldBeCalled()->will(function ($args) {
|
||||
$this->getThirdPartySetting('the_module', 'the_key', NULL)->willReturn($args[2]);
|
||||
});
|
||||
|
||||
// Update the plugin value.
|
||||
$this->plugin->setThirdPartySetting('the_module', 'the_key', 'value 2');
|
||||
// Assert that the returned value matches.
|
||||
$this->assertSame('value 2', $this->plugin->getThirdPartySetting('the_module', 'the_key'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::extractIdFromRoute
|
||||
*
|
||||
* @dataProvider providerTestExtractIdFromRoute
|
||||
*/
|
||||
public function testExtractIdFromRoute($expected, $value, array $defaults) {
|
||||
$result = $this->plugin->extractIdFromRoute($value, [], 'the_parameter_name', $defaults);
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for ::testExtractIdFromRoute().
|
||||
*/
|
||||
public function providerTestExtractIdFromRoute() {
|
||||
$data = [];
|
||||
$data['with value'] = [
|
||||
'foo.bar.baz',
|
||||
'foo.bar.baz',
|
||||
[],
|
||||
];
|
||||
$data['empty value, without bundle'] = [
|
||||
'my_entity_type.bundle_name.default',
|
||||
'',
|
||||
[
|
||||
'entity_type_id' => 'my_entity_type',
|
||||
'view_mode_name' => 'default',
|
||||
'bundle_key' => 'my_bundle',
|
||||
'my_bundle' => 'bundle_name',
|
||||
],
|
||||
];
|
||||
$data['empty value, with bundle'] = [
|
||||
'my_entity_type.bundle_name.default',
|
||||
'',
|
||||
[
|
||||
'entity_type_id' => 'my_entity_type',
|
||||
'view_mode_name' => 'default',
|
||||
'bundle' => 'bundle_name',
|
||||
],
|
||||
];
|
||||
$data['without value, empty defaults'] = [
|
||||
NULL,
|
||||
'',
|
||||
[],
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getSectionListFromId
|
||||
*
|
||||
* @dataProvider providerTestGetSectionListFromId
|
||||
*/
|
||||
public function testGetSectionListFromId($success, $expected_entity_id, $value) {
|
||||
if ($expected_entity_id) {
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
$entity_storage->load($expected_entity_id)->willReturn('the_return_value');
|
||||
|
||||
$this->entityTypeManager->getDefinition('entity_view_display')->willReturn(new EntityType(['id' => 'entity_view_display']));
|
||||
$this->entityTypeManager->getStorage('entity_view_display')->willReturn($entity_storage->reveal());
|
||||
}
|
||||
else {
|
||||
$this->entityTypeManager->getDefinition('entity_view_display')->shouldNotBeCalled();
|
||||
$this->entityTypeManager->getStorage('entity_view_display')->shouldNotBeCalled();
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$this->setExpectedException(\InvalidArgumentException::class);
|
||||
}
|
||||
|
||||
$result = $this->plugin->getSectionListFromId($value);
|
||||
if ($success) {
|
||||
$this->assertEquals('the_return_value', $result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for ::testGetSectionListFromId().
|
||||
*/
|
||||
public function providerTestGetSectionListFromId() {
|
||||
$data = [];
|
||||
$data['with value'] = [
|
||||
TRUE,
|
||||
'foo.bar.baz',
|
||||
'foo.bar.baz',
|
||||
];
|
||||
$data['without value, empty defaults'] = [
|
||||
FALSE,
|
||||
NULL,
|
||||
'',
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getSectionListFromId
|
||||
*/
|
||||
public function testGetSectionListFromIdCreate() {
|
||||
$expected = 'the_return_value';
|
||||
$value = 'foo.bar.baz';
|
||||
$expected_create_values = [
|
||||
'targetEntityType' => 'foo',
|
||||
'bundle' => 'bar',
|
||||
'mode' => 'baz',
|
||||
'status' => TRUE,
|
||||
];
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
$entity_storage->load($value)->willReturn(NULL);
|
||||
$entity_storage->create($expected_create_values)->willReturn($expected);
|
||||
|
||||
$this->entityTypeManager->getDefinition('entity_view_display')->willReturn(new EntityType(['id' => 'entity_view_display']));
|
||||
$this->entityTypeManager->getStorage('entity_view_display')->willReturn($entity_storage->reveal());
|
||||
|
||||
$result = $this->plugin->getSectionListFromId($value);
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildRoutes
|
||||
* @covers ::getEntityTypes
|
||||
*/
|
||||
public function testBuildRoutes() {
|
||||
$entity_types = [];
|
||||
|
||||
$not_fieldable = $this->prophesize(EntityTypeInterface::class);
|
||||
$not_fieldable->entityClassImplements(FieldableEntityInterface::class)->willReturn(FALSE);
|
||||
$entity_types['not_fieldable'] = $not_fieldable->reveal();
|
||||
|
||||
$no_view_builder = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_view_builder->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_view_builder->hasViewBuilderClass()->willReturn(FALSE);
|
||||
$entity_types['no_view_builder'] = $no_view_builder->reveal();
|
||||
|
||||
$no_field_ui_route = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_field_ui_route->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_field_ui_route->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$no_field_ui_route->get('field_ui_base_route')->willReturn(NULL);
|
||||
$entity_types['no_field_ui_route'] = $no_field_ui_route->reveal();
|
||||
|
||||
$unknown_field_ui_route = $this->prophesize(EntityTypeInterface::class);
|
||||
$unknown_field_ui_route->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$unknown_field_ui_route->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$unknown_field_ui_route->get('field_ui_base_route')->willReturn('unknown');
|
||||
$entity_types['unknown_field_ui_route'] = $unknown_field_ui_route->reveal();
|
||||
|
||||
$with_bundle_key = $this->prophesize(EntityTypeInterface::class);
|
||||
$with_bundle_key->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$with_bundle_key->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$with_bundle_key->get('field_ui_base_route')->willReturn('known');
|
||||
$with_bundle_key->hasKey('bundle')->willReturn(TRUE);
|
||||
$with_bundle_key->getBundleEntityType()->willReturn('my_bundle_type');
|
||||
$entity_types['with_bundle_key'] = $with_bundle_key->reveal();
|
||||
|
||||
$with_bundle_parameter = $this->prophesize(EntityTypeInterface::class);
|
||||
$with_bundle_parameter->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$with_bundle_parameter->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$with_bundle_parameter->get('field_ui_base_route')->willReturn('with_bundle');
|
||||
$entity_types['with_bundle_parameter'] = $with_bundle_parameter->reveal();
|
||||
$this->entityTypeManager->getDefinitions()->willReturn($entity_types);
|
||||
|
||||
$expected = [
|
||||
'known' => new Route('/admin/entity/whatever', [], [], ['_admin_route' => TRUE]),
|
||||
'with_bundle' => new Route('/admin/entity/{bundle}'),
|
||||
'layout_builder.defaults.with_bundle_key.view' => new Route(
|
||||
'/admin/entity/whatever/display-layout/{view_mode_name}',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_key',
|
||||
'bundle_key' => 'my_bundle_type',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'is_rebuilding' => FALSE,
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::layout',
|
||||
'_title_callback' => '\Drupal\layout_builder\Controller\LayoutBuilderController::title',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_key display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_key.save' => new Route(
|
||||
'/admin/entity/whatever/display-layout/{view_mode_name}/save',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_key',
|
||||
'bundle_key' => 'my_bundle_type',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_key display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_key.cancel' => new Route(
|
||||
'/admin/entity/whatever/display-layout/{view_mode_name}/cancel',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_key',
|
||||
'bundle_key' => 'my_bundle_type',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::cancelLayout',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_key display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_key.disable' => new Route(
|
||||
'/admin/entity/whatever/display-layout/{view_mode_name}/disable',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_key',
|
||||
'bundle_key' => 'my_bundle_type',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_form' => '\Drupal\layout_builder\Form\LayoutBuilderDisableForm',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_key display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_parameter.view' => new Route(
|
||||
'/admin/entity/{bundle}/display-layout/{view_mode_name}',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_parameter',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'is_rebuilding' => FALSE,
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::layout',
|
||||
'_title_callback' => '\Drupal\layout_builder\Controller\LayoutBuilderController::title',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_parameter display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_parameter.save' => new Route(
|
||||
'/admin/entity/{bundle}/display-layout/{view_mode_name}/save',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_parameter',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_parameter display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_parameter.cancel' => new Route(
|
||||
'/admin/entity/{bundle}/display-layout/{view_mode_name}/cancel',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_parameter',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::cancelLayout',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_parameter display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_parameter.disable' => new Route(
|
||||
'/admin/entity/{bundle}/display-layout/{view_mode_name}/disable',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_parameter',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_form' => '\Drupal\layout_builder\Form\LayoutBuilderDisableForm',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_parameter display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
]
|
||||
),
|
||||
];
|
||||
|
||||
$collection = new RouteCollection();
|
||||
$collection->add('known', new Route('/admin/entity/whatever', [], [], ['_admin_route' => TRUE]));
|
||||
$collection->add('with_bundle', new Route('/admin/entity/{bundle}'));
|
||||
|
||||
$this->plugin->buildRoutes($collection);
|
||||
$this->assertEquals($expected, $collection->all());
|
||||
$this->assertSame(array_keys($expected), array_keys($collection->all()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\layout_builder\InlineBlockUsage;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\InlineBlockUsage
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class InlineBlockUsageTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests calling deleteUsage() with empty array.
|
||||
*
|
||||
* @covers ::deleteUsage
|
||||
*/
|
||||
public function testEmptyDeleteUsageCall() {
|
||||
$connection = $this->prophesize(Connection::class);
|
||||
$connection->delete('inline_block_usage')->shouldNotBeCalled();
|
||||
|
||||
(new InlineBlockUsage($connection->reveal()))->deleteUsage([]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\layout_builder\Routing\LayoutBuilderRouteEnhancer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Routing\LayoutBuilderRouteEnhancer
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutBuilderRouteEnhancerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::enhance
|
||||
*/
|
||||
public function testEnhanceValidDefaults() {
|
||||
$route = new Route('/the/path', [], [], ['_layout_builder' => TRUE]);
|
||||
$route_enhancer = new LayoutBuilderRouteEnhancer();
|
||||
$defaults = [
|
||||
RouteObjectInterface::ROUTE_OBJECT => $route,
|
||||
];
|
||||
// Ensure that the 'section_storage' key now contains the value stored for a
|
||||
// given entity type.
|
||||
$expected = [
|
||||
RouteObjectInterface::ROUTE_OBJECT => $route,
|
||||
'is_rebuilding' => TRUE,
|
||||
];
|
||||
$result = $route_enhancer->enhance($defaults, new Request(['layout_is_rebuilding' => TRUE]));
|
||||
$this->assertEquals($expected, $result);
|
||||
|
||||
$expected['is_rebuilding'] = FALSE;
|
||||
$result = $route_enhancer->enhance($defaults, new Request());
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\Core\Routing\RouteBuildEvent;
|
||||
use Drupal\layout_builder\Routing\LayoutBuilderRoutes;
|
||||
use Drupal\layout_builder\SectionStorage\SectionStorageDefinition;
|
||||
use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Routing\LayoutBuilderRoutes
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutBuilderRoutesTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The Layout Builder route builder.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface
|
||||
*/
|
||||
protected $sectionStorageManager;
|
||||
|
||||
/**
|
||||
* The Layout Builder route builder.
|
||||
*
|
||||
* @var \Drupal\layout_builder\Routing\LayoutBuilderRoutes
|
||||
*/
|
||||
protected $routeBuilder;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->sectionStorageManager = $this->prophesize(SectionStorageManagerInterface::class);
|
||||
$this->routeBuilder = new LayoutBuilderRoutes($this->sectionStorageManager->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onAlterRoutes
|
||||
*/
|
||||
public function testOnAlterRoutes() {
|
||||
$expected = [
|
||||
'test_route1' => new Route('/test/path1'),
|
||||
'test_route_shared' => new Route('/test/path/shared2'),
|
||||
'test_route2' => new Route('/test/path2'),
|
||||
];
|
||||
|
||||
$section_storage_first = $this->prophesize(SectionStorageInterface::class);
|
||||
$section_storage_first->buildRoutes(Argument::type(RouteCollection::class))->shouldBeCalled()->will(function ($args) {
|
||||
/** @var \Symfony\Component\Routing\RouteCollection $collection */
|
||||
$collection = $args[0];
|
||||
$collection->add('test_route_shared', new Route('/test/path/shared1'));
|
||||
$collection->add('test_route1', new Route('/test/path1'));
|
||||
});
|
||||
$section_storage_second = $this->prophesize(SectionStorageInterface::class);
|
||||
$section_storage_second->buildRoutes(Argument::type(RouteCollection::class))->shouldBeCalled()->will(function ($args) {
|
||||
/** @var \Symfony\Component\Routing\RouteCollection $collection */
|
||||
$collection = $args[0];
|
||||
$collection->add('test_route_shared', new Route('/test/path/shared2'));
|
||||
$collection->add('test_route2', new Route('/test/path2'));
|
||||
});
|
||||
|
||||
$this->sectionStorageManager->loadEmpty('first')->willReturn($section_storage_first->reveal());
|
||||
$this->sectionStorageManager->loadEmpty('second')->willReturn($section_storage_second->reveal());
|
||||
$definitions['first'] = new SectionStorageDefinition();
|
||||
$definitions['second'] = new SectionStorageDefinition();
|
||||
$this->sectionStorageManager->getDefinitions()->willReturn($definitions);
|
||||
|
||||
$collection = new RouteCollection();
|
||||
$event = new RouteBuildEvent($collection);
|
||||
$this->routeBuilder->onAlterRoutes($event);
|
||||
$this->assertEquals($expected, $collection->all());
|
||||
$this->assertSame(array_keys($expected), array_keys($collection->all()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
|
||||
use Drupal\layout_builder\Routing\LayoutTempstoreParamConverter;
|
||||
use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Routing\LayoutTempstoreParamConverter
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutTempstoreParamConverterTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::convert
|
||||
*/
|
||||
public function testConvert() {
|
||||
$layout_tempstore_repository = $this->prophesize(LayoutTempstoreRepositoryInterface::class);
|
||||
$section_storage_manager = $this->prophesize(SectionStorageManagerInterface::class);
|
||||
$converter = new LayoutTempstoreParamConverter($layout_tempstore_repository->reveal(), $section_storage_manager->reveal());
|
||||
|
||||
$section_storage = $this->prophesize(SectionStorageInterface::class);
|
||||
|
||||
$value = 'some_value';
|
||||
$definition = ['layout_builder_tempstore' => TRUE];
|
||||
$name = 'the_parameter_name';
|
||||
$defaults = ['section_storage_type' => 'my_type'];
|
||||
$expected = 'the_return_value';
|
||||
|
||||
$section_storage_manager->hasDefinition('my_type')->willReturn(TRUE);
|
||||
$section_storage_manager->loadFromRoute('my_type', $value, $definition, $name, $defaults)->willReturn($section_storage);
|
||||
|
||||
$layout_tempstore_repository->get($section_storage->reveal())->willReturn($expected);
|
||||
|
||||
$result = $converter->convert($value, $definition, $name, $defaults);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::convert
|
||||
*/
|
||||
public function testConvertNoType() {
|
||||
$layout_tempstore_repository = $this->prophesize(LayoutTempstoreRepositoryInterface::class);
|
||||
$section_storage_manager = $this->prophesize(SectionStorageManagerInterface::class);
|
||||
$converter = new LayoutTempstoreParamConverter($layout_tempstore_repository->reveal(), $section_storage_manager->reveal());
|
||||
|
||||
$value = 'some_value';
|
||||
$definition = ['layout_builder_tempstore' => TRUE];
|
||||
$name = 'the_parameter_name';
|
||||
$defaults = ['section_storage_type' => NULL];
|
||||
|
||||
$section_storage_manager->hasDefinition()->shouldNotBeCalled();
|
||||
$section_storage_manager->loadFromRoute()->shouldNotBeCalled();
|
||||
$layout_tempstore_repository->get()->shouldNotBeCalled();
|
||||
|
||||
$result = $converter->convert($value, $definition, $name, $defaults);
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::convert
|
||||
*/
|
||||
public function testConvertInvalidConverter() {
|
||||
$layout_tempstore_repository = $this->prophesize(LayoutTempstoreRepositoryInterface::class);
|
||||
$section_storage_manager = $this->prophesize(SectionStorageManagerInterface::class);
|
||||
$converter = new LayoutTempstoreParamConverter($layout_tempstore_repository->reveal(), $section_storage_manager->reveal());
|
||||
|
||||
$value = 'some_value';
|
||||
$definition = ['layout_builder_tempstore' => TRUE];
|
||||
$name = 'the_parameter_name';
|
||||
$defaults = ['section_storage_type' => 'invalid'];
|
||||
|
||||
$section_storage_manager->hasDefinition('invalid')->willReturn(FALSE);
|
||||
$section_storage_manager->loadFromRoute()->shouldNotBeCalled();
|
||||
$layout_tempstore_repository->get()->shouldNotBeCalled();
|
||||
|
||||
$result = $converter->convert($value, $definition, $name, $defaults);
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\Core\TempStore\SharedTempStore;
|
||||
use Drupal\Core\TempStore\SharedTempStoreFactory;
|
||||
use Drupal\layout_builder\LayoutTempstoreRepository;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\LayoutTempstoreRepository
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutTempstoreRepositoryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testGetEmptyTempstore() {
|
||||
$section_storage = $this->prophesize(SectionStorageInterface::class);
|
||||
$section_storage->getStorageType()->willReturn('my_storage_type');
|
||||
$section_storage->getStorageId()->willReturn('my_storage_id');
|
||||
|
||||
$tempstore = $this->prophesize(SharedTempStore::class);
|
||||
$tempstore->get('my_storage_id')->shouldBeCalled();
|
||||
|
||||
$tempstore_factory = $this->prophesize(SharedTempStoreFactory::class);
|
||||
$tempstore_factory->get('layout_builder.section_storage.my_storage_type')->willReturn($tempstore->reveal());
|
||||
|
||||
$repository = new LayoutTempstoreRepository($tempstore_factory->reveal());
|
||||
|
||||
$result = $repository->get($section_storage->reveal());
|
||||
$this->assertSame($section_storage->reveal(), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testGetLoadedTempstore() {
|
||||
$section_storage = $this->prophesize(SectionStorageInterface::class);
|
||||
$section_storage->getStorageType()->willReturn('my_storage_type');
|
||||
$section_storage->getStorageId()->willReturn('my_storage_id');
|
||||
|
||||
$tempstore_section_storage = $this->prophesize(SectionStorageInterface::class);
|
||||
$tempstore = $this->prophesize(SharedTempStore::class);
|
||||
$tempstore->get('my_storage_id')->willReturn(['section_storage' => $tempstore_section_storage->reveal()]);
|
||||
$tempstore_factory = $this->prophesize(SharedTempStoreFactory::class);
|
||||
$tempstore_factory->get('layout_builder.section_storage.my_storage_type')->willReturn($tempstore->reveal());
|
||||
|
||||
$repository = new LayoutTempstoreRepository($tempstore_factory->reveal());
|
||||
|
||||
$result = $repository->get($section_storage->reveal());
|
||||
$this->assertSame($tempstore_section_storage->reveal(), $result);
|
||||
$this->assertNotSame($section_storage->reveal(), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testGetInvalidEntry() {
|
||||
$section_storage = $this->prophesize(SectionStorageInterface::class);
|
||||
$section_storage->getStorageType()->willReturn('my_storage_type');
|
||||
$section_storage->getStorageId()->willReturn('my_storage_id');
|
||||
|
||||
$tempstore = $this->prophesize(SharedTempStore::class);
|
||||
$tempstore->get('my_storage_id')->willReturn(['section_storage' => 'this_is_not_an_entity']);
|
||||
|
||||
$tempstore_factory = $this->prophesize(SharedTempStoreFactory::class);
|
||||
$tempstore_factory->get('layout_builder.section_storage.my_storage_type')->willReturn($tempstore->reveal());
|
||||
|
||||
$repository = new LayoutTempstoreRepository($tempstore_factory->reveal());
|
||||
|
||||
$this->setExpectedException(\UnexpectedValueException::class, 'The entry with storage type "my_storage_type" and ID "my_storage_id" is invalid');
|
||||
$repository->get($section_storage->reveal());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,390 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
use Drupal\layout_builder\SectionStorage\SectionStorageDefinition;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class OverridesSectionStorageTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The plugin.
|
||||
*
|
||||
* @var \Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
$this->entityFieldManager = $this->prophesize(EntityFieldManagerInterface::class);
|
||||
|
||||
$definition = new SectionStorageDefinition([
|
||||
'id' => 'overrides',
|
||||
'class' => OverridesSectionStorage::class,
|
||||
]);
|
||||
$this->plugin = new OverridesSectionStorage([], 'overrides', $definition, $this->entityTypeManager->reveal(), $this->entityFieldManager->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::extractIdFromRoute
|
||||
*
|
||||
* @dataProvider providerTestExtractIdFromRoute
|
||||
*/
|
||||
public function testExtractIdFromRoute($expected, $value, array $defaults) {
|
||||
$result = $this->plugin->extractIdFromRoute($value, [], 'the_parameter_name', $defaults);
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for ::testExtractIdFromRoute().
|
||||
*/
|
||||
public function providerTestExtractIdFromRoute() {
|
||||
$data = [];
|
||||
$data['with value, with layout'] = [
|
||||
'my_entity_type.entity_with_layout',
|
||||
'my_entity_type.entity_with_layout',
|
||||
[],
|
||||
];
|
||||
$data['with value, without layout'] = [
|
||||
NULL,
|
||||
'my_entity_type',
|
||||
[],
|
||||
];
|
||||
$data['empty value, populated defaults'] = [
|
||||
'my_entity_type.entity_with_layout',
|
||||
'',
|
||||
[
|
||||
'entity_type_id' => 'my_entity_type',
|
||||
'my_entity_type' => 'entity_with_layout',
|
||||
],
|
||||
];
|
||||
$data['empty value, empty defaults'] = [
|
||||
NULL,
|
||||
'',
|
||||
[],
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getSectionListFromId
|
||||
*
|
||||
* @dataProvider providerTestGetSectionListFromId
|
||||
*/
|
||||
public function testGetSectionListFromId($success, $expected_entity_type_id, $id) {
|
||||
$defaults['the_parameter_name'] = $id;
|
||||
|
||||
if ($expected_entity_type_id) {
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
|
||||
$entity_without_layout = $this->prophesize(FieldableEntityInterface::class);
|
||||
$entity_without_layout->hasField('layout_builder__layout')->willReturn(FALSE);
|
||||
$entity_without_layout->get('layout_builder__layout')->shouldNotBeCalled();
|
||||
$entity_storage->load('entity_without_layout')->willReturn($entity_without_layout->reveal());
|
||||
|
||||
$entity_with_layout = $this->prophesize(FieldableEntityInterface::class);
|
||||
$entity_with_layout->hasField('layout_builder__layout')->willReturn(TRUE);
|
||||
$entity_with_layout->get('layout_builder__layout')->willReturn('the_return_value');
|
||||
$entity_storage->load('entity_with_layout')->willReturn($entity_with_layout->reveal());
|
||||
|
||||
$this->entityTypeManager->getStorage($expected_entity_type_id)->willReturn($entity_storage->reveal());
|
||||
}
|
||||
else {
|
||||
$this->entityTypeManager->getStorage(Argument::any())->shouldNotBeCalled();
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$this->setExpectedException(\InvalidArgumentException::class);
|
||||
}
|
||||
|
||||
$result = $this->plugin->getSectionListFromId($id);
|
||||
if ($success) {
|
||||
$this->assertEquals('the_return_value', $result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for ::testGetSectionListFromId().
|
||||
*/
|
||||
public function providerTestGetSectionListFromId() {
|
||||
$data = [];
|
||||
$data['with value, with layout'] = [
|
||||
TRUE,
|
||||
'my_entity_type',
|
||||
'my_entity_type.entity_with_layout',
|
||||
];
|
||||
$data['with value, without layout'] = [
|
||||
FALSE,
|
||||
'my_entity_type',
|
||||
'my_entity_type.entity_without_layout',
|
||||
];
|
||||
$data['empty value, empty defaults'] = [
|
||||
FALSE,
|
||||
NULL,
|
||||
'',
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildRoutes
|
||||
* @covers ::hasIntegerId
|
||||
* @covers ::getEntityTypes
|
||||
*/
|
||||
public function testBuildRoutes() {
|
||||
$entity_types = [];
|
||||
|
||||
$not_fieldable = $this->prophesize(EntityTypeInterface::class);
|
||||
$not_fieldable->entityClassImplements(FieldableEntityInterface::class)->willReturn(FALSE);
|
||||
$entity_types['not_fieldable'] = $not_fieldable->reveal();
|
||||
|
||||
$no_view_builder = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_view_builder->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_view_builder->hasViewBuilderClass()->willReturn(FALSE);
|
||||
$entity_types['no_view_builder'] = $no_view_builder->reveal();
|
||||
|
||||
$no_canonical_link = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_canonical_link->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_canonical_link->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$no_canonical_link->hasLinkTemplate('canonical')->willReturn(FALSE);
|
||||
$entity_types['no_canonical_link'] = $no_canonical_link->reveal();
|
||||
$this->entityFieldManager->getFieldStorageDefinitions('no_canonical_link')->shouldNotBeCalled();
|
||||
|
||||
$with_string_id = $this->prophesize(EntityTypeInterface::class);
|
||||
$with_string_id->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$with_string_id->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$with_string_id->hasLinkTemplate('canonical')->willReturn(TRUE);
|
||||
$with_string_id->getLinkTemplate('canonical')->willReturn('/entity/{entity}');
|
||||
$with_string_id->id()->willReturn('with_string_id');
|
||||
$with_string_id->getKey('id')->willReturn('id');
|
||||
$entity_types['with_string_id'] = $with_string_id->reveal();
|
||||
$string_id = $this->prophesize(FieldStorageDefinitionInterface::class);
|
||||
$string_id->getType()->willReturn('string');
|
||||
$this->entityFieldManager->getFieldStorageDefinitions('with_string_id')->willReturn(['id' => $string_id->reveal()]);
|
||||
|
||||
$with_integer_id = $this->prophesize(EntityTypeInterface::class);
|
||||
$with_integer_id->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$with_integer_id->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$with_integer_id->hasLinkTemplate('canonical')->willReturn(TRUE);
|
||||
$with_integer_id->getLinkTemplate('canonical')->willReturn('/entity/{entity}');
|
||||
$with_integer_id->id()->willReturn('with_integer_id');
|
||||
$with_integer_id->getKey('id')->willReturn('id');
|
||||
$entity_types['with_integer_id'] = $with_integer_id->reveal();
|
||||
$integer_id = $this->prophesize(FieldStorageDefinitionInterface::class);
|
||||
$integer_id->getType()->willReturn('integer');
|
||||
$this->entityFieldManager->getFieldStorageDefinitions('with_integer_id')->willReturn(['id' => $integer_id->reveal()]);
|
||||
|
||||
$this->entityTypeManager->getDefinitions()->willReturn($entity_types);
|
||||
|
||||
$expected = [
|
||||
'layout_builder.overrides.with_string_id.view' => new Route(
|
||||
'/entity/{entity}/layout',
|
||||
[
|
||||
'entity_type_id' => 'with_string_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'is_rebuilding' => FALSE,
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::layout',
|
||||
'_title_callback' => '\Drupal\layout_builder\Controller\LayoutBuilderController::title',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_string_id' => ['type' => 'entity:with_string_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_string_id.save' => new Route(
|
||||
'/entity/{entity}/layout/save',
|
||||
[
|
||||
'entity_type_id' => 'with_string_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_string_id' => ['type' => 'entity:with_string_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_string_id.cancel' => new Route(
|
||||
'/entity/{entity}/layout/cancel',
|
||||
[
|
||||
'entity_type_id' => 'with_string_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::cancelLayout',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_string_id' => ['type' => 'entity:with_string_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_string_id.revert' => new Route(
|
||||
'/entity/{entity}/layout/revert',
|
||||
[
|
||||
'entity_type_id' => 'with_string_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_form' => '\Drupal\layout_builder\Form\RevertOverridesForm',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_string_id' => ['type' => 'entity:with_string_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_integer_id.view' => new Route(
|
||||
'/entity/{entity}/layout',
|
||||
[
|
||||
'entity_type_id' => 'with_integer_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'is_rebuilding' => FALSE,
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::layout',
|
||||
'_title_callback' => '\Drupal\layout_builder\Controller\LayoutBuilderController::title',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
'with_integer_id' => '\d+',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_integer_id' => ['type' => 'entity:with_integer_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_integer_id.save' => new Route(
|
||||
'/entity/{entity}/layout/save',
|
||||
[
|
||||
'entity_type_id' => 'with_integer_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
'with_integer_id' => '\d+',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_integer_id' => ['type' => 'entity:with_integer_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_integer_id.cancel' => new Route(
|
||||
'/entity/{entity}/layout/cancel',
|
||||
[
|
||||
'entity_type_id' => 'with_integer_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::cancelLayout',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
'with_integer_id' => '\d+',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_integer_id' => ['type' => 'entity:with_integer_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_integer_id.revert' => new Route(
|
||||
'/entity/{entity}/layout/revert',
|
||||
[
|
||||
'entity_type_id' => 'with_integer_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_form' => '\Drupal\layout_builder\Form\RevertOverridesForm',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
'with_integer_id' => '\d+',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_integer_id' => ['type' => 'entity:with_integer_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
];
|
||||
|
||||
$collection = new RouteCollection();
|
||||
$this->plugin->buildRoutes($collection);
|
||||
$this->assertEquals($expected, $collection->all());
|
||||
$this->assertSame(array_keys($expected), array_keys($collection->all()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Layout\LayoutInterface;
|
||||
use Drupal\Core\Layout\LayoutPluginManagerInterface;
|
||||
use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent;
|
||||
use Drupal\layout_builder\LayoutBuilderEvents;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\SectionComponent
|
||||
* @group layout_builder
|
||||
*/
|
||||
class SectionComponentTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::toRenderArray
|
||||
*/
|
||||
public function testToRenderArray() {
|
||||
$existing_block = $this->prophesize(BlockPluginInterface::class);
|
||||
$existing_block->getPluginId()->willReturn('block_plugin_id');
|
||||
|
||||
$block_manager = $this->prophesize(BlockManagerInterface::class);
|
||||
$block_manager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($existing_block->reveal());
|
||||
|
||||
// Imitate an event subscriber by setting a resulting build on the event.
|
||||
$event_dispatcher = $this->prophesize(EventDispatcherInterface::class);
|
||||
$event_dispatcher
|
||||
->dispatch(LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY, Argument::type(SectionComponentBuildRenderArrayEvent::class))
|
||||
->shouldBeCalled()
|
||||
->will(function ($args) {
|
||||
/** @var \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent $event */
|
||||
$event = $args[1];
|
||||
$event->setBuild(['#markup' => $event->getPlugin()->getPluginId()]);
|
||||
return;
|
||||
});
|
||||
|
||||
$layout_plugin = $this->prophesize(LayoutInterface::class);
|
||||
$layout_plugin->build(Argument::type('array'))->willReturnArgument(0);
|
||||
|
||||
$layout_manager = $this->prophesize(LayoutPluginManagerInterface::class);
|
||||
$layout_manager->createInstance('layout_onecol', [])->willReturn($layout_plugin->reveal());
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('plugin.manager.block', $block_manager->reveal());
|
||||
$container->set('event_dispatcher', $event_dispatcher->reveal());
|
||||
$container->set('plugin.manager.core.layout', $layout_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$expected = [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
'#markup' => 'block_plugin_id',
|
||||
];
|
||||
|
||||
$component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']);
|
||||
$result = $component->toRenderArray();
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Layout\LayoutDefinition;
|
||||
use Drupal\Core\Layout\LayoutInterface;
|
||||
use Drupal\Core\Layout\LayoutPluginManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Section
|
||||
* @group layout_builder
|
||||
*/
|
||||
class SectionRenderTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The block plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Block\BlockManagerInterface
|
||||
*/
|
||||
protected $blockManager;
|
||||
|
||||
/**
|
||||
* The plugin context handler.
|
||||
*
|
||||
* @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
|
||||
*/
|
||||
protected $contextHandler;
|
||||
|
||||
/**
|
||||
* The context manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
|
||||
*/
|
||||
protected $contextRepository;
|
||||
|
||||
/**
|
||||
* The event dispatcher.
|
||||
*
|
||||
* @var \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$layout_plugin_manager = $this->prophesize(LayoutPluginManagerInterface::class);
|
||||
$this->blockManager = $this->prophesize(BlockManagerInterface::class);
|
||||
$this->contextHandler = $this->prophesize(ContextHandlerInterface::class);
|
||||
$this->contextRepository = $this->prophesize(ContextRepositoryInterface::class);
|
||||
// @todo Refactor this into some better tests in https://www.drupal.org/node/2942605.
|
||||
$this->eventDispatcher = (new \ReflectionClass(ContainerAwareEventDispatcher::class))->newInstanceWithoutConstructor();
|
||||
|
||||
$this->account = $this->prophesize(AccountInterface::class);
|
||||
$subscriber = new BlockComponentRenderArray($this->account->reveal());
|
||||
$this->eventDispatcher->addSubscriber($subscriber);
|
||||
|
||||
$layout = $this->prophesize(LayoutInterface::class);
|
||||
$layout->getPluginDefinition()->willReturn(new LayoutDefinition([]));
|
||||
$layout->build(Argument::type('array'))->willReturnArgument(0);
|
||||
$layout_plugin_manager->createInstance('layout_onecol', [])->willReturn($layout->reveal());
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('current_user', $this->account->reveal());
|
||||
$container->set('plugin.manager.block', $this->blockManager->reveal());
|
||||
$container->set('plugin.manager.core.layout', $layout_plugin_manager->reveal());
|
||||
$container->set('context.handler', $this->contextHandler->reveal());
|
||||
$container->set('context.repository', $this->contextRepository->reveal());
|
||||
$container->set('event_dispatcher', $this->eventDispatcher);
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::toRenderArray
|
||||
*/
|
||||
public function testToRenderArray() {
|
||||
$block_content = ['#markup' => 'The block content.'];
|
||||
$render_array = [
|
||||
'#theme' => 'block',
|
||||
'#weight' => 0,
|
||||
'#configuration' => [],
|
||||
'#plugin_id' => 'block_plugin_id',
|
||||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => $block_content,
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
|
||||
|
||||
$access_result = AccessResult::allowed();
|
||||
$block->access($this->account->reveal(), TRUE)->willReturn($access_result);
|
||||
$block->build()->willReturn($block_content);
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn([]);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
$block->getPluginId()->willReturn('block_plugin_id');
|
||||
$block->getBaseId()->willReturn('block_plugin_id');
|
||||
$block->getDerivativeId()->willReturn(NULL);
|
||||
$block->getConfiguration()->willReturn([]);
|
||||
|
||||
$section = [
|
||||
new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
|
||||
];
|
||||
$expected = [
|
||||
'content' => [
|
||||
'some_uuid' => $render_array,
|
||||
],
|
||||
];
|
||||
$result = (new Section('layout_onecol', [], $section))->toRenderArray();
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::toRenderArray
|
||||
*/
|
||||
public function testToRenderArrayAccessDenied() {
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
|
||||
|
||||
$access_result = AccessResult::forbidden();
|
||||
$block->access($this->account->reveal(), TRUE)->willReturn($access_result);
|
||||
$block->build()->shouldNotBeCalled();
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn([]);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
|
||||
$section = [
|
||||
new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
|
||||
];
|
||||
$expected = [
|
||||
'content' => [
|
||||
'some_uuid' => [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$result = (new Section('layout_onecol', [], $section))->toRenderArray();
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::toRenderArray
|
||||
*/
|
||||
public function testToRenderArrayPreview() {
|
||||
$block_content = ['#markup' => 'The block content.'];
|
||||
$render_array = [
|
||||
'#theme' => 'block',
|
||||
'#weight' => 0,
|
||||
'#configuration' => [],
|
||||
'#plugin_id' => 'block_plugin_id',
|
||||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => $block_content,
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => 0,
|
||||
],
|
||||
];
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
|
||||
|
||||
$block->access($this->account->reveal(), TRUE)->shouldNotBeCalled();
|
||||
$block->build()->willReturn($block_content);
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn([]);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
$block->getConfiguration()->willReturn([]);
|
||||
$block->getPluginId()->willReturn('block_plugin_id');
|
||||
$block->getBaseId()->willReturn('block_plugin_id');
|
||||
$block->getDerivativeId()->willReturn(NULL);
|
||||
|
||||
$section = [
|
||||
new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
|
||||
];
|
||||
$expected = [
|
||||
'content' => [
|
||||
'some_uuid' => $render_array,
|
||||
],
|
||||
];
|
||||
$result = (new Section('layout_onecol', [], $section))->toRenderArray([], TRUE);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::toRenderArray
|
||||
*/
|
||||
public function testToRenderArrayEmpty() {
|
||||
$section = [];
|
||||
$expected = [];
|
||||
$result = (new Section('layout_onecol', [], $section))->toRenderArray();
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::toRenderArray
|
||||
*/
|
||||
public function testContextAwareBlock() {
|
||||
$render_array = [
|
||||
'#theme' => 'block',
|
||||
'#weight' => 0,
|
||||
'#configuration' => [],
|
||||
'#plugin_id' => 'block_plugin_id',
|
||||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => [],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
|
||||
$block = $this->prophesize(BlockPluginInterface::class)->willImplement(ContextAwarePluginInterface::class);
|
||||
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
|
||||
|
||||
$access_result = AccessResult::allowed();
|
||||
$block->access($this->account->reveal(), TRUE)->willReturn($access_result);
|
||||
$block->build()->willReturn([]);
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn([]);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
$block->getContextMapping()->willReturn([]);
|
||||
$block->getPluginId()->willReturn('block_plugin_id');
|
||||
$block->getBaseId()->willReturn('block_plugin_id');
|
||||
$block->getDerivativeId()->willReturn(NULL);
|
||||
$block->getConfiguration()->willReturn([]);
|
||||
|
||||
$section = [
|
||||
new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
|
||||
];
|
||||
$expected = [
|
||||
'content' => [
|
||||
'some_uuid' => $render_array,
|
||||
],
|
||||
];
|
||||
$result = (new Section('layout_onecol', [], $section))->toRenderArray();
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::toRenderArray
|
||||
*/
|
||||
public function testToRenderArrayMissingPluginId() {
|
||||
$this->setExpectedException(PluginException::class, 'No plugin ID specified for component with "some_uuid" UUID');
|
||||
(new Section('layout_onecol', [], [new SectionComponent('some_uuid', 'content')]))->toRenderArray();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\Component\Plugin\Factory\FactoryInterface;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\layout_builder\SectionListInterface;
|
||||
use Drupal\layout_builder\SectionStorage\SectionStorageManager;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\SectionStorage\SectionStorageManager
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class SectionStorageManagerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The section storage manager.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionStorage\SectionStorageManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* The plugin.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionStorageInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$cache = $this->prophesize(CacheBackendInterface::class);
|
||||
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||
$this->manager = new SectionStorageManager(new \ArrayObject(), $cache->reveal(), $module_handler->reveal());
|
||||
|
||||
$this->plugin = $this->prophesize(SectionStorageInterface::class);
|
||||
|
||||
$factory = $this->prophesize(FactoryInterface::class);
|
||||
$factory->createInstance('the_plugin_id', [])->willReturn($this->plugin->reveal());
|
||||
$reflection_property = new \ReflectionProperty($this->manager, 'factory');
|
||||
$reflection_property->setAccessible(TRUE);
|
||||
$reflection_property->setValue($this->manager, $factory->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::loadEmpty
|
||||
*/
|
||||
public function testLoadEmpty() {
|
||||
$result = $this->manager->loadEmpty('the_plugin_id');
|
||||
$this->assertInstanceOf(SectionStorageInterface::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::loadFromStorageId
|
||||
*/
|
||||
public function testLoadFromStorageId() {
|
||||
$section_list = $this->prophesize(SectionListInterface::class);
|
||||
$this->plugin->setSectionList($section_list->reveal())->will(function () {
|
||||
return $this;
|
||||
});
|
||||
$this->plugin->getSectionListFromId('the_storage_id')->willReturn($section_list->reveal());
|
||||
|
||||
$result = $this->manager->loadFromStorageId('the_plugin_id', 'the_storage_id');
|
||||
$this->assertInstanceOf(SectionStorageInterface::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::loadFromRoute
|
||||
*/
|
||||
public function testLoadFromRoute() {
|
||||
$section_list = $this->prophesize(SectionListInterface::class);
|
||||
$this->plugin->extractIdFromRoute('the_value', [], 'the_parameter_name', [])->willReturn('the_storage_id');
|
||||
$this->plugin->getSectionListFromId('the_storage_id')->willReturn($section_list->reveal());
|
||||
$this->plugin->setSectionList($section_list->reveal())->will(function () {
|
||||
return $this;
|
||||
});
|
||||
|
||||
$result = $this->manager->loadFromRoute('the_plugin_id', 'the_value', [], 'the_parameter_name', []);
|
||||
$this->assertInstanceOf(SectionStorageInterface::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::loadFromRoute
|
||||
*/
|
||||
public function testLoadFromRouteNull() {
|
||||
$this->plugin->extractIdFromRoute('the_value', [], 'the_parameter_name', ['_route' => 'the_route_name'])->willReturn(NULL);
|
||||
|
||||
$result = $this->manager->loadFromRoute('the_plugin_id', 'the_value', [], 'the_parameter_name', ['_route' => 'the_route_name']);
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
}
|
182
web/core/modules/layout_builder/tests/src/Unit/SectionTest.php
Normal file
182
web/core/modules/layout_builder/tests/src/Unit/SectionTest.php
Normal file
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\layout_builder\Section
|
||||
* @group layout_builder
|
||||
*/
|
||||
class SectionTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The section object to test.
|
||||
*
|
||||
* @var \Drupal\layout_builder\Section
|
||||
*/
|
||||
protected $section;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->section = new Section('layout_onecol', [], [
|
||||
new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']),
|
||||
(new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
(new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__construct
|
||||
* @covers ::setComponent
|
||||
* @covers ::getComponents
|
||||
*/
|
||||
public function testGetComponents() {
|
||||
$expected = [
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
];
|
||||
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getComponent
|
||||
*/
|
||||
public function testGetComponentInvalidUuid() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID "invalid-uuid"');
|
||||
$this->section->getComponent('invalid-uuid');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getComponent
|
||||
*/
|
||||
public function testGetComponent() {
|
||||
$expected = new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']);
|
||||
|
||||
$this->assertEquals($expected, $this->section->getComponent('existing-uuid'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::removeComponent
|
||||
* @covers ::getComponentsByRegion
|
||||
*/
|
||||
public function testRemoveComponent() {
|
||||
$expected = [
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
];
|
||||
|
||||
$this->section->removeComponent('first-uuid');
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::appendComponent
|
||||
* @covers ::getNextHighestWeight
|
||||
* @covers ::getComponentsByRegion
|
||||
*/
|
||||
public function testAppendComponent() {
|
||||
$expected = [
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
'new-uuid' => (new SectionComponent('new-uuid', 'some-region', []))->setWeight(1),
|
||||
];
|
||||
|
||||
$this->section->appendComponent(new SectionComponent('new-uuid', 'some-region'));
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertAfterComponent
|
||||
*/
|
||||
public function testInsertAfterComponent() {
|
||||
$expected = [
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(4),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(3),
|
||||
];
|
||||
|
||||
$this->section->insertAfterComponent('first-uuid', new SectionComponent('new-uuid', 'ordered-region'));
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertAfterComponent
|
||||
*/
|
||||
public function testInsertAfterComponentValidUuidRegionMismatch() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID "existing-uuid"');
|
||||
$this->section->insertAfterComponent('existing-uuid', new SectionComponent('new-uuid', 'ordered-region'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertAfterComponent
|
||||
*/
|
||||
public function testInsertAfterComponentInvalidUuid() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID "invalid-uuid"');
|
||||
$this->section->insertAfterComponent('invalid-uuid', new SectionComponent('new-uuid', 'ordered-region'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertComponent
|
||||
* @covers ::getComponentsByRegion
|
||||
*/
|
||||
public function testInsertComponent() {
|
||||
$expected = [
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(4),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(3),
|
||||
'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(2),
|
||||
];
|
||||
|
||||
$this->section->insertComponent(0, new SectionComponent('new-uuid', 'ordered-region'));
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertComponent
|
||||
*/
|
||||
public function testInsertComponentAppend() {
|
||||
$expected = [
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(4),
|
||||
];
|
||||
|
||||
$this->section->insertComponent(2, new SectionComponent('new-uuid', 'ordered-region'));
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertComponent
|
||||
*/
|
||||
public function testInsertComponentInvalidDelta() {
|
||||
$this->setExpectedException(\OutOfBoundsException::class, 'Invalid delta "7" for the "new-uuid" component');
|
||||
$this->section->insertComponent(7, new SectionComponent('new-uuid', 'ordered-region'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the section has the expected components.
|
||||
*
|
||||
* @param \Drupal\layout_builder\SectionComponent[] $expected
|
||||
* The expected sections.
|
||||
* @param \Drupal\layout_builder\Section $section
|
||||
* The section storage to check.
|
||||
*/
|
||||
protected function assertComponents(array $expected, Section $section) {
|
||||
$result = $section->getComponents();
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertSame(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue