3 namespace Drupal\Tests\layout_builder\FunctionalJavascript;
5 use Drupal\block_content\Entity\BlockContentType;
6 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
7 use Drupal\Tests\contextual\FunctionalJavascript\ContextualLinkClickTrait;
10 * Base class for testing inline blocks.
12 abstract class InlineBlockTestBase extends WebDriverTestBase {
14 use ContextualLinkClickTrait;
17 * Locator for inline blocks.
19 const INLINE_BLOCK_LOCATOR = '.block-inline-blockbasic';
22 * Path prefix for the field UI for the test bundle.
24 const FIELD_UI_PREFIX = 'admin/structure/types/manage/bundle_with_section_field';
29 public static $modules = [
35 // @todo Remove after https://www.drupal.org/project/drupal/issues/2901792.
42 * @var \Drupal\Core\Entity\EntityStorageInterface
44 protected $blockStorage;
49 protected function setUp() {
51 // @todo The Layout Builder UI relies on local tasks; fix in
52 // https://www.drupal.org/project/drupal/issues/2917777.
53 $this->drupalPlaceBlock('local_tasks_block');
55 $this->createContentType(['type' => 'bundle_with_section_field', 'new_revision' => TRUE]);
57 'type' => 'bundle_with_section_field',
58 'title' => 'The node title',
61 'value' => 'The node body',
66 'type' => 'bundle_with_section_field',
67 'title' => 'The node2 title',
70 'value' => 'The node2 body',
74 $this->createBlockContentType('basic', 'Basic block');
76 $this->blockStorage = $this->container->get('entity_type.manager')->getStorage('block_content');
80 * Saves a layout and asserts the message is correct.
82 protected function assertSaveLayout() {
83 $assert_session = $this->assertSession();
84 $assert_session->linkExists('Save Layout');
85 // Go to the Save Layout page. Currently there are random test failures if
86 // 'clickLink()' is used.
87 // @todo Convert tests that extend this class to NightWatch tests in
88 // https://www.drupal.org/node/2984161
89 $link = $this->getSession()->getPage()->findLink('Save Layout');
90 $this->drupalGet($link->getAttribute('href'));
91 $this->assertNotEmpty($assert_session->waitForElement('css', '.messages--status'));
93 if (stristr($this->getUrl(), 'admin/structure') === FALSE) {
94 $assert_session->pageTextContains('The layout override has been saved.');
97 $assert_session->pageTextContains('The layout has been saved.');
102 * Gets the latest block entity id.
104 protected function getLatestBlockEntityId() {
105 $block_ids = \Drupal::entityQuery('block_content')->sort('id', 'DESC')->range(0, 1)->execute();
106 $block_id = array_pop($block_ids);
107 $this->assertNotEmpty($this->blockStorage->load($block_id));
112 * Removes an entity block from the layout but does not save the layout.
114 protected function removeInlineBlockFromLayout() {
115 $assert_session = $this->assertSession();
116 $page = $this->getSession()->getPage();
117 $block_text = $page->find('css', static::INLINE_BLOCK_LOCATOR)->getText();
118 $this->assertNotEmpty($block_text);
119 $assert_session->pageTextContains($block_text);
120 $this->clickContextualLink(static::INLINE_BLOCK_LOCATOR, 'Remove block');
121 $assert_session->waitForElement('css', "#drupal-off-canvas input[value='Remove']");
122 $assert_session->assertWaitOnAjaxRequest();
123 $page->find('css', '#drupal-off-canvas')->pressButton('Remove');
124 $this->waitForNoElement('#drupal-off-canvas');
125 $this->waitForNoElement(static::INLINE_BLOCK_LOCATOR);
126 $assert_session->assertWaitOnAjaxRequest();
127 $assert_session->pageTextNotContains($block_text);
131 * Adds an entity block to the layout.
133 * @param string $title
134 * The title field value.
135 * @param string $body
136 * The body field value.
138 protected function addInlineBlockToLayout($title, $body) {
139 $assert_session = $this->assertSession();
140 $page = $this->getSession()->getPage();
141 $page->clickLink('Add Block');
142 $assert_session->assertWaitOnAjaxRequest();
143 $this->assertNotEmpty($assert_session->waitForLink('Create custom block'));
144 $this->clickLink('Create custom block');
145 $assert_session->assertWaitOnAjaxRequest();
146 $textarea = $assert_session->waitForElement('css', '[name="settings[block_form][body][0][value]"]');
147 $this->assertNotEmpty($textarea);
148 $assert_session->fieldValueEquals('Title', '');
149 $page->findField('Title')->setValue($title);
150 $textarea->setValue($body);
151 $page->pressButton('Add Block');
152 $this->assertDialogClosedAndTextVisible($body, static::INLINE_BLOCK_LOCATOR);
156 * Configures an inline block in the Layout Builder.
158 * @param string $old_body
159 * The old body field value.
160 * @param string $new_body
161 * The new body field value.
162 * @param string $block_css_locator
163 * The CSS locator to use to select the contextual link.
165 protected function configureInlineBlock($old_body, $new_body, $block_css_locator = NULL) {
166 $block_css_locator = $block_css_locator ?: static::INLINE_BLOCK_LOCATOR;
167 $assert_session = $this->assertSession();
168 $page = $this->getSession()->getPage();
169 $this->clickContextualLink($block_css_locator, 'Configure');
170 $textarea = $assert_session->waitForElementVisible('css', '[name="settings[block_form][body][0][value]"]');
171 $this->assertNotEmpty($textarea);
172 $this->assertSame($old_body, $textarea->getValue());
173 $textarea->setValue($new_body);
174 $page->pressButton('Update');
175 $this->waitForNoElement('#drupal-off-canvas');
176 $assert_session->assertWaitOnAjaxRequest();
177 $this->assertDialogClosedAndTextVisible($new_body);
181 * Waits for an element to be removed from the page.
183 * @param string $selector
185 * @param int $timeout
186 * (optional) Timeout in milliseconds, defaults to 10000.
188 * @todo Remove in https://www.drupal.org/node/2892440.
190 protected function waitForNoElement($selector, $timeout = 10000) {
191 $condition = "(typeof jQuery !== 'undefined' && jQuery('$selector').length === 0)";
192 $this->assertJsCondition($condition, $timeout);
196 * Asserts that the dialog closes and the new text appears on the main canvas.
198 * @param string $text
200 * @param string|null $css_locator
201 * The css locator to use inside the main canvas if any.
203 protected function assertDialogClosedAndTextVisible($text, $css_locator = NULL) {
204 $assert_session = $this->assertSession();
205 $this->waitForNoElement('#drupal-off-canvas');
206 $assert_session->assertWaitOnAjaxRequest();
207 $assert_session->elementNotExists('css', '#drupal-off-canvas');
209 $this->assertNotEmpty($assert_session->waitForElementVisible('css', ".dialog-off-canvas-main-canvas $css_locator:contains('$text')"));
212 $this->assertNotEmpty($assert_session->waitForElementVisible('css', ".dialog-off-canvas-main-canvas:contains('$text')"));
217 * Creates a block content type.
221 * @param string $label
222 * The block type label.
224 protected function createBlockContentType($id, $label) {
225 $bundle = BlockContentType::create([
231 block_content_add_body_field($bundle->id());