Updated to Drupal 8.6.4, which is PHP 7.3 friendly. Also updated HTMLaw library....
[yaffs-website] / web / core / modules / field / tests / src / Functional / EntityReference / EntityReferenceAdminTest.php
1 <?php
2
3 namespace Drupal\Tests\field\Functional\EntityReference;
4
5 use Behat\Mink\Element\NodeElement;
6 use Drupal\Core\Field\FieldStorageDefinitionInterface;
7 use Drupal\field\Entity\FieldConfig;
8 use Drupal\node\Entity\Node;
9 use Drupal\taxonomy\Entity\Vocabulary;
10 use Drupal\Tests\BrowserTestBase;
11 use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
12
13 /**
14  * Tests for the administrative UI.
15  *
16  * @group entity_reference
17  */
18 class EntityReferenceAdminTest extends BrowserTestBase {
19
20   use FieldUiTestTrait;
21
22   /**
23    * Modules to install.
24    *
25    * Enable path module to ensure that the selection handler does not fail for
26    * entities with a path field.
27    * Enable views_ui module to see the no_view_help text.
28    *
29    * @var array
30    */
31   public static $modules = ['node', 'field_ui', 'path', 'taxonomy', 'block', 'views_ui'];
32
33   /**
34    * The name of the content type created for testing purposes.
35    *
36    * @var string
37    */
38   protected $type;
39
40   /**
41    * {@inheritdoc}
42    */
43   protected function setUp() {
44     parent::setUp();
45     $this->drupalPlaceBlock('system_breadcrumb_block');
46
47     // Create a content type, with underscores.
48     $type_name = strtolower($this->randomMachineName(8)) . '_test';
49     $type = $this->drupalCreateContentType(['name' => $type_name, 'type' => $type_name]);
50     $this->type = $type->id();
51
52     // Create test user.
53     $admin_user = $this->drupalCreateUser([
54       'access content',
55       'administer node fields',
56       'administer node display',
57       'administer views',
58       'create ' . $type_name . ' content',
59       'edit own ' . $type_name . ' content',
60     ]);
61     $this->drupalLogin($admin_user);
62   }
63
64   /**
65    * Tests the Entity Reference Admin UI.
66    */
67   public function testFieldAdminHandler() {
68     $bundle_path = 'admin/structure/types/manage/' . $this->type;
69     // Create a new view and display it as a entity reference.
70     $edit = [
71       'id' => 'node_test_view',
72       'label' => 'Node Test View',
73       'show[wizard_key]' => 'node',
74       'show[sort]' => 'none',
75       'page[create]' => 1,
76       'page[title]' => 'Test Node View',
77       'page[path]' => 'test/node/view',
78       'page[style][style_plugin]' => 'default',
79       'page[style][row_plugin]' => 'fields',
80     ];
81     $this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit'));
82     $this->drupalPostForm(NULL, [], t('Duplicate as Entity Reference'));
83     $this->clickLink(t('Settings'));
84     $edit = [
85       'style_options[search_fields][title]' => 'title',
86     ];
87     $this->drupalPostForm(NULL, $edit, t('Apply'));
88
89     // Set sort to NID ascending.
90     $edit = [
91       'name[node_field_data.nid]' => 1,
92     ];
93     $this->drupalPostForm('admin/structure/views/nojs/add-handler/node_test_view/entity_reference_1/sort', $edit, t('Add and configure sort criteria'));
94     $this->drupalPostForm(NULL, NULL, t('Apply'));
95
96     $this->drupalPostForm('admin/structure/views/view/node_test_view/edit/entity_reference_1', [], t('Save'));
97     $this->clickLink(t('Settings'));
98
99     // Create a test entity reference field.
100     $field_name = 'test_entity_ref_field';
101     $edit = [
102       'new_storage_type' => 'field_ui:entity_reference:node',
103       'label' => 'Test Entity Reference Field',
104       'field_name' => $field_name,
105     ];
106     $this->drupalPostForm($bundle_path . '/fields/add-field', $edit, t('Save and continue'));
107
108     // Set to unlimited.
109     $edit = [
110       'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
111     ];
112     $this->drupalPostForm(NULL, $edit, t('Save field settings'));
113
114     // Add the view to the test field.
115     $edit = [
116       'settings[handler]' => 'views',
117     ];
118     $this->drupalPostForm(NULL, $edit, t('Change handler'));
119     $edit = [
120       'required' => FALSE,
121       'settings[handler_settings][view][view_and_display]' => 'node_test_view:entity_reference_1',
122     ];
123     $this->drupalPostForm(NULL, $edit, t('Save settings'));
124
125     // Create nodes.
126     $node1 = Node::create([
127       'type' => $this->type,
128       'title' => 'Foo Node',
129     ]);
130     $node1->save();
131     $node2 = Node::create([
132       'type' => $this->type,
133       'title' => 'Foo Node',
134     ]);
135     $node2->save();
136
137     // Try to add a new node and fill the entity reference field.
138     $this->drupalGet('node/add/' . $this->type);
139     $result = $this->xpath('//input[@name="field_test_entity_ref_field[0][target_id]" and contains(@data-autocomplete-path, "/entity_reference_autocomplete/node/views/")]');
140     $target_url = $this->getAbsoluteUrl($result[0]->getAttribute('data-autocomplete-path'));
141     $this->drupalGet($target_url, ['query' => ['q' => 'Foo']]);
142     $this->assertRaw($node1->getTitle() . ' (' . $node1->id() . ')');
143     $this->assertRaw($node2->getTitle() . ' (' . $node2->id() . ')');
144
145     // Try to add a new node, fill the entity reference field and submit the
146     // form.
147     $this->drupalPostForm('node/add/' . $this->type, [], t('Add another item'));
148     $edit = [
149       'title[0][value]' => 'Example',
150       'field_test_entity_ref_field[0][target_id]' => 'Foo Node (' . $node1->id() . ')',
151       'field_test_entity_ref_field[1][target_id]' => 'Foo Node (' . $node2->id() . ')',
152     ];
153     $this->drupalPostForm(NULL, $edit, t('Save'));
154     $this->assertResponse(200);
155
156     $edit = [
157       'title[0][value]' => 'Example',
158       'field_test_entity_ref_field[0][target_id]' => 'Test',
159     ];
160     $this->drupalPostForm('node/add/' . $this->type, $edit, t('Save'));
161
162     // Assert that entity reference autocomplete field is validated.
163     $this->assertText(t('There are no entities matching "@entity"', ['@entity' => 'Test']));
164
165     $edit = [
166       'title[0][value]' => 'Test',
167       'field_test_entity_ref_field[0][target_id]' => $node1->getTitle(),
168     ];
169     $this->drupalPostForm('node/add/' . $this->type, $edit, t('Save'));
170
171     // Assert the results multiple times to avoid sorting problem of nodes with
172     // the same title.
173     $this->assertText(t('Multiple entities match this reference;'));
174     $this->assertText(t("@node1", ['@node1' => $node1->getTitle() . ' (' . $node1->id() . ')']));
175     $this->assertText(t("@node2", ['@node2' => $node2->getTitle() . ' (' . $node2->id() . ')']));
176     $this->assertText(t('Specify the one you want by appending the id in parentheses, like "@example".', ['@example' => $node2->getTitle() . ' (' . $node2->id() . ')']));
177
178     $edit = [
179       'title[0][value]' => 'Test',
180       'field_test_entity_ref_field[0][target_id]' => $node1->getTitle() . ' (' . $node1->id() . ')',
181     ];
182     $this->drupalPostForm('node/add/' . $this->type, $edit, t('Save'));
183     $this->assertLink($node1->getTitle());
184
185     // Tests adding default values to autocomplete widgets.
186     Vocabulary::create(['vid' => 'tags', 'name' => 'tags'])->save();
187     $taxonomy_term_field_name = $this->createEntityReferenceField('taxonomy_term', ['tags']);
188     $field_path = 'node.' . $this->type . '.field_' . $taxonomy_term_field_name;
189     $this->drupalGet($bundle_path . '/fields/' . $field_path . '/storage');
190     $edit = [
191       'cardinality' => -1,
192     ];
193     $this->drupalPostForm(NULL, $edit, t('Save field settings'));
194     $this->drupalGet($bundle_path . '/fields/' . $field_path);
195     $term_name = $this->randomString();
196     $result = \Drupal::entityQuery('taxonomy_term')
197       ->condition('name', $term_name)
198       ->condition('vid', 'tags')
199       ->accessCheck(FALSE)
200       ->execute();
201     $this->assertIdentical(0, count($result), "No taxonomy terms exist with the name '$term_name'.");
202     $edit = [
203       // This must be set before new entities will be auto-created.
204       'settings[handler_settings][auto_create]' => 1,
205     ];
206     $this->drupalPostForm(NULL, $edit, t('Save settings'));
207     $this->drupalGet($bundle_path . '/fields/' . $field_path);
208     $edit = [
209       // A term that doesn't yet exist.
210       'default_value_input[field_' . $taxonomy_term_field_name . '][0][target_id]' => $term_name,
211     ];
212     $this->drupalPostForm(NULL, $edit, t('Save settings'));
213     // The term should now exist.
214     $result = \Drupal::entityQuery('taxonomy_term')
215       ->condition('name', $term_name)
216       ->condition('vid', 'tags')
217       ->accessCheck(FALSE)
218       ->execute();
219     $this->assertIdentical(1, count($result), 'Taxonomy term was auto created when set as field default.');
220   }
221
222   /**
223    * Tests the formatters for the Entity References.
224    */
225   public function testAvailableFormatters() {
226     // Create a new vocabulary.
227     Vocabulary::create(['vid' => 'tags', 'name' => 'tags'])->save();
228
229     // Create entity reference field with taxonomy term as a target.
230     $taxonomy_term_field_name = $this->createEntityReferenceField('taxonomy_term', ['tags']);
231
232     // Create entity reference field with user as a target.
233     $user_field_name = $this->createEntityReferenceField('user');
234
235     // Create entity reference field with node as a target.
236     $node_field_name = $this->createEntityReferenceField('node', [$this->type]);
237
238     // Create entity reference field with date format as a target.
239     $date_format_field_name = $this->createEntityReferenceField('date_format');
240
241     // Display all newly created Entity Reference configuration.
242     $this->drupalGet('admin/structure/types/manage/' . $this->type . '/display');
243
244     // Check for Taxonomy Term select box values.
245     // Test if Taxonomy Term Entity Reference Field has the correct formatters.
246     $this->assertFieldSelectOptions('fields[field_' . $taxonomy_term_field_name . '][type]', [
247       'entity_reference_label',
248       'entity_reference_entity_id',
249       'entity_reference_rss_category',
250       'entity_reference_entity_view',
251     ]);
252
253     // Test if User Reference Field has the correct formatters.
254     // Author should be available for this field.
255     // RSS Category should not be available for this field.
256     $this->assertFieldSelectOptions('fields[field_' . $user_field_name . '][type]', [
257       'author',
258       'entity_reference_entity_id',
259       'entity_reference_entity_view',
260       'entity_reference_label',
261     ]);
262
263     // Test if Node Entity Reference Field has the correct formatters.
264     // RSS Category should not be available for this field.
265     $this->assertFieldSelectOptions('fields[field_' . $node_field_name . '][type]', [
266       'entity_reference_label',
267       'entity_reference_entity_id',
268       'entity_reference_entity_view',
269     ]);
270
271     // Test if Date Format Reference Field has the correct formatters.
272     // RSS Category & Entity View should not be available for this field.
273     // This could be any field without a ViewBuilder.
274     $this->assertFieldSelectOptions('fields[field_' . $date_format_field_name . '][type]', [
275       'entity_reference_label',
276       'entity_reference_entity_id',
277     ]);
278   }
279
280   /**
281    * Tests field settings for an entity reference field when the field has
282    * multiple target bundles and is set to auto-create the target entity.
283    */
284   public function testMultipleTargetBundles() {
285     /** @var \Drupal\taxonomy\Entity\Vocabulary[] $vocabularies */
286     $vocabularies = [];
287     for ($i = 0; $i < 2; $i++) {
288       $vid = mb_strtolower($this->randomMachineName());
289       $vocabularies[$i] = Vocabulary::create([
290         'name' => $this->randomString(),
291         'vid' => $vid,
292       ]);
293       $vocabularies[$i]->save();
294     }
295
296     // Create a new field pointing to the first vocabulary.
297     $field_name = $this->createEntityReferenceField('taxonomy_term', [$vocabularies[0]->id()]);
298     $field_name = "field_$field_name";
299     $field_id = 'node.' . $this->type . '.' . $field_name;
300     $path = 'admin/structure/types/manage/' . $this->type . '/fields/' . $field_id;
301
302     $this->drupalGet($path);
303
304     // Expect that there's no 'auto_create_bundle' selected.
305     $this->assertNoFieldByName('settings[handler_settings][auto_create_bundle]');
306
307     $edit = [
308       'settings[handler_settings][target_bundles][' . $vocabularies[1]->id() . ']' => TRUE,
309     ];
310     // Enable the second vocabulary as a target bundle.
311     $this->drupalPostForm($path, $edit, 'Save settings');
312     $this->drupalGet($path);
313     // Expect a select element with the two vocabularies as options.
314     $this->assertFieldByXPath("//select[@name='settings[handler_settings][auto_create_bundle]']/option[@value='" . $vocabularies[0]->id() . "']");
315     $this->assertFieldByXPath("//select[@name='settings[handler_settings][auto_create_bundle]']/option[@value='" . $vocabularies[1]->id() . "']");
316
317     $edit = [
318       'settings[handler_settings][auto_create]' => TRUE,
319       'settings[handler_settings][auto_create_bundle]' => $vocabularies[1]->id(),
320     ];
321     $this->drupalPostForm(NULL, $edit, t('Save settings'));
322
323     /** @var \Drupal\field\Entity\FieldConfig $field_config */
324     $field_config = FieldConfig::load($field_id);
325     // Expect that the target bundle has been saved in the backend.
326     $this->assertEqual($field_config->getSetting('handler_settings')['auto_create_bundle'], $vocabularies[1]->id());
327
328     // Delete the other bundle. Field config should not be affected.
329     $vocabularies[0]->delete();
330     $field_config = FieldConfig::load($field_id);
331     $this->assertTrue($field_config->getSetting('handler_settings')['auto_create']);
332     $this->assertIdentical($field_config->getSetting('handler_settings')['auto_create_bundle'], $vocabularies[1]->id());
333
334     // Delete the bundle set for entity auto-creation. Auto-created settings
335     // should be reset (no auto-creation).
336     $vocabularies[1]->delete();
337     $field_config = FieldConfig::load($field_id);
338     $this->assertFalse($field_config->getSetting('handler_settings')['auto_create']);
339     $this->assertFalse(isset($field_config->getSetting('handler_settings')['auto_create_bundle']));
340   }
341
342   /**
343    * Creates a new Entity Reference fields with a given target type.
344    *
345    * @param string $target_type
346    *   The name of the target type
347    * @param string[] $bundles
348    *   A list of bundle IDs. Defaults to [].
349    *
350    * @return string
351    *   Returns the generated field name
352    */
353   protected function createEntityReferenceField($target_type, $bundles = []) {
354     // Generates a bundle path for the newly created content type.
355     $bundle_path = 'admin/structure/types/manage/' . $this->type;
356
357     // Generate a random field name, must be only lowercase characters.
358     $field_name = strtolower($this->randomMachineName());
359
360     $storage_edit = $field_edit = [];
361     $storage_edit['settings[target_type]'] = $target_type;
362     foreach ($bundles as $bundle) {
363       $field_edit['settings[handler_settings][target_bundles][' . $bundle . ']'] = TRUE;
364     }
365
366     $this->fieldUIAddNewField($bundle_path, $field_name, NULL, 'entity_reference', $storage_edit, $field_edit);
367
368     // Returns the generated field name.
369     return $field_name;
370   }
371
372   /**
373    * Checks if a select element contains the specified options.
374    *
375    * @param string $name
376    *   The field name.
377    * @param array $expected_options
378    *   An array of expected options.
379    */
380   protected function assertFieldSelectOptions($name, array $expected_options) {
381     $xpath = $this->buildXPathQuery('//select[@name=:name]', [':name' => $name]);
382     $fields = $this->xpath($xpath);
383     if ($fields) {
384       $field = $fields[0];
385       $options = $field->findAll('xpath', 'option');
386       array_walk($options, function (NodeElement &$option) {
387         $option = $option->getValue();
388       });
389       sort($options);
390       sort($expected_options);
391       $this->assertIdentical($options, $expected_options);
392     }
393     else {
394       $this->fail('Unable to find field ' . $name);
395     }
396   }
397
398 }