Pull merge.
[yaffs-website] / web / core / modules / media / tests / src / Functional / MediaUiFunctionalTest.php
1 <?php
2
3 namespace Drupal\Tests\media\Functional;
4
5 use Behat\Mink\Element\NodeElement;
6 use Drupal\media\Entity\Media;
7 use Drupal\Core\Field\FieldStorageDefinitionInterface;
8 use Drupal\Core\Url;
9 use Drupal\field\Entity\FieldConfig;
10 use Drupal\field\Entity\FieldStorageConfig;
11
12 /**
13  * Ensures that media UI works correctly.
14  *
15  * @group media
16  */
17 class MediaUiFunctionalTest extends MediaFunctionalTestBase {
18
19   /**
20    * Modules to enable.
21    *
22    * @var array
23    */
24   public static $modules = [
25     'block',
26     'media_test_source',
27   ];
28
29   /**
30    * {@inheritdoc}
31    */
32   protected function setUp() {
33     parent::setUp();
34     $this->drupalPlaceBlock('local_actions_block');
35     $this->drupalPlaceBlock('local_tasks_block');
36   }
37
38   /**
39    * Tests the media actions (add/edit/delete).
40    */
41   public function testMediaWithOnlyOneMediaType() {
42     $session = $this->getSession();
43     $page = $session->getPage();
44     $assert_session = $this->assertSession();
45
46     $media_type = $this->createMediaType('test', [
47       'queue_thumbnail_downloads' => FALSE,
48     ]);
49
50     $this->drupalGet('media/add');
51     $assert_session->statusCodeEquals(200);
52     $assert_session->addressEquals('media/add/' . $media_type->id());
53     $assert_session->elementNotExists('css', '#edit-revision');
54
55     // Tests media add form.
56     $media_name = $this->randomMachineName();
57     $page->fillField('name[0][value]', $media_name);
58     $revision_log_message = $this->randomString();
59     $page->fillField('revision_log_message[0][value]', $revision_log_message);
60     $source_field = $this->randomString();
61     $page->fillField('field_media_test[0][value]', $source_field);
62     $page->pressButton('Save');
63     $media_id = $this->container->get('entity_type.manager')
64       ->getStorage('media')
65       ->getQuery()
66       ->execute();
67     $media_id = reset($media_id);
68     /** @var \Drupal\media\MediaInterface $media */
69     $media = $this->container->get('entity_type.manager')
70       ->getStorage('media')
71       ->loadUnchanged($media_id);
72     $this->assertSame($media->getRevisionLogMessage(), $revision_log_message);
73     $this->assertSame($media->getName(), $media_name);
74     $this->drupalGet('media/' . $media_id);
75     $assert_session->titleEquals($media_name . ' | Drupal');
76
77     // Tests media edit form.
78     $media_type->setNewRevision(FALSE);
79     $media_type->save();
80     $media_name2 = $this->randomMachineName();
81     $this->drupalGet('media/' . $media_id . '/edit');
82     $assert_session->checkboxNotChecked('edit-revision');
83     $media_name = $this->randomMachineName();
84     $page->fillField('name[0][value]', $media_name2);
85     $page->pressButton('Save');
86     /** @var \Drupal\media\MediaInterface $media */
87     $media = $this->container->get('entity_type.manager')
88       ->getStorage('media')
89       ->loadUnchanged($media_id);
90     $this->assertSame($media->getName(), $media_name2);
91     $this->drupalGet('media/' . $media_id);
92     $assert_session->titleEquals($media_name2 . ' | Drupal');
93
94     // Test that there is no empty vertical tabs element, if the container is
95     // empty (see #2750697).
96     // Make the "Publisher ID" and "Created" fields hidden.
97     $this->drupalGet('/admin/structure/media/manage/' . $media_type->id() . '/form-display');
98     $page->selectFieldOption('fields[created][parent]', 'hidden');
99     $page->selectFieldOption('fields[uid][parent]', 'hidden');
100     $page->pressButton('Save');
101     // Assure we are testing with a user without permission to manage revisions.
102     $this->drupalLogin($this->nonAdminUser);
103     // Check the container is not present.
104     $this->drupalGet('media/' . $media_id . '/edit');
105     $assert_session->elementNotExists('css', 'input.vertical-tabs__active-tab');
106     // Continue testing as admin.
107     $this->drupalLogin($this->adminUser);
108
109     // Enable revisions by default.
110     $previous_revision_id = $media->getRevisionId();
111     $media_type->setNewRevision(TRUE);
112     $media_type->save();
113     $this->drupalGet('media/' . $media_id . '/edit');
114     $assert_session->checkboxChecked('edit-revision');
115     $page->fillField('name[0][value]', $media_name);
116     $page->fillField('revision_log_message[0][value]', $revision_log_message);
117     $page->pressButton('Save');
118     $this->drupalGet('media/' . $media_id);
119     $assert_session->titleEquals($media_name . ' | Drupal');
120     /** @var \Drupal\media\MediaInterface $media */
121     $media = $this->container->get('entity_type.manager')
122       ->getStorage('media')
123       ->loadUnchanged($media_id);
124     $this->assertSame($media->getRevisionLogMessage(), $revision_log_message);
125     $this->assertNotEquals($previous_revision_id, $media->getRevisionId());
126
127     // Test the status checkbox.
128     $this->drupalGet('media/' . $media_id . '/edit');
129     $page->uncheckField('status[value]');
130     $page->pressButton('Save');
131     /** @var \Drupal\media\MediaInterface $media */
132     $media = $this->container->get('entity_type.manager')
133       ->getStorage('media')
134       ->loadUnchanged($media_id);
135     $this->assertFalse($media->isPublished());
136
137     // Tests media delete form.
138     $this->drupalGet('media/' . $media_id . '/edit');
139     $page->clickLink('Delete');
140     $assert_session->pageTextContains('This action cannot be undone');
141     $page->pressButton('Delete');
142     $media_id = \Drupal::entityQuery('media')->execute();
143     $this->assertFalse($media_id);
144   }
145
146   /**
147    * Tests the "media/add" and "media/mid" pages.
148    *
149    * Tests if the "media/add" page gives you a selecting option if there are
150    * multiple media types available.
151    */
152   public function testMediaWithMultipleMediaTypes() {
153     $assert_session = $this->assertSession();
154
155     // Tests and creates the first media type.
156     $first_media_type = $this->createMediaType('test', ['description' => $this->randomMachineName()]);
157
158     // Test and create a second media type.
159     $second_media_type = $this->createMediaType('test', ['description' => $this->randomMachineName()]);
160
161     // Test if media/add displays two media type options.
162     $this->drupalGet('media/add');
163
164     // Checks for the first media type.
165     $assert_session->pageTextContains($first_media_type->label());
166     $assert_session->pageTextContains($first_media_type->getDescription());
167     // Checks for the second media type.
168     $assert_session->pageTextContains($second_media_type->label());
169     $assert_session->pageTextContains($second_media_type->getDescription());
170
171     // Continue testing media type filter.
172     $first_media_item = Media::create(['bundle' => $first_media_type->id()]);
173     $first_media_item->save();
174     $second_media_item = Media::create(['bundle' => $second_media_type->id()]);
175     $second_media_item->save();
176
177     // Go to first media item.
178     $this->drupalGet('media/' . $first_media_item->id());
179     $assert_session->statusCodeEquals(200);
180     $assert_session->pageTextContains($first_media_item->getName());
181     $assert_session->elementsCount('css', '.media--view-mode-full', 1);
182
183     // Go to second media item.
184     $this->drupalGet('media/' . $second_media_item->id());
185     $assert_session->statusCodeEquals(200);
186     $assert_session->pageTextContains($second_media_item->getName());
187   }
188
189   /**
190    * Test that media in ER fields use the Rendered Entity formatter by default.
191    */
192   public function testRenderedEntityReferencedMedia() {
193     $page = $this->getSession()->getPage();
194     $assert_session = $this->assertSession();
195
196     $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
197     $this->drupalGet('/admin/structure/types/manage/page/fields/add-field');
198     $page->selectFieldOption('new_storage_type', 'field_ui:entity_reference:media');
199     $page->fillField('label', 'Foo field');
200     $page->fillField('field_name', 'foo_field');
201     $page->pressButton('Save and continue');
202     $this->drupalGet('/admin/structure/types/manage/page/display');
203     $assert_session->fieldValueEquals('fields[field_foo_field][type]', 'entity_reference_entity_view');
204   }
205
206   /**
207    * Data provider for testMediaReferenceWidget().
208    *
209    * @return array[]
210    *   Test data. See testMediaReferenceWidget() for the child array structure.
211    */
212   public function providerTestMediaReferenceWidget() {
213     return [
214       // Single-value fields with a single media type and the default widget:
215       // - The user can create and list the media.
216       'single_value:single_type:create_list' => [1, [TRUE], TRUE],
217       // - The user can list but not create the media.
218       'single_value:single_type:list' => [1, [FALSE], TRUE],
219       // - The user can create but not list the media.
220       'single_value:single_type:create' => [1, [TRUE], FALSE],
221       // - The user can neither create nor list the media.
222       'single_value:single_type' => [1, [FALSE], FALSE],
223
224       // Single-value fields with the tags-style widget:
225       // - The user can create and list the media.
226       'single_value:single_type:create_list:tags' => [1, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
227       // - The user can list but not create the media.
228       'single_value:single_type:list:tags' => [1, [FALSE], TRUE, 'entity_reference_autocomplete_tags'],
229       // - The user can create but not list the media.
230       'single_value:single_type:create:tags' => [1, [TRUE], FALSE, 'entity_reference_autocomplete_tags'],
231       // - The user can neither create nor list the media.
232       'single_value:single_type:tags' => [1, [FALSE], FALSE, 'entity_reference_autocomplete_tags'],
233
234       // Single-value fields with two media types:
235       // - The user can create both types.
236       'single_value:two_type:create2_list' => [1, [TRUE, TRUE], TRUE],
237       // - The user can create only one type.
238       'single_value:two_type:create1_list' => [1, [TRUE, FALSE], TRUE],
239       // - The user cannot create either type.
240       'single_value:two_type:list' => [1, [FALSE, FALSE], TRUE],
241
242       // Multiple-value field with a cardinality of 3, with media the user can
243       // create and list.
244       'multi_value:single_type:create_list' => [3, [TRUE], TRUE],
245       // The same, with the tags field.
246       'multi-value:single_type:create_list:tags' => [3, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
247
248       // Unlimited value field.
249       'unlimited_value:single_type:create_list' => [FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, [TRUE], TRUE],
250       // Unlimited value field with the tags widget.
251       'unlimited_value:single_type:create_list' => [FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, [TRUE], TRUE, 'entity_reference_autocomplete_tags'],
252     ];
253   }
254
255   /**
256    * Tests the default autocomplete widgets for media reference fields.
257    *
258    * @param int $cardinality
259    *   The field cardinality.
260    * @param bool[] $media_type_create_access
261    *   An array of booleans indicating whether to grant the test user create
262    *   access for each media type. A media type is created automatically for
263    *   each; for example, an array [TRUE, FALSE] would create two media types,
264    *   one that allows the user to create media and a second that does not.
265    * @param bool $list_access
266    *   Whether to grant the test user access to list media.
267    *
268    * @see media_field_widget_entity_reference_autocomplete_form_alter()
269    * @see media_field_widget_multiple_entity_reference_autocomplete_form_alter()
270    *
271    * @dataProvider providerTestMediaReferenceWidget
272    */
273   public function testMediaReferenceWidget($cardinality, array $media_type_create_access, $list_access, $widget_id = 'entity_reference_autocomplete') {
274     $assert_session = $this->assertSession();
275
276     // Create two content types.
277     $non_media_content_type = $this->createContentType();
278     $content_type = $this->createContentType();
279
280     // Create some media types.
281     $media_types = [];
282     $permissions = [];
283     $create_media_types = [];
284     foreach ($media_type_create_access as $id => $access) {
285       if ($access) {
286         $create_media_types[] = "media_type_$id";
287         $permissions[] = "create media_type_$id media";
288       }
289       $this->createMediaType('test', [
290         'id' => "media_type_$id",
291         'label' => "media_type_$id",
292       ]);
293       $media_types["media_type_$id"] = "media_type_$id";
294     }
295
296     // Create a user that can create content of the type, with other
297     // permissions as given by the data provider.
298     $permissions[] = "create {$content_type->id()} content";
299     if ($list_access) {
300       $permissions[] = "access media overview";
301     }
302     $test_user = $this->drupalCreateUser($permissions);
303
304     // Create a non-media entity reference.
305     $non_media_storage = FieldStorageConfig::create([
306       'field_name' => 'field_not_a_media_field',
307       'entity_type' => 'node',
308       'type' => 'entity_reference',
309       'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
310       'settings' => [
311         'target_type' => 'node',
312       ],
313     ]);
314     $non_media_storage->save();
315     $non_media_field = FieldConfig::create([
316       'label' => 'No media here!',
317       'field_storage' => $non_media_storage,
318       'entity_type' => 'node',
319       'bundle' => $non_media_content_type->id(),
320       'settings' => [
321         'handler' => 'default',
322         'handler_settings' => [
323           'target_bundles' => [
324             $non_media_content_type->id() => $non_media_content_type->id(),
325           ],
326         ],
327       ],
328     ]);
329     $non_media_field->save();
330     \Drupal::entityTypeManager()
331       ->getStorage('entity_form_display')
332       ->load('node.' . $non_media_content_type->id() . '.default')
333       ->setComponent('field_not_a_media_field', [
334         'type' => $widget_id,
335       ])
336       ->save();
337
338     // Create a media field through the user interface to ensure that the
339     // help text handling does not break the default value entry on the field
340     // settings form.
341     // Using drupalPostForm() to avoid dealing with JavaScript on the previous
342     // page in the field creation.
343     $edit = [
344       'new_storage_type' => 'field_ui:entity_reference:media',
345       'label' => "Media (cardinality $cardinality)",
346       'field_name' => 'media_reference',
347     ];
348     $this->drupalPostForm("admin/structure/types/manage/{$content_type->id()}/fields/add-field", $edit, 'Save and continue');
349     $edit = [];
350     foreach ($media_types as $type) {
351       $edit["settings[handler_settings][target_bundles][$type]"] = TRUE;
352     }
353     $this->drupalPostForm("admin/structure/types/manage/{$content_type->id()}/fields/node.{$content_type->id()}.field_media_reference", $edit, "Save settings");
354     \Drupal::entityTypeManager()
355       ->getStorage('entity_form_display')
356       ->load('node.' . $content_type->id() . '.default')
357       ->setComponent('field_media_reference', [
358         'type' => $widget_id,
359       ])
360       ->save();
361
362     // Some of the expected texts.
363     $create_help = 'Create your media on the media add page (opens a new window), then add it by name to the field below.';
364     $list_text = 'See the media list (opens a new window) to help locate media.';
365     $use_help = 'Type part of the media name.';
366     $create_header = "Create new media";
367     $use_header = "Use existing media";
368
369     // First check that none of the help texts are on the non-media content.
370     $this->drupalGet("/node/add/{$non_media_content_type->id()}");
371     $this->assertNoHelpTexts([
372       $create_header,
373       $create_help,
374       $use_header,
375       $use_help,
376       $list_text,
377       'Allowed media types:',
378     ]);
379
380     // Now, check that the widget displays the expected help text under the
381     // given conditions for the test user.
382     $this->drupalLogin($test_user);
383     $this->drupalGet("/node/add/{$content_type->id()}");
384
385     // Specific expected help texts for the media field.
386     $create_header = "Create new media";
387     $use_header = "Use existing media";
388     $type_list = 'Allowed media types: ' . implode(", ", array_keys($media_types));
389
390     $fieldset_selector = '#edit-field-media-reference-wrapper fieldset';
391     $fieldset = $assert_session->elementExists('css', $fieldset_selector);
392
393     $this->assertSame("Media (cardinality $cardinality)", $assert_session->elementExists('css', 'legend', $fieldset)->getText());
394
395     // Assert text that should be displayed regardless of other access.
396     $this->assertHelpTexts([$use_header, $use_help, $type_list], $fieldset_selector);
397
398     // The entire section for creating new media should only be displayed if
399     // the user can create at least one media of the type.
400     if ($create_media_types) {
401       if (count($create_media_types) === 1) {
402         $url = Url::fromRoute('entity.media.add_form')->setRouteParameter('media_type', $create_media_types[0]);
403       }
404       else {
405         $url = Url::fromRoute('entity.media.add_page');
406       }
407       $this->assertHelpTexts([$create_header, $create_help], $fieldset_selector);
408       $this->assertHelpLink(
409         $fieldset,
410         'media add page',
411         [
412           'target' => '_blank',
413           'href' => $url->toString(),
414         ]
415       );
416     }
417     else {
418       $this->assertNoHelpTexts([$create_header, $create_help]);
419       $this->assertNoHelpLink($fieldset, 'media add page');
420     }
421
422     if ($list_access) {
423       $this->assertHelpTexts([$list_text], $fieldset_selector);
424       $this->assertHelpLink(
425         $fieldset,
426         'media list',
427         [
428           'target' => '_blank',
429           'href' => Url::fromRoute('entity.media.collection')->toString(),
430         ]
431       );
432     }
433     else {
434       $this->assertNoHelpTexts([$list_text]);
435       $this->assertNoHelpLink($fieldset, 'media list');
436     }
437   }
438
439   /**
440    * Tests the redirect URL after creating a media item.
441    */
442   public function testMediaCreateRedirect() {
443     $session = $this->getSession();
444     $page = $session->getPage();
445     $assert_session = $this->assertSession();
446
447     $this->createMediaType('test', [
448       'queue_thumbnail_downloads' => FALSE,
449     ]);
450
451     // Test a redirect to the media canonical URL for a user without the 'access
452     // media overview' permission.
453     $this->drupalLogin($this->drupalCreateUser([
454       'view media',
455       'create media',
456     ]));
457     $this->drupalGet('media/add');
458     $page->fillField('name[0][value]', $this->randomMachineName());
459     $page->fillField('field_media_test[0][value]', $this->randomString());
460     $page->pressButton('Save');
461     $media_id = $this->container->get('entity_type.manager')
462       ->getStorage('media')
463       ->getQuery()
464       ->execute();
465     $media_id = reset($media_id);
466     $assert_session->addressEquals('media/' . $media_id);
467
468     // Test a redirect to the media overview for a user with the 'access media
469     // overview' permission.
470     $this->drupalLogin($this->drupalCreateUser([
471       'view media',
472       'create media',
473       'access media overview',
474     ]));
475     $this->drupalGet('media/add');
476     $page->fillField('name[0][value]', $this->randomMachineName());
477     $page->fillField('field_media_test[0][value]', $this->randomString());
478     $page->pressButton('Save');
479     $assert_session->addressEquals('admin/content/media');
480   }
481
482   /**
483    * Asserts that the given texts are present exactly once.
484    *
485    * @param string[] $texts
486    *   A list of the help texts to check.
487    * @param string $selector
488    *   (optional) The selector to search.
489    */
490   public function assertHelpTexts(array $texts, $selector = '') {
491     $assert_session = $this->assertSession();
492     foreach ($texts as $text) {
493       // We only want to escape single quotes, so use str_replace() rather than
494       // addslashes().
495       $text = str_replace("'", "\'", $text);
496       if ($selector) {
497         $assert_session->elementsCount('css', $selector . ":contains('$text')", 1);
498       }
499       else {
500         $assert_session->pageTextContains($text);
501       }
502     }
503   }
504
505   /**
506    * Asserts that none of the given texts are present.
507    *
508    * @param string[] $texts
509    *   A list of the help texts to check.
510    */
511   public function assertNoHelpTexts(array $texts) {
512     $assert_session = $this->assertSession();
513     foreach ($texts as $text) {
514       $assert_session->pageTextNotContains($text);
515     }
516   }
517
518   /**
519    * Asserts whether a given link is present.
520    *
521    * @param \Behat\Mink\Element\NodeElement $element
522    *   The element to search.
523    * @param string $text
524    *   The link text.
525    * @param string[] $attributes
526    *   An associative array of any expected attributes, keyed by the
527    *   attribute name.
528    */
529   protected function assertHelpLink(NodeElement $element, $text, array $attributes = []) {
530     // Find all the links inside the element.
531     $link = $element->findLink($text);
532
533     $this->assertNotEmpty($link);
534     foreach ($attributes as $attribute => $value) {
535       $this->assertSame($link->getAttribute($attribute), $value);
536     }
537   }
538
539   /**
540    * Asserts that a given link is not present.
541    *
542    * @param \Behat\Mink\Element\NodeElement $element
543    *   The element to search.
544    * @param string $text
545    *   The link text.
546    */
547   protected function assertNoHelpLink(NodeElement $element, $text) {
548     $assert_session = $this->assertSession();
549     // Assert that the link and its text are not present anywhere on the page.
550     $assert_session->elementNotExists('named', ['link', $text], $element);
551     $assert_session->pageTextNotContains($text);
552   }
553
554   /**
555    * Test the media collection route.
556    */
557   public function testMediaCollectionRoute() {
558     /** @var \Drupal\Core\Entity\EntityStorageInterface $media_storage */
559     $media_storage = $this->container->get('entity_type.manager')->getStorage('media');
560
561     $this->container->get('module_installer')->uninstall(['views']);
562
563     // Create a media type and media item.
564     $media_type = $this->createMediaType('test');
565     $media = $media_storage->create([
566       'bundle' => $media_type->id(),
567       'name' => 'Unnamed',
568     ]);
569     $media->save();
570
571     $this->drupalGet($media->toUrl('collection'));
572
573     $assert_session = $this->assertSession();
574
575     // Media list table exists.
576     $assert_session->elementExists('css', 'th:contains("Media Name")');
577     $assert_session->elementExists('css', 'th:contains("Type")');
578     $assert_session->elementExists('css', 'th:contains("Operations")');
579     // Media item is present.
580     $assert_session->elementExists('css', 'td:contains("Unnamed")');
581   }
582
583 }