graphql_compose-1.0.0-beta20/tests/src/Functional/Core/LayoutBuilderTest.php
tests/src/Functional/Core/LayoutBuilderTest.php
<?php
declare(strict_types=1);
namespace Drupal\Tests\graphql_compose\Functional\Core;
use Drupal\Tests\graphql_compose\Functional\GraphQLComposeBrowserTestBase;
use Drupal\Tests\layout_builder\FunctionalJavascript\LayoutBuilderSortTrait;
use Drupal\block_content\BlockContentInterface;
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drupal\node\NodeStorageInterface;
/**
* Layout builder tests.
*
* @group graphql_compose
*/
class LayoutBuilderTest extends GraphQLComposeBrowserTestBase {
use LayoutBuilderSortTrait;
/**
* The test node.
*
* @var \Drupal\node\NodeInterface
*/
protected NodeInterface $node;
/**
* The node storage.
*
* @var \Drupal\node\NodeStorageInterface
*/
protected NodeStorageInterface $nodeStorage;
/**
* Block content to place.
*
* @var \Drupal\block_content\BlockContentInterface
*/
protected BlockContentInterface $blockContent;
/**
* {@inheritdoc}
*/
protected static $modules = [
'field_ui',
'layout_builder',
'contextual',
'graphql_compose_layout_builder',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create a block type to place.
$block_type = BlockContentType::create([
'id' => 'basic_block',
'label' => 'Basic block',
]);
$block_type->save();
$field = FieldConfig::loadByName('block_content', $block_type->id(), 'body');
$field_storage = FieldStorageConfig::loadByName('block_content', 'body');
if (!$field_storage) {
$field_storage = FieldStorageConfig::create([
'field_name' => 'body',
'entity_type' => 'block_content',
'type' => 'text_with_summary',
]);
$field_storage->save();
}
if (!$field) {
$field = FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $block_type->id(),
'label' => 'Body',
'settings' => [
'display_summary' => FALSE,
'allowed_formats' => [],
],
]);
$field->save();
}
\Drupal::service('entity_display.repository')
->getFormDisplay('block_content', $block_type->id())
->setComponent('body', [
'type' => 'text_textarea_with_summary',
])
->save();
\Drupal::service('entity_display.repository')
->getViewDisplay('block_content', $block_type->id())
->setComponent('body', [
'type' => 'text_default',
])
->save();
$this->blockContent = BlockContent::create([
'info' => 'My content block',
'type' => $block_type->id(),
'body' => [
[
'value' => 'This is the block content',
'format' => filter_default_format(),
],
],
]);
$this->blockContent->save();
// Create the node type.
$this->createContentType(['type' => 'test']);
$this->createContentType(['type' => 'another_type']);
// Enable layout builder (default).
LayoutBuilderEntityViewDisplay::load('node.test.default')
->enableLayoutBuilder()
->setOverridable()
->save();
// Create the node.
$this->node = $this->createNode([
'type' => 'test',
'title' => 'The node title',
'body' => [
[
'value' => 'The node body',
'format' => 'plain_text',
],
],
]);
// Configure the layout.
$this->drupalLogin($this->drupalCreateUser([
'configure any layout',
'bypass node access',
'create test content',
'edit any test content',
'create basic_block block content',
'edit any basic_block block content',
'create and edit custom blocks',
]));
$page = $this->getSession()->getPage();
$this->drupalGet('node/' . $this->node->id() . '/layout');
// Add a new section.
$this->clickLink('Add section');
$this->clickLink('Three column');
$page->fillField('layout_settings[label]', 'Top section');
$page->selectFieldOption('layout_settings[column_widths]', '25-25-50');
$page->pressButton('Add section');
// Region first, Left content, plugin block.
$page->find('css', '.layout--threecol-section[data-layout-delta="0"] > div:nth-child(1)')->clickLink('Add block');
$page->clickLink('Powered by Drupal');
$page->checkField('settings[label_display]');
$page->pressButton('Add block');
// Region third, Right content, inline block content.
$page->find('css', '.layout--threecol-section[data-layout-delta="0"] > div:nth-child(3)')->clickLink('Add block');
$page->clickLink('Create content block');
$page->fillField('settings[label]', 'My inline block');
$page->fillField('settings[block_form][body][0][value]', 'My inline block content');
$page->uncheckField('settings[label_display]');
$page->pressButton('Add block');
// Region second. Middle content, basic block content
// (Added last to mess with the order).
$page->find('css', '.layout--threecol-section[data-layout-delta="0"] > div:nth-child(2)')->clickLink('Add block');
$page->clickLink('My content block');
$page->fillField('settings[label]', 'My test block');
$page->checkField('settings[label_display]');
$page->pressButton('Add block');
// Ensure the body is on there already.
$page->find('css', '.field--name-body');
// Add the title field (Test for interface field)
// Region second. Middle content, basic block content
// (Added last to mess with the order).
$page->find('css', '.layout--onecol')->clickLink('Add block');
$page->clickLink('Title');
$page->uncheckField('settings[label_display]');
$page->selectFieldOption('settings[formatter][label]', 'hidden');
$page->uncheckField('settings[formatter][settings][link_to_entity]');
$page->pressButton('Add block');
$page->pressButton('Save layout');
// Reload node entity.
$this->node = Node::load($this->node->id());
$this->drupalLogout();
// Setup GraphQL Compose.
// Enable node.
$this->setEntityConfig('node', 'test', [
'enabled' => TRUE,
'query_load_enabled' => TRUE,
'layout_builder_enabled' => TRUE,
]);
$this->setFieldConfig('node', 'test', 'body', [
'enabled' => TRUE,
]);
// Enable block type.
$this->setEntityConfig('block_content', 'basic_block', [
'enabled' => TRUE,
'query_load_enabled' => TRUE,
]);
$this->setFieldConfig('block_content', 'basic_block', 'body', [
'enabled' => TRUE,
]);
}
/**
* Ensure the layout teaser is the default.
*/
public function testLayoutTeaserNotDefault(): void {
// Enable layout builder.
LayoutBuilderEntityViewDisplay::load('node.test.teaser')
->enableLayoutBuilder()
->save();
$query = <<<GQL
query {
a: node(id: "{$this->node->uuid()}") {
... on NodeTest {
sections {
id
}
}
}
b: node(id: "{$this->node->uuid()}") {
... on NodeTest {
sections(viewMode: "teaser") {
id
}
}
}
}
GQL;
$content = $this->executeQuery($query);
$this->assertNotEmpty($content['data']['a']);
$this->assertNotEmpty($content['data']['b']);
$this->assertNotEquals(
$content['data']['a']['sections'],
$content['data']['b']['sections']
);
// 2 sections on default
$this->assertCount(2, $content['data']['a']['sections']);
// Default 1 section on teaser.
$this->assertCount(1, $content['data']['b']['sections']);
}
/**
* Ensure an disabled view mode falls back to default.
*/
public function testDisabledViewFallbackDefault(): void {
$query = <<<GQL
query {
a: node(id: "{$this->node->uuid()}") {
... on NodeTest {
sections {
id
}
}
}
b: node(id: "{$this->node->uuid()}") {
... on NodeTest {
sections(viewMode: "teaser") {
id
}
}
}
}
GQL;
$content = $this->executeQuery($query);
$this->assertNotEmpty($content['data']['a']);
$this->assertNotEmpty($content['data']['b']);
$this->assertEquals(
$content['data']['a']['sections'],
$content['data']['b']['sections']
);
}
/**
* Ensure disabled entity type has no layout.
*/
public function testDisabledEntityIsNull(): void {
// Disable node.
$this->setEntityConfig('node', 'another_type', [
'enabled' => TRUE,
'query_load_enabled' => TRUE,
]);
$node = $this->createNode([
'type' => 'another_type',
]);
$query = <<<GQL
query {
node(id: "{$node->uuid()}") {
... on NodeAnotherType {
sections {
id
}
}
}
}
GQL;
try {
$content = $this->executeQuery($query);
}
catch (\Exception) {
// Swallow error.
}
$this->assertStringContainsStringIgnoringCase(
'Cannot query field "sections" on type "NodeAnotherType".',
$content['errors'][0]['message'] ?? NULL
);
}
/**
* Test the sections load and are in the correct order.
*/
public function testLayoutSections(): void {
$query = <<<GQL
query {
node(id: "{$this->node->uuid()}") {
... on NodeTest {
id
sections {
__typename
id
settings
layout {
category
defaultRegion
id
label
regions
}
}
}
}
}
GQL;
$content = $this->executeQuery($query);
$this->assertNotEmpty($content['data']['node']);
$node = $content['data']['node'];
$this->assertCount(2, $node['sections']);
// Three column.
$this->assertEquals($node['sections'][0]['settings']['column_widths'], '25-25-50');
$this->assertEquals($node['sections'][0]['layout']['id'], 'layout_threecol_section');
}
/**
* Check a layout component is correctly loaded and has correct config.
*/
public function testComponentConfig(): void {
$query = <<<GQL
query {
node(id: "{$this->node->uuid()}") {
... on NodeTest {
id
sections {
components {
id
region
weight
configuration
}
}
}
}
}
GQL;
$content = $this->executeQuery($query);
$this->assertNotEmpty($content['data']['node']);
$node = $content['data']['node'];
$first = array_filter($node['sections'][0]['components'], function ($component) {
return $component['region'] === 'first';
});
$second = array_filter($node['sections'][0]['components'], function ($component) {
return $component['region'] === 'second';
});
$third = array_filter($node['sections'][0]['components'], function ($component) {
return $component['region'] === 'third';
});
$this->assertCount(1, $first);
$this->assertCount(1, $second);
$this->assertCount(1, $third);
$first = reset($first);
$second = reset($second);
$third = reset($third);
$this->assertIsInt($first['weight']);
$this->assertIsInt($second['weight']);
$this->assertIsInt($third['weight']);
// System block.
$this->assertEquals($first['configuration']['id'], 'system_powered_by_block');
$this->assertEquals($first['configuration']['label'], 'Powered by Drupal');
$this->assertEquals($first['configuration']['label_display'], 'visible');
// Basic block content.
$this->assertEquals($second['configuration']['id'], 'block_content:' . $this->blockContent->uuid());
$this->assertEquals($second['configuration']['label'], 'My test block');
$this->assertEquals($second['configuration']['label_display'], 'visible');
// Inline basic block.
$this->assertEquals($third['configuration']['id'], 'inline_block:basic_block');
$this->assertEquals($third['configuration']['label'], 'My inline block');
$this->assertIsInt($third['configuration']['label_display']);
$this->assertEquals(0, $third['configuration']['label_display']);
// Body field (after links field by default).
$body = $node['sections'][1]['components'];
$this->assertIsInt($body[0]['weight']);
$this->assertIsInt($body[1]['weight']);
// Just confirm its there.
$this->assertEquals($body[1]['configuration']['id'], 'field_block:node:test:body');
}
/**
* Check blocks can render.
*/
public function testComponentBlockRender(): void {
$query = <<<GQL
query {
node(id: "{$this->node->uuid()}") {
... on NodeTest {
id
sections {
components {
block {
... on BlockInterface {
render
}
}
}
}
}
}
}
GQL;
$content = $this->executeQuery($query);
$this->assertNotEmpty($content['data']['node']);
$node = $content['data']['node'];
$first = $node['sections'][0]['components'][0];
$second = $node['sections'][0]['components'][2];
$third = $node['sections'][0]['components'][1];
$body = $node['sections'][1]['components'][1];
$title = $node['sections'][1]['components'][2];
$this->assertStringContainsStringIgnoringCase(
'Powered by',
$first['block']['render']
);
$this->assertStringContainsStringIgnoringCase(
'This is the block content',
$second['block']['render']
);
$this->assertStringContainsStringIgnoringCase(
'My inline block content',
$third['block']['render']
);
$this->assertStringContainsStringIgnoringCase(
'The node body',
$body['block']['render']
);
$this->assertStringContainsStringIgnoringCase(
'The node title',
$title['block']['render']
);
}
/**
* Check layout builder field block has correct content.
*/
public function testComponentBlockField(): void {
$query = <<<GQL
query {
node(id: "{$this->node->uuid()}") {
... on NodeTest {
id
sections {
components {
block {
... on BlockField {
id
title
fieldName
field {
... on BlockFieldNodeTitle {
title
}
... on BlockFieldNodeTestBody {
body {
processed
}
}
}
}
}
}
}
}
}
}
GQL;
$content = $this->executeQuery($query);
$this->assertNotEmpty($content['data']['node']);
$node = $content['data']['node'];
$body = $node['sections'][1]['components'][1];
$title = $node['sections'][1]['components'][2];
$this->assertEquals('body', $body['block']['fieldName']);
$this->assertNull($body['block']['title']);
$this->assertStringContainsStringIgnoringCase(
'The node body',
$body['block']['field']['body']['processed']
);
$this->assertStringContainsStringIgnoringCase(
'The node title',
$title['block']['field']['title']
);
}
/**
* Check block content are typed correctly.
*/
public function testComponentBlockContent(): void {
$query = <<<GQL
query {
node(id: "{$this->node->uuid()}") {
... on NodeTest {
id
sections {
components {
block {
... on BlockContent {
id
title
entity {
... on BlockContentBasicBlock {
id
title
body {
processed
}
}
}
}
}
}
}
}
}
}
GQL;
$content = $this->executeQuery($query);
$this->assertNotEmpty($content['data']['node']);
$node = $content['data']['node'];
$second = $node['sections'][0]['components'][2];
$third = $node['sections'][0]['components'][1];
// Uses the default block.
$this->assertEquals($this->blockContent->uuid(), $second['block']['entity']['id']);
$this->assertStringContainsStringIgnoringCase(
'This is the block content',
$second['block']['entity']['body']['processed']
);
// Check the overridden content is applied.
$this->assertStringContainsStringIgnoringCase(
'My inline block content',
$third['block']['entity']['body']['processed']
);
}
/**
* Ensure a field isn't accessible if not exposed.
*/
public function testHiddenFieldNotAccessible(): void {
$this->setFieldConfig('node', 'test', 'body', [
'enabled' => FALSE,
]);
$query = <<<GQL
query {
node(id: "{$this->node->uuid()}") {
... on NodeTest {
id
sections {
components {
block {
... on BlockField {
id
title
render
field {
... on BlockFieldNodeId {
id
}
... on BlockFieldNodeTestBody {
body {
processed
}
}
}
}
}
}
}
}
}
}
GQL;
try {
$content = $this->executeQuery($query);
}
catch (\Exception) {
// Swallow error.
}
$this->assertStringContainsStringIgnoringCase(
'Unknown type "BlockFieldNodeTestBody"',
$content['errors'][0]['message'] ?? NULL
);
$node = $content['data']['node'] ?? NULL;
$body = $node['sections'][1]['components'][1] ?? NULL;
$this->assertNull($body);
}
}
