3 namespace Drupal\Tests\outside_in\FunctionalJavascript;
5 use Drupal\block\Entity\Block;
6 use Drupal\block_content\Entity\BlockContent;
7 use Drupal\block_content\Entity\BlockContentType;
8 use Drupal\user\Entity\Role;
11 * Testing opening and saving block forms in the off-canvas dialog.
15 class OutsideInBlockFormTest extends OutsideInJavascriptTestBase {
17 const TOOLBAR_EDIT_LINK_SELECTOR = '#toolbar-bar div.contextual-toolbar-tab button';
22 public static $modules = [
33 // Add test module to override CSS pointer-events properties because they
34 // cause test failures.
35 'outside_in_test_css',
41 protected function setUp() {
44 $this->createBlockContentType('basic', TRUE);
45 $block_content = $this->createBlockContent('Custom Block', 'basic', TRUE);
46 $user = $this->createUser([
48 'access contextual links',
51 'access in-place editing',
54 $this->drupalLogin($user);
55 $this->placeBlock('block_content:' . $block_content->uuid(), ['id' => 'custom']);
59 * Tests opening off-canvas dialog by click blocks and elements in the blocks.
61 * @dataProvider providerTestBlocks
63 public function testBlocks($block_plugin, $new_page_text, $element_selector, $label_selector, $button_text, $toolbar_item) {
64 $web_assert = $this->assertSession();
65 $page = $this->getSession()->getPage();
66 foreach ($this->getTestThemes() as $theme) {
67 $this->enableTheme($theme);
68 $block = $this->placeBlock($block_plugin);
69 $block_selector = str_replace('_', '-', $this->getBlockSelector($block));
70 $block_id = $block->id();
71 $this->drupalGet('user');
73 $link = $page->find('css', "$block_selector .contextual-links li a");
74 $this->assertEquals('Quick edit', $link->getText(), "'Quick edit' is the first contextual link for the block.");
75 $this->assertContains("/admin/structure/block/manage/$block_id/off-canvas?destination=user/2", $link->getAttribute('href'));
77 if (isset($toolbar_item)) {
78 // Check that you can open a toolbar tray and it will be closed after
79 // entering edit mode.
80 if ($element = $page->find('css', "#toolbar-administration a.is-active")) {
81 // If a tray was open from page load close it.
83 $this->waitForNoElement("#toolbar-administration a.is-active");
85 $page->find('css', $toolbar_item)->click();
86 $web_assert->waitForElementVisible('css', "{$toolbar_item}.is-active");
88 $this->enableEditMode();
89 if (isset($toolbar_item)) {
90 $this->waitForNoElement("{$toolbar_item}.is-active");
92 $this->openBlockForm($block_selector);
93 switch ($block_plugin) {
94 case 'system_powered_by_block':
95 // Fill out form, save the form.
96 $page->fillField('settings[label]', $new_page_text);
97 $page->checkField('settings[label_display]');
100 case 'system_branding_block':
101 // Fill out form, save the form.
102 $page->fillField('settings[site_information][site_name]', $new_page_text);
106 if (isset($new_page_text)) {
107 $page->pressButton($button_text);
108 // Make sure the changes are present.
109 // @todo Use a wait method that will take into account the form submitting
110 // and all JavaScript activity. https://www.drupal.org/node/2837676
111 // The use \Behat\Mink\WebAssert::pageTextContains to check text.
112 $this->assertJsCondition('jQuery("' . $block_selector . ' ' . $label_selector . '").html() == "' . $new_page_text . '"');
115 $this->openBlockForm($block_selector);
117 $this->disableEditMode();
118 // Canvas should close when editing module is closed.
119 $this->waitForOffCanvasToClose();
121 $this->enableEditMode();
123 // Open block form by clicking a element inside the block.
124 // This confirms that default action for links and form elements is
126 $this->openBlockForm("$block_selector {$element_selector}");
127 $web_assert->elementTextContains('css', '.contextual-toolbar-tab button', 'Editing');
128 $web_assert->elementAttributeContains('css', '.dialog-off-canvas__main-canvas', 'class', 'js-outside-in-edit-mode');
129 // Simulate press the Escape key.
130 $this->getSession()->executeScript('jQuery("body").trigger(jQuery.Event("keyup", { keyCode: 27 }));');
131 $this->waitForOffCanvasToClose();
132 $this->getSession()->wait(100);
133 $this->assertEditModeDisabled();
134 $web_assert->elementTextContains('css', '#drupal-live-announce', 'Exited edit mode.');
135 $web_assert->elementTextNotContains('css', '.contextual-toolbar-tab button', 'Editing');
136 $web_assert->elementAttributeNotContains('css', '.dialog-off-canvas__main-canvas', 'class', 'js-outside-in-edit-mode');
137 // Delete the block that was placed for the current theme.
143 * Dataprovider for testBlocks().
145 public function providerTestBlocks() {
148 'block_plugin' => 'system_powered_by_block',
149 'new_page_text' => 'Can you imagine anyone showing the label on this block?',
150 'element_selector' => 'span a',
151 'label_selector' => 'h2',
152 'button_text' => 'Save Powered by Drupal',
153 'toolbar_item' => '#toolbar-item-user',
155 'block-branding' => [
156 'block_plugin' => 'system_branding_block',
157 'new_page_text' => 'The site that will live a very short life.',
158 'element_selector' => "a[rel='home']:last-child",
159 'label_selector' => "a[rel='home']:last-child",
160 'button_text' => 'Save Site branding',
161 'toolbar_item' => '#toolbar-item-administration',
164 'block_plugin' => 'search_form_block',
165 'new_page_text' => NULL,
166 'element_selector' => '#edit-submit',
167 'label_selector' => 'h2',
168 'button_text' => 'Save Search form',
169 'toolbar_item' => NULL,
176 * Enables edit mode by pressing edit button in the toolbar.
178 protected function enableEditMode() {
179 $this->pressToolbarEditButton();
180 $this->waitForToolbarToLoad();
181 $this->assertEditModeEnabled();
185 * Disables edit mode by pressing edit button in the toolbar.
187 protected function disableEditMode() {
188 $this->pressToolbarEditButton();
189 $this->waitForToolbarToLoad();
190 $this->assertEditModeDisabled();
194 * Asserts that Off-Canvas block form is valid.
196 protected function assertOffCanvasBlockFormIsValid() {
197 $web_assert = $this->assertSession();
198 // Check that common block form elements exist.
199 $web_assert->elementExists('css', 'input[data-drupal-selector="edit-settings-label"]');
200 $web_assert->elementExists('css', 'input[data-drupal-selector="edit-settings-label-display"]');
201 // Check that advanced block form elements do not exist.
202 $web_assert->elementNotExists('css', 'input[data-drupal-selector="edit-visibility-request-path-pages"]');
203 $web_assert->elementNotExists('css', 'select[data-drupal-selector="edit-region"]');
207 * Open block form by clicking the element found with a css selector.
209 * @param string $block_selector
210 * A css selector selects the block or an element within it.
212 protected function openBlockForm($block_selector) {
213 $this->waitForToolbarToLoad();
214 $this->click($block_selector);
215 $this->waitForOffCanvasToOpen();
216 $this->assertOffCanvasBlockFormIsValid();
220 * Tests QuickEdit links behavior.
222 public function testQuickEditLinks() {
223 $quick_edit_selector = '#quickedit-entity-toolbar';
224 $node_selector = '[data-quickedit-entity-id="node/1"]';
225 $body_selector = '[data-quickedit-field-id="node/1/body/en/full"]';
226 $web_assert = $this->assertSession();
227 // Create a Content type and two test nodes.
228 $this->createContentType(['type' => 'page']);
229 $auth_role = Role::load(Role::AUTHENTICATED_ID);
230 $this->grantPermissions($auth_role, [
231 'edit any page content',
234 $node = $this->createNode(
236 'title' => 'Page One',
240 'value' => 'Regular NODE body for the test.',
241 'format' => 'plain_text',
246 $page = $this->getSession()->getPage();
247 $block_plugin = 'system_powered_by_block';
249 foreach ($this->getTestThemes() as $theme) {
251 $this->enableTheme($theme);
253 $block = $this->placeBlock($block_plugin);
254 $block_selector = str_replace('_', '-', $this->getBlockSelector($block));
255 // Load the same page twice.
256 foreach ([1, 2] as $page_load_times) {
257 $this->drupalGet('node/' . $node->id());
258 // Waiting for Toolbar module.
259 // @todo Remove the hack after https://www.drupal.org/node/2542050.
260 $web_assert->waitForElementVisible('css', '.toolbar-fixed');
261 // Waiting for Toolbar animation.
262 $web_assert->assertWaitOnAjaxRequest();
263 // The 2nd page load we should already be in edit mode.
264 if ($page_load_times == 1) {
265 $this->enableEditMode();
267 // In Edit mode clicking field should open QuickEdit toolbar.
268 $page->find('css', $body_selector)->click();
269 $web_assert->waitForElementVisible('css', $quick_edit_selector);
271 $this->disableEditMode();
272 // Exiting Edit mode should close QuickEdit toolbar.
273 $web_assert->elementNotExists('css', $quick_edit_selector);
274 // When not in Edit mode QuickEdit toolbar should not open.
275 $page->find('css', $body_selector)->click();
276 $web_assert->elementNotExists('css', $quick_edit_selector);
277 $this->enableEditMode();
278 $this->openBlockForm($block_selector);
279 $page->find('css', $body_selector)->click();
280 $web_assert->waitForElementVisible('css', $quick_edit_selector);
281 // Off-canvas dialog should be closed when opening QuickEdit toolbar.
282 $this->waitForOffCanvasToClose();
284 $this->openBlockForm($block_selector);
285 // QuickEdit toolbar should be closed when opening Off-canvas dialog.
286 $web_assert->elementNotExists('css', $quick_edit_selector);
288 // Check using contextual links to invoke QuickEdit and open the tray.
289 $this->drupalGet('node/' . $node->id());
290 $web_assert->assertWaitOnAjaxRequest();
291 $this->disableEditMode();
292 // Open QuickEdit toolbar before going into Edit mode.
293 $this->clickContextualLink($node_selector, "Quick edit");
294 $web_assert->waitForElementVisible('css', $quick_edit_selector);
295 // Open off-canvas and enter Edit mode via contextual link.
296 $this->clickContextualLink($block_selector, "Quick edit");
297 $this->waitForOffCanvasToOpen();
298 // QuickEdit toolbar should be closed when opening off-canvas dialog.
299 $web_assert->elementNotExists('css', $quick_edit_selector);
300 // Open QuickEdit toolbar via contextual link while in Edit mode.
301 $this->clickContextualLink($node_selector, "Quick edit", FALSE);
302 $this->waitForOffCanvasToClose();
303 $web_assert->waitForElementVisible('css', $quick_edit_selector);
304 $this->disableEditMode();
309 * Tests enabling and disabling Edit Mode.
311 public function testEditModeEnableDisable() {
312 foreach ($this->getTestThemes() as $theme) {
313 $this->enableTheme($theme);
314 $block = $this->placeBlock('system_powered_by_block');
315 foreach (['contextual_link', 'toolbar_link'] as $enable_option) {
316 $this->drupalGet('user');
317 $this->assertEditModeDisabled();
318 switch ($enable_option) {
320 case 'contextual_link':
321 $this->clickContextualLink($this->getBlockSelector($block), "Quick edit");
322 $this->waitForOffCanvasToOpen();
323 $this->assertEditModeEnabled();
327 $this->enableEditMode();
330 $this->disableEditMode();
332 // Make another page request to ensure Edit mode is still disabled.
333 $this->drupalGet('user');
334 $this->assertEditModeDisabled();
335 // Make sure on this page request it also re-enables and disables
337 $this->enableEditMode();
338 $this->disableEditMode();
344 * Assert that edit mode has been properly enabled.
346 protected function assertEditModeEnabled() {
347 $web_assert = $this->assertSession();
348 // No contextual triggers should be hidden.
349 $web_assert->elementNotExists('css', '.contextual .trigger.visually-hidden');
350 // The toolbar edit button should read "Editing".
351 $web_assert->elementContains('css', static::TOOLBAR_EDIT_LINK_SELECTOR, 'Editing');
352 // The main canvas element should have the "js-outside-in-edit-mode" class.
353 $web_assert->elementExists('css', '.dialog-off-canvas__main-canvas.js-outside-in-edit-mode');
357 * Assert that edit mode has been properly disabled.
359 protected function assertEditModeDisabled() {
360 $web_assert = $this->assertSession();
361 // Contextual triggers should be hidden.
362 $web_assert->elementExists('css', '.contextual .trigger.visually-hidden');
363 // No contextual triggers should be not hidden.
364 $web_assert->elementNotExists('css', '.contextual .trigger:not(.visually-hidden)');
365 // The toolbar edit button should read "Edit".
366 $web_assert->elementContains('css', static::TOOLBAR_EDIT_LINK_SELECTOR, 'Edit');
367 // The main canvas element should NOT have the "js-outside-in-edit-mode"
369 $web_assert->elementNotExists('css', '.dialog-off-canvas__main-canvas.js-outside-in-edit-mode');
373 * Press the toolbar Edit button provided by the contextual module.
375 protected function pressToolbarEditButton() {
376 $this->assertSession()->waitForElement('css', '[data-contextual-id] .contextual-links a');
377 $edit_button = $this->getSession()
379 ->find('css', static::TOOLBAR_EDIT_LINK_SELECTOR);
380 $edit_button->press();
384 * Creates a custom block.
386 * @param bool|string $title
387 * (optional) Title of block. When no value is given uses a random name.
389 * @param string $bundle
390 * (optional) Bundle name. Defaults to 'basic'.
392 * (optional) Whether to save the block. Defaults to TRUE.
394 * @return \Drupal\block_content\Entity\BlockContent
395 * Created custom block.
397 protected function createBlockContent($title = FALSE, $bundle = 'basic', $save = TRUE) {
398 $title = $title ?: $this->randomName();
399 $block_content = BlockContent::create([
404 'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
405 'format' => 'plain_text',
408 if ($block_content && $save === TRUE) {
409 $block_content->save();
411 return $block_content;
415 * Creates a custom block type (bundle).
417 * @param string $label
418 * The block type label.
419 * @param bool $create_body
420 * Whether or not to create the body field.
422 * @return \Drupal\block_content\Entity\BlockContentType
423 * Created custom block type.
425 protected function createBlockContentType($label, $create_body = FALSE) {
426 $bundle = BlockContentType::create([
433 block_content_add_body_field($bundle->id());
439 * Tests that contextual links in custom blocks are changed.
441 * "Quick edit" is quickedit.module link.
442 * "Quick edit settings" is outside_in.module link.
444 public function testCustomBlockLinks() {
445 $this->drupalGet('user');
446 $page = $this->getSession()->getPage();
447 $links = $page->findAll('css', "#block-custom .contextual-links li a");
449 /** @var \Behat\Mink\Element\NodeElement $link */
450 foreach ($links as $link) {
451 $link_labels[$link->getAttribute('href')] = $link->getText();
453 $href = array_search('Quick edit', $link_labels);
454 $this->assertEquals('', $href);
455 $href = array_search('Quick edit settings', $link_labels);
456 $this->assertTrue(strstr($href, '/admin/structure/block/manage/custom/off-canvas?destination=user/2') !== FALSE);
460 * Gets the block CSS selector.
462 * @param \Drupal\block\Entity\Block $block
468 public function getBlockSelector(Block $block) {
469 return '#block-' . $block->id();