a5eacc5c53932169ea798209d8221c9628bfdd22
[yaffs-website] / web / core / modules / outside_in / tests / src / FunctionalJavascript / OutsideInBlockFormTest.php
1 <?php
2
3 namespace Drupal\Tests\outside_in\FunctionalJavascript;
4
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;
9
10 /**
11  * Testing opening and saving block forms in the off-canvas dialog.
12  *
13  * @group outside_in
14  */
15 class OutsideInBlockFormTest extends OutsideInJavascriptTestBase {
16
17   const TOOLBAR_EDIT_LINK_SELECTOR = '#toolbar-bar div.contextual-toolbar-tab button';
18
19   /**
20    * {@inheritdoc}
21    */
22   public static $modules = [
23     'node',
24     'block',
25     'system',
26     'breakpoint',
27     'toolbar',
28     'contextual',
29     'outside_in',
30     'quickedit',
31     'search',
32     'block_content',
33     // Add test module to override CSS pointer-events properties because they
34     // cause test failures.
35     'outside_in_test_css',
36   ];
37
38   /**
39    * {@inheritdoc}
40    */
41   protected function setUp() {
42     parent::setUp();
43
44     $this->createBlockContentType('basic', TRUE);
45     $block_content = $this->createBlockContent('Custom Block', 'basic', TRUE);
46     $user = $this->createUser([
47       'administer blocks',
48       'access contextual links',
49       'access toolbar',
50       'administer nodes',
51       'access in-place editing',
52       'search content',
53     ]);
54     $this->drupalLogin($user);
55     $this->placeBlock('block_content:' . $block_content->uuid(), ['id' => 'custom']);
56   }
57
58   /**
59    * Tests opening off-canvas dialog by click blocks and elements in the blocks.
60    *
61    * @dataProvider providerTestBlocks
62    */
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');
72
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'));
76
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.
82           $element->click();
83           $this->waitForNoElement("#toolbar-administration a.is-active");
84         }
85         $page->find('css', $toolbar_item)->click();
86         $web_assert->waitForElementVisible('css', "{$toolbar_item}.is-active");
87       }
88       $this->enableEditMode();
89       if (isset($toolbar_item)) {
90         $this->waitForNoElement("{$toolbar_item}.is-active");
91       }
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]');
98           break;
99
100         case 'system_branding_block':
101           // Fill out form, save the form.
102           $page->fillField('settings[site_information][site_name]', $new_page_text);
103           break;
104       }
105
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 . '"');
113       }
114
115       $this->openBlockForm($block_selector);
116
117       $this->disableEditMode();
118       // Canvas should close when editing module is closed.
119       $this->waitForOffCanvasToClose();
120
121       $this->enableEditMode();
122
123       // Open block form by clicking a element inside the block.
124       // This confirms that default action for links and form elements is
125       // suppressed.
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.
138       $block->delete();
139     }
140   }
141
142   /**
143    * Dataprovider for testBlocks().
144    */
145   public function providerTestBlocks() {
146     $blocks = [
147       'block-powered' => [
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',
154       ],
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',
162       ],
163       'block-search' => [
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,
170       ],
171     ];
172     return $blocks;
173   }
174
175   /**
176    * Enables edit mode by pressing edit button in the toolbar.
177    */
178   protected function enableEditMode() {
179     $this->pressToolbarEditButton();
180     $this->waitForToolbarToLoad();
181     $this->assertEditModeEnabled();
182   }
183
184   /**
185    * Disables edit mode by pressing edit button in the toolbar.
186    */
187   protected function disableEditMode() {
188     $this->pressToolbarEditButton();
189     $this->waitForToolbarToLoad();
190     $this->assertEditModeDisabled();
191   }
192
193   /**
194    * Asserts that Off-Canvas block form is valid.
195    */
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"]');
204   }
205
206   /**
207    * Open block form by clicking the element found with a css selector.
208    *
209    * @param string $block_selector
210    *   A css selector selects the block or an element within it.
211    */
212   protected function openBlockForm($block_selector) {
213     $this->waitForToolbarToLoad();
214     $this->click($block_selector);
215     $this->waitForOffCanvasToOpen();
216     $this->assertOffCanvasBlockFormIsValid();
217   }
218
219   /**
220    * Tests QuickEdit links behavior.
221    */
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',
232       'access content',
233     ]);
234     $node = $this->createNode(
235       [
236         'title' => 'Page One',
237         'type' => 'page',
238         'body' => [
239           [
240             'value' => 'Regular NODE body for the test.',
241             'format' => 'plain_text',
242           ],
243         ],
244       ]
245     );
246     $page = $this->getSession()->getPage();
247     $block_plugin = 'system_powered_by_block';
248
249     foreach ($this->getTestThemes() as $theme) {
250
251       $this->enableTheme($theme);
252
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();
266         }
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);
270
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();
283
284         $this->openBlockForm($block_selector);
285         // QuickEdit toolbar should be closed when opening Off-canvas dialog.
286         $web_assert->elementNotExists('css', $quick_edit_selector);
287       }
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();
305     }
306   }
307
308   /**
309    * Tests enabling and disabling Edit Mode.
310    */
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) {
319           // Enable Edit mode.
320           case 'contextual_link':
321             $this->clickContextualLink($this->getBlockSelector($block), "Quick edit");
322             $this->waitForOffCanvasToOpen();
323             $this->assertEditModeEnabled();
324             break;
325
326           case 'toolbar_link':
327             $this->enableEditMode();
328             break;
329         }
330         $this->disableEditMode();
331
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
336         // correctly.
337         $this->enableEditMode();
338         $this->disableEditMode();
339       }
340     }
341   }
342
343   /**
344    * Assert that edit mode has been properly enabled.
345    */
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');
354   }
355
356   /**
357    * Assert that edit mode has been properly disabled.
358    */
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"
368     // class.
369     $web_assert->elementNotExists('css', '.dialog-off-canvas__main-canvas.js-outside-in-edit-mode');
370   }
371
372   /**
373    * Press the toolbar Edit button provided by the contextual module.
374    */
375   protected function pressToolbarEditButton() {
376     $this->assertSession()->waitForElement('css', '[data-contextual-id] .contextual-links a');
377     $edit_button = $this->getSession()
378       ->getPage()
379       ->find('css', static::TOOLBAR_EDIT_LINK_SELECTOR);
380     $edit_button->press();
381   }
382
383   /**
384    * Creates a custom block.
385    *
386    * @param bool|string $title
387    *   (optional) Title of block. When no value is given uses a random name.
388    *   Defaults to FALSE.
389    * @param string $bundle
390    *   (optional) Bundle name. Defaults to 'basic'.
391    * @param bool $save
392    *   (optional) Whether to save the block. Defaults to TRUE.
393    *
394    * @return \Drupal\block_content\Entity\BlockContent
395    *   Created custom block.
396    */
397   protected function createBlockContent($title = FALSE, $bundle = 'basic', $save = TRUE) {
398     $title = $title ?: $this->randomName();
399     $block_content = BlockContent::create([
400       'info' => $title,
401       'type' => $bundle,
402       'langcode' => 'en',
403       'body' => [
404         'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
405         'format' => 'plain_text',
406       ],
407     ]);
408     if ($block_content && $save === TRUE) {
409       $block_content->save();
410     }
411     return $block_content;
412   }
413
414   /**
415    * Creates a custom block type (bundle).
416    *
417    * @param string $label
418    *   The block type label.
419    * @param bool $create_body
420    *   Whether or not to create the body field.
421    *
422    * @return \Drupal\block_content\Entity\BlockContentType
423    *   Created custom block type.
424    */
425   protected function createBlockContentType($label, $create_body = FALSE) {
426     $bundle = BlockContentType::create([
427       'id' => $label,
428       'label' => $label,
429       'revision' => FALSE,
430     ]);
431     $bundle->save();
432     if ($create_body) {
433       block_content_add_body_field($bundle->id());
434     }
435     return $bundle;
436   }
437
438   /**
439    * Tests that contextual links in custom blocks are changed.
440    *
441    * "Quick edit" is quickedit.module link.
442    * "Quick edit settings" is outside_in.module link.
443    */
444   public function testCustomBlockLinks() {
445     $this->drupalGet('user');
446     $page = $this->getSession()->getPage();
447     $links = $page->findAll('css', "#block-custom .contextual-links li a");
448     $link_labels = [];
449     /** @var \Behat\Mink\Element\NodeElement $link */
450     foreach ($links as $link) {
451       $link_labels[$link->getAttribute('href')] = $link->getText();
452     }
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);
457   }
458
459   /**
460    * Gets the block CSS selector.
461    *
462    * @param \Drupal\block\Entity\Block $block
463    *   The block.
464    *
465    * @return string
466    *   The CSS selector.
467    */
468   public function getBlockSelector(Block $block) {
469     return '#block-' . $block->id();
470   }
471
472 }