Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / inline_entity_form / src / Tests / ComplexWidgetWebTest.php
1 <?php
2
3 namespace Drupal\inline_entity_form\Tests;
4
5 use Drupal\node\Entity\Node;
6
7 /**
8  * IEF complex field widget tests.
9  *
10  * @group inline_entity_form
11  */
12 class ComplexWidgetWebTest extends InlineEntityFormTestBase {
13
14   /**
15    * Modules to enable.
16    *
17    * @var array
18    */
19   public static $modules = [
20     'inline_entity_form_test',
21     'field',
22     'field_ui',
23   ];
24
25   /**
26    * URL to add new content.
27    *
28    * @var string
29    */
30   protected $formContentAddUrl;
31
32   /**
33    * Entity form display storage.
34    *
35    * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
36    */
37   protected $entityFormDisplayStorage;
38
39   /**
40    * Prepares environment for
41    */
42   protected function setUp() {
43     parent::setUp();
44
45     $this->user = $this->createUser([
46       'create ief_reference_type content',
47       'edit any ief_reference_type content',
48       'delete any ief_reference_type content',
49       'create ief_test_complex content',
50       'edit any ief_test_complex content',
51       'delete any ief_test_complex content',
52       'edit any ief_test_nested1 content',
53       'edit any ief_test_nested2 content',
54       'edit any ief_test_nested3 content',
55       'view own unpublished content',
56       'administer content types',
57     ]);
58     $this->drupalLogin($this->user);
59
60     $this->formContentAddUrl = 'node/add/ief_test_complex';
61     $this->entityFormDisplayStorage = $this->container->get('entity_type.manager')->getStorage('entity_form_display');
62   }
63
64   /**
65    * Tests if form behaves correctly when field is empty.
66    */
67   public function testEmptyFieldIEF() {
68     // Don't allow addition of existing nodes.
69     $this->updateSetting('allow_existing', FALSE);
70     $this->drupalGet($this->formContentAddUrl);
71
72     $this->assertFieldByName('multi[form][inline_entity_form][title][0][value]', NULL, 'Title field on inline form exists.');
73     $this->assertFieldByName('multi[form][inline_entity_form][first_name][0][value]', NULL, 'First name field on inline form exists.');
74     $this->assertFieldByName('multi[form][inline_entity_form][last_name][0][value]', NULL, 'Last name field on inline form exists.');
75     $this->assertFieldByXpath('//input[@type="submit" and @value="Create node"]', NULL, 'Found "Create node" submit button');
76
77     // Allow addition of existing nodes.
78     $this->updateSetting('allow_existing', TRUE);
79     $this->drupalGet($this->formContentAddUrl);
80
81     $this->assertNoFieldByName('multi[form][inline_entity_form][title][0][value]', NULL, 'Title field does not appear.');
82     $this->assertNoFieldByName('multi[form][inline_entity_form][first_name][0][value]', NULL, 'First name field does not appear.');
83     $this->assertNoFieldByName('multi[form][inline_entity_form][last_name][0][value]', NULL, 'Last name field does not appear.');
84     $this->assertFieldByXpath('//input[@type="submit" and @value="Add new node"]', NULL, 'Found "Add new node" submit button');
85     $this->assertFieldByXpath('//input[@type="submit" and @value="Add existing node"]', NULL, 'Found "Add existing node" submit button');
86
87     // Now submit 'Add new node' button.
88     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add new node" and @data-drupal-selector="edit-multi-actions-ief-add"]'));
89
90     $this->assertFieldByName('multi[form][inline_entity_form][title][0][value]', NULL, 'Title field on inline form exists.');
91     $this->assertFieldByName('multi[form][inline_entity_form][first_name][0][value]', NULL, 'First name field on inline form exists.');
92     $this->assertFieldByName('multi[form][inline_entity_form][last_name][0][value]', NULL, 'Second name field on inline form exists.');
93     $this->assertFieldByXpath('//input[@type="submit" and @value="Create node"]', NULL, 'Found "Create node" submit button');
94     $this->assertFieldByXpath('//input[@type="submit" and @value="Cancel"]', NULL, 'Found "Cancel" submit button');
95
96     // Now submit 'Add Existing node' button.
97     $this->drupalGet($this->formContentAddUrl);
98     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add existing node" and @data-drupal-selector="edit-multi-actions-ief-add-existing"]'));
99
100     $this->assertFieldByName('multi[form][entity_id]', NULL, 'Existing entity reference autocomplete field found.');
101     $this->assertFieldByXpath('//input[@type="submit" and @value="Add node"]', NULL, 'Found "Add node" submit button');
102     $this->assertFieldByXpath('//input[@type="submit" and @value="Cancel"]', NULL, 'Found "Cancel" submit button');
103   }
104
105   /**
106    * Tests creation of entities.
107    */
108   public function testEntityCreation() {
109     // Allow addition of existing nodes.
110     $this->updateSetting('allow_existing', TRUE);
111     $this->drupalGet($this->formContentAddUrl);
112
113     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add new node" and @data-drupal-selector="edit-multi-actions-ief-add"]'));
114     $this->assertResponse(200, 'Opening new inline form was successful.');
115
116     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Create node" and @data-drupal-selector="edit-multi-form-inline-entity-form-actions-ief-add-save"]'));
117     $this->assertResponse(200, 'Submitting empty form was successful.');
118     $this->assertText('First name field is required.', 'Validation failed for empty "First name" field.');
119     $this->assertText('Last name field is required.', 'Validation failed for empty "Last name" field.');
120     $this->assertText('Title field is required.', 'Validation failed for empty "Title" field.');
121
122     // Create ief_reference_type node in IEF.
123     $this->drupalGet($this->formContentAddUrl);
124     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add new node" and @data-drupal-selector="edit-multi-actions-ief-add"]'));
125     $this->assertResponse(200, 'Opening new inline form was successful.');
126
127     $edit = [
128       'multi[form][inline_entity_form][title][0][value]' => 'Some reference',
129       'multi[form][inline_entity_form][first_name][0][value]' => 'John',
130       'multi[form][inline_entity_form][last_name][0][value]' => 'Doe',
131     ];
132     $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @value="Create node" and @data-drupal-selector="edit-multi-form-inline-entity-form-actions-ief-add-save"]'));
133     $this->assertResponse(200, 'Creating node via inline form was successful.');
134
135     // Tests if correct fields appear in the table.
136     $this->assertTrue((bool) $this->xpath('//td[@class="inline-entity-form-node-label" and contains(.,"Some reference")]'), 'Node title field appears in the table');
137     $this->assertTrue((bool) $this->xpath('//td[@class="inline-entity-form-node-status" and ./div[contains(.,"Published")]]'), 'Node status field appears in the table');
138
139     // Tests if edit and remove buttons appear.
140     $this->assertTrue((bool) $this->xpath('//input[@type="submit" and @value="Edit"]'), 'Edit button appears in the table.');
141     $this->assertTrue((bool) $this->xpath('//input[@type="submit" and @value="Remove"]'), 'Remove button appears in the table.');
142
143     // Test edit functionality.
144     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Edit"]'));
145     $edit = [
146       'multi[form][inline_entity_form][entities][0][form][title][0][value]' => 'Some changed reference',
147     ];
148     $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @value="Update node"]'));
149     $this->assertTrue((bool) $this->xpath('//td[@class="inline-entity-form-node-label" and contains(.,"Some changed reference")]'), 'Node title field appears in the table');
150     $this->assertTrue((bool) $this->xpath('//td[@class="inline-entity-form-node-status" and ./div[contains(.,"Published")]]'), 'Node status field appears in the table');
151
152     // Make sure unrelated AJAX submit doesn't save the referenced entity.
153     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Upload"]'));
154     $node = $this->drupalGetNodeByTitle('Some changed reference');
155     $this->assertFalse($node, 'Referenced node was not saved during unrelated AJAX submit.');
156
157     // Create ief_test_complex node.
158     $edit = ['title[0][value]' => 'Some title'];
159     $this->drupalPostForm(NULL, $edit, t('Save'));
160     $this->assertResponse(200, 'Saving parent entity was successful.');
161
162     // Checks values of created entities.
163     $node = $this->drupalGetNodeByTitle('Some changed reference');
164     $this->assertTrue($node, 'Created ief_reference_type node ' . $node->label());
165     $this->assertTrue($node->get('first_name')->value == 'John', 'First name in reference node set to John');
166     $this->assertTrue($node->get('last_name')->value == 'Doe', 'Last name in reference node set to Doe');
167
168     $parent_node = $this->drupalGetNodeByTitle('Some title');
169     $this->assertTrue($parent_node, 'Created ief_test_complex node ' . $parent_node->label());
170     $this->assertTrue($parent_node->multi->target_id == $node->id(), 'Refererence node id set to ' . $node->id());
171   }
172
173   /**
174    * Tests the entity creation with different bundles nested in each other.
175    *
176    * ief_test_nested1 -> ief_test_nested2 -> ief_test_nested3
177    */
178   public function testNestedEntityCreationWithDifferentBundlesAjaxSubmit() {
179     $required_possibilities = [
180       FALSE,
181       TRUE,
182     ];
183     foreach ($required_possibilities as $required) {
184       $this->setupNestedComplexForm($required);
185
186       $nested3_title = 'nested3 title steps ' . ($required ? 'required' : 'not required');
187       $nested2_title = 'nested2 title steps ' . ($required ? 'required' : 'not required');
188       $nested1_title = 'nested1 title steps ' . ($required ? 'required' : 'not required');
189       $edit = [
190         'test_ref_nested1[form][inline_entity_form][test_ref_nested2][form][inline_entity_form][title][0][value]' => $nested3_title,
191       ];
192       $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @value="Create node 3"]'));
193       $this->assertText($nested3_title, 'Title of second nested node found.');
194       $this->assertNoNodeByTitle($nested3_title, 'Second nested entity is not saved yet.');
195
196       $edit = [
197         'test_ref_nested1[form][inline_entity_form][title][0][value]' => $nested2_title,
198       ];
199       $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @value="Create node 2"]'));
200       $this->assertText($nested2_title, 'Title of first nested node found.');
201       $this->assertNoNodeByTitle($nested2_title, 'First nested entity is not saved yet.');
202
203       $edit = [
204         'title[0][value]' => $nested1_title,
205       ];
206       $this->drupalPostForm(NULL, $edit, t('Save'));
207       $this->checkNestedNodes($nested1_title, $nested2_title, $nested3_title);
208     }
209   }
210
211   /**
212    * Checks that nested IEF entity references can be edit and saved.
213    *
214    * @param \Drupal\node\Entity\Node $node
215    *   Top level node of type ief_test_nested1 to check.
216    * @param bool $ajax_submit
217    *   Whether IEF form widgets should be submitted via AJAX or left open.
218    */
219   protected function checkNestedEntityEditing(Node $node, $ajax_submit = TRUE) {
220     $this->drupalGet("node/{$node->id()}/edit");
221     /** @var \Drupal\node\Entity\Node $level_1_node */
222     $level_1_node = $node->test_ref_nested1->entity;
223     /** @var \Drupal\node\Entity\Node $level_2_node */
224     $level_2_node = $node->test_ref_nested1->entity->test_ref_nested2->entity;
225     $level_2_node_update_title = $level_2_node->getTitle() . ' - updated';
226     // edit-test-ref-nested1-entities-0-actions-ief-entity-edit
227     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-test-ref-nested1-entities-0-actions-ief-entity-edit"]'));
228     // edit-test-ref-nested1-form-inline-entity-form-entities-0-form-test-ref-nested2-entities-0-actions-ief-entity-edit
229     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-test-ref-nested1-form-inline-entity-form-entities-0-form-test-ref-nested2-entities-0-actions-ief-entity-edit"]'));
230     $edit['test_ref_nested1[form][inline_entity_form][entities][0][form][test_ref_nested2][form][inline_entity_form][entities][0][form][title][0][value]'] = $level_2_node_update_title;
231     if ($ajax_submit) {
232       // Close IEF Forms with AJAX posts
233       // edit-test-ref-nested1-form-inline-entity-form-entities-0-form-test-ref-nested2-form-inline-entity-form-entities-0-form-actions-ief-edit-save
234       $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-test-ref-nested1-form-inline-entity-form-entities-0-form-test-ref-nested2-form-inline-entity-form-entities-0-form-actions-ief-edit-save"]'));
235       $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-test-ref-nested1-form-inline-entity-form-entities-0-form-actions-ief-edit-save"]'));
236       $this->drupalPostForm(NULL, [], t('Save'));
237     }
238     else {
239       $this->drupalPostForm(NULL, $edit, t('Save'));
240     }
241     $this->nodeStorage->resetCache([$level_2_node->id()]);
242     $level_2_node = $this->nodeStorage->load($level_2_node->id());
243     $this->assertEqual($level_2_node_update_title, $level_2_node->getTitle());
244   }
245
246   /**
247    * Tests the entity creation with different bundles nested in each other.
248    *
249    * ief_test_nested1 -> ief_test_nested2 -> ief_test_nested3
250    */
251   public function testNestedEntityCreationWithDifferentBundlesNoAjaxSubmit() {
252     $required_possibilities = [
253       FALSE,
254       TRUE,
255     ];
256
257     foreach ($required_possibilities as $required) {
258       $this->setupNestedComplexForm($required);
259
260       $nested3_title = 'nested3 title single ' . ($required ? 'required' : 'not required');
261       $nested2_title = 'nested2 title single ' . ($required ? 'required' : 'not required');
262       $nested1_title = 'nested1 title single ' . ($required ? 'required' : 'not required');
263
264       $edit = [
265         'title[0][value]' => $nested1_title,
266         'test_ref_nested1[form][inline_entity_form][title][0][value]' => $nested2_title,
267         'test_ref_nested1[form][inline_entity_form][test_ref_nested2][form][inline_entity_form][title][0][value]' => $nested3_title,
268       ];
269       $this->drupalPostForm(NULL, $edit, t('Save'));
270       $this->checkNestedNodes($nested1_title, $nested2_title, $nested3_title);
271     }
272   }
273
274   /**
275    * Tests if editing and removing entities work.
276    */
277   public function testEntityEditingAndRemoving() {
278     // Allow addition of existing nodes.
279     $this->updateSetting('allow_existing', TRUE);
280
281     // Create three ief_reference_type entities.
282     $referenceNodes = $this->createReferenceContent(3);
283     $this->drupalCreateNode([
284       'type' => 'ief_test_complex',
285       'title' => 'Some title',
286       'multi' => array_values($referenceNodes),
287     ]);
288     /** @var \Drupal\node\NodeInterface $node */
289     $parent_node = $this->drupalGetNodeByTitle('Some title');
290
291     // Edit the second entity.
292     $this->drupalGet('node/' . $parent_node->id() . '/edit');
293     $cell = $this->xpath('//table[@id="ief-entity-table-edit-multi-entities"]/tbody/tr[@class="ief-row-entity draggable even"]/td[@class="inline-entity-form-node-label"]');
294     $title = (string) $cell[0];
295
296     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @id="edit-multi-entities-1-actions-ief-entity-edit"]'));
297     $this->assertResponse(200, 'Opening inline edit form was successful.');
298
299     $edit = [
300       'multi[form][inline_entity_form][entities][1][form][first_name][0][value]' => 'John',
301       'multi[form][inline_entity_form][entities][1][form][last_name][0][value]' => 'Doe',
302     ];
303     $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-inline-entity-form-entities-1-form-actions-ief-edit-save"]'));
304     $this->assertResponse(200, 'Saving inline edit form was successful.');
305
306     // Save the ief_test_complex node.
307     $this->drupalPostForm(NULL, [], t('Save'));
308     $this->assertResponse(200, 'Saving parent entity was successful.');
309
310     // Checks values of changed entities.
311     $node = $this->drupalGetNodeByTitle($title, TRUE);
312     $this->assertTrue($node->first_name->value == 'John', 'First name in reference node changed to John');
313     $this->assertTrue($node->last_name->value == 'Doe', 'Last name in reference node changed to Doe');
314
315     // Delete the second entity.
316     $this->drupalGet('node/' . $parent_node->id() . '/edit');
317     $cell = $this->xpath('//table[@id="ief-entity-table-edit-multi-entities"]/tbody/tr[@class="ief-row-entity draggable even"]/td[@class="inline-entity-form-node-label"]');
318     $title = (string) $cell[0];
319
320     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @id="edit-multi-entities-1-actions-ief-entity-remove"]'));
321     $this->assertResponse(200, 'Opening inline remove confirm form was successful.');
322     $this->assertText('Are you sure you want to remove', 'Remove warning message is displayed.');
323
324     $this->drupalPostAjaxForm(NULL, ['multi[form][entities][1][form][delete]' => TRUE], $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-entities-1-form-actions-ief-remove-confirm"]'));
325     $this->assertResponse(200, 'Removing inline entity was successful.');
326     $this->assertNoText($title, 'Deleted inline entity is not present on the page.');
327
328     // Save the ief_test_complex node.
329     $this->drupalPostForm(NULL, [], t('Save'));
330     $this->assertResponse(200, 'Saving parent node was successful.');
331
332     $deleted_node = $this->drupalGetNodeByTitle($title);
333     $this->assertTrue(empty($deleted_node), 'The inline entity was deleted from the site.');
334
335     // Checks that entity does nor appear in IEF.
336     $this->drupalGet('node/' . $parent_node->id() . '/edit');
337     $this->assertNoText($title, 'Deleted inline entity is not present on the page after saving parent.');
338
339     // Delete the third entity reference only, don't delete the node. The third
340     // entity now is second referenced entity because the second one was deleted
341     // in previous step.
342     $this->drupalGet('node/' . $parent_node->id() . '/edit');
343     $cell = $this->xpath('//table[@id="ief-entity-table-edit-multi-entities"]/tbody/tr[@class="ief-row-entity draggable even"]/td[@class="inline-entity-form-node-label"]');
344     $title = (string) $cell[0];
345
346     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @id="edit-multi-entities-1-actions-ief-entity-remove"]'));
347     $this->assertResponse(200, 'Opening inline remove confirm form was successful.');
348
349     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-entities-1-form-actions-ief-remove-confirm"]'));
350     $this->assertResponse(200, 'Removing inline entity was successful.');
351
352     // Save the ief_test_complex node.
353     $this->drupalPostForm(NULL, [], t('Save'));
354     $this->assertResponse(200, 'Saving parent node was successful.');
355
356     // Checks that entity does nor appear in IEF.
357     $this->drupalGet('node/' . $parent_node->id() . '/edit');
358     $this->assertNoText($title, 'Deleted inline entity is not present on the page after saving parent.');
359
360     // Checks that entity is not deleted.
361     $node = $this->drupalGetNodeByTitle($title, TRUE);
362     $this->assertTrue($node, 'Reference node not deleted');
363   }
364
365   /**
366    * Tests if referencing existing entities work.
367    */
368   public function testReferencingExistingEntities() {
369     // Allow addition of existing nodes.
370     $this->updateSetting('allow_existing', TRUE);
371
372     // Create three ief_reference_type entities.
373     $referenceNodes = $this->createReferenceContent(3);
374
375     // Create a node for every bundle available.
376     $bundle_nodes = $this->createNodeForEveryBundle();
377
378     // Create ief_test_complex node with first ief_reference_type node and first
379     // node from bundle nodes.
380     $this->drupalCreateNode([
381       'type' => 'ief_test_complex',
382       'title' => 'Some title',
383       'multi' => [1],
384       'all_bundles' => key($bundle_nodes),
385     ]);
386     // Remove first node since we already added it.
387     unset($bundle_nodes[key($bundle_nodes)]);
388
389     $parent_node = $this->drupalGetNodeByTitle('Some title', TRUE);
390
391     // Add remaining existing reference nodes.
392     $this->drupalGet('node/' . $parent_node->id() . '/edit');
393     for ($i = 2; $i <= 3; $i++) {
394       $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add existing node" and @data-drupal-selector="edit-multi-actions-ief-add-existing"]'));
395       $this->assertResponse(200, 'Opening reference form was successful.');
396       $title = 'Some reference ' . $i;
397       $edit = [
398         'multi[form][entity_id]' => $title . ' (' . $referenceNodes[$title] . ')',
399       ];
400       $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-actions-ief-reference-save"]'));
401       $this->assertResponse(200, 'Adding new referenced entity was successful.');
402     }
403     // Add all remaining nodes from all bundles.
404     foreach ($bundle_nodes as $id => $title) {
405       $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add existing node" and @data-drupal-selector="edit-all-bundles-actions-ief-add-existing"]'));
406       $this->assertResponse(200, 'Opening reference form was successful.');
407       $edit = [
408         'all_bundles[form][entity_id]' => $title . ' (' . $id . ')',
409       ];
410       $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-all-bundles-form-actions-ief-reference-save"]'));
411       $this->assertResponse(200, 'Adding new referenced entity was successful.');
412     }
413     // Save the node.
414     $this->drupalPostForm(NULL, [], t('Save'));
415     $this->assertResponse(200, 'Saving parent for was successful.');
416
417     // Check if entities are referenced.
418     $this->drupalGet('node/' . $parent_node->id() . '/edit');
419     for ($i = 2; $i <= 3; $i++) {
420       $cell = $this->xpath('//table[@id="ief-entity-table-edit-multi-entities"]/tbody/tr[' . $i . ']/td[@class="inline-entity-form-node-label"]');
421       $this->assertTrue($cell[0] == 'Some reference ' . $i, 'Found reference node title "Some reference ' . $i . '" in the IEF table.');
422     }
423     // Check if all remaining nodes from all bundles are referenced.
424     $count = 2;
425     foreach ($bundle_nodes as $id => $title) {
426       $cell = $this->xpath('//table[@id="ief-entity-table-edit-all-bundles-entities"]/tbody/tr[' . $count . ']/td[@class="inline-entity-form-node-label"]');
427       $this->assertTrue($cell[0] == $title, 'Found reference node title "' . $title . '" in the IEF table.');
428       $count++;
429     }
430   }
431
432   /**
433    * Test if invalid values get correct validation messages in reference existing entity form.
434    *
435    * Also checks if existing entity reference form can be canceled.
436    */
437   public function testReferenceExistingValidation() {
438     $this->updateSetting('allow_existing', TRUE);
439
440     $this->drupalGet('node/add/ief_test_complex');
441     $this->checkExistingValidationExpectation('', 'Node field is required.');
442     $this->checkExistingValidationExpectation('Fake Title', "There are no entities matching \"Fake Title\"");
443     // Check adding nodes that cannot be referenced by this field.
444     $bundle_nodes = $this->createNodeForEveryBundle();
445     foreach ($bundle_nodes as $id => $title) {
446       $node = $this->nodeStorage->load($id);
447       if ($node->bundle() != 'ief_reference_type') {
448         $this->checkExistingValidationExpectation("$title ($id)", "The referenced entity (node: $id) does not exist.");
449       }
450     }
451
452     $nodes = $this->createReferenceContent(2);
453     foreach ($nodes as $title => $id) {
454       $this->openMultiExistingForm();
455       $edit = [
456         'multi[form][entity_id]' => "$title ($id)",
457       ];
458       // Add a node successfully.
459       $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-actions-ief-reference-save"]'));
460       $this->assertNoFieldByName('multi[form][entity_id]', NULL, 'Existing entity reference autocomplete field removed.');
461       // Try to add the same node again.
462       $this->checkExistingValidationExpectation("$title ($id)", 'The selected node has already been added.');
463     }
464   }
465
466   /**
467    * Tests if duplicating entities works.
468    */
469   public function testDuplicatingEntities() {
470     $this->updateSetting('allow_duplicate', TRUE);
471
472     $referenceNodes = $this->createReferenceContent(2);
473     $this->drupalCreateNode([
474       'type' => 'ief_test_complex',
475       'title' => 'Some title',
476       'multi' => array_values($referenceNodes),
477     ]);
478     /** @var \Drupal\node\NodeInterface $node */
479     $parent_node = $this->drupalGetNodeByTitle('Some title');
480
481     $this->drupalGet('node/' . $parent_node->id() . '/edit');
482     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @id="edit-multi-entities-0-actions-ief-entity-duplicate"]'));
483     $this->assertResponse(200, 'Opening inline duplicate form was successful.');
484
485     $edit = [
486       'multi[form][inline_entity_form][entities][0][form][title][0][value]' => 'Duplicate!',
487       'multi[form][inline_entity_form][entities][0][form][first_name][0][value]' => 'Bojan',
488     ];
489     $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-inline-entity-form-entities-0-form-actions-ief-duplicate-save"]'));
490     $this->assertResponse(200, 'Saving inline duplicate form was successful.');
491
492     $this->assertText('Some reference 1');
493     $this->assertText('Some reference 2');
494     $this->assertText('Duplicate!');
495     $this->drupalPostForm(NULL, [], t('Save'));
496     $this->assertResponse(200, 'Saving parent entity was successful.');
497
498     // Confirm a duplicate was made.
499     $duplicate = Node::load(4);
500     $this->assertEqual($duplicate->label(), 'Duplicate!');
501     $this->assertEqual($duplicate->first_name->value, 'Bojan');
502   }
503
504   /**
505    * Tests if a referenced content can be edited while the referenced content is
506    * newer than the referencing parent node.
507    */
508   public function testEditedInlineEntityValidation() {
509     $this->updateSetting('allow_existing', TRUE);
510
511     // Create referenced content.
512     $referenced_nodes = $this->createReferenceContent(1);
513
514     // Create first referencing node.
515     $this->drupalCreateNode([
516       'type' => 'ief_test_complex',
517       'title' => 'First referencing node',
518       'multi' => array_values($referenced_nodes),
519     ]);
520     $first_node = $this->drupalGetNodeByTitle('First referencing node');
521
522     // Create second referencing node.
523     $this->drupalCreateNode([
524       'type' => 'ief_test_complex',
525       'title' => 'Second referencing node',
526       'multi' => array_values($referenced_nodes),
527     ]);
528     $second_node = $this->drupalGetNodeByTitle('Second referencing node');
529
530     // Edit referenced content in first node.
531     $this->drupalGet('node/' . $first_node->id() . '/edit');
532
533     // Edit referenced node.
534     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Edit" and @data-drupal-selector="edit-multi-entities-0-actions-ief-entity-edit"]'));
535     $edit = [
536       'multi[form][inline_entity_form][entities][0][form][title][0][value]' => 'Some reference updated',
537     ];
538     $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @value="Update node" and @data-drupal-selector="edit-multi-form-inline-entity-form-entities-0-form-actions-ief-edit-save"]'));
539
540     // Save the first node after editing the reference.
541     $edit = ['title[0][value]' => 'First node updated'];
542     $this->drupalPostForm(NULL, $edit, t('Save'));
543
544     // The changed value of the referenced content is now newer than the
545     // changed value of the second node.
546
547     // Edit referenced content in second node.
548     $this->drupalGet('node/' . $second_node->id() . '/edit');
549
550     // Edit referenced node.
551     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Edit" and @data-drupal-selector="edit-multi-entities-0-actions-ief-entity-edit"]'));
552     $edit = [
553       'multi[form][inline_entity_form][entities][0][form][title][0][value]' => 'Some reference updated the second time',
554     ];
555     $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @value="Update node" and @data-drupal-selector="edit-multi-form-inline-entity-form-entities-0-form-actions-ief-edit-save"]'));
556
557     // Save the second node after editing the reference.
558     $edit = ['title[0][value]' => 'Second node updated'];
559     $this->drupalPostForm(NULL, $edit, t('Save'));
560
561     // Check if the referenced content could be edited.
562     $this->assertNoText('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.', 'The referenced content could be edited.');
563   }
564
565   /**
566    * Creates ief_reference_type nodes which shall serve as reference nodes.
567    *
568    * @param int $numNodes
569    *   The number of nodes to create
570    * @return array
571    *   Array of created node ids keyed by labels.
572    */
573   protected function createReferenceContent($numNodes = 3) {
574     $retval = [];
575     for ($i = 1; $i <= $numNodes; $i++) {
576       $this->drupalCreateNode([
577         'type' => 'ief_reference_type',
578         'title' => 'Some reference ' . $i,
579         'first_name' => 'First Name ' . $i,
580         'last_name' => 'Last Name ' . $i,
581       ]);
582       $node = $this->drupalGetNodeByTitle('Some reference ' . $i);
583       $this->assertTrue($node, 'Created ief_reference_type node "' . $node->label() . '"');
584       $retval[$node->label()] = $node->id();
585     }
586     return $retval;
587   }
588
589   /**
590    * Updates an IEF setting and saves the underlying entity display.
591    *
592    * @param string $name
593    *   The name of the setting.
594    * @param mixed $value
595    *   The value to set.
596    */
597   protected function updateSetting($name, $value) {
598     /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $display */
599     $display = $this->entityFormDisplayStorage->load('node.ief_test_complex.default');
600     $component = $display->getComponent('multi');
601     $component['settings'][$name] = $value;
602     $display->setComponent('multi', $component)->save();
603   }
604
605   /**
606    * Creates a node for every node bundle.
607    *
608    * @return array
609    *   Array of node titles keyed by ids.
610    */
611   protected function createNodeForEveryBundle() {
612     $retval = [];
613     $bundles = $this->container->get('entity.manager')->getBundleInfo('node');
614     foreach ($bundles as $id => $value) {
615       $this->drupalCreateNode(['type' => $id, 'title' => $value['label']]);
616       $node = $this->drupalGetNodeByTitle($value['label']);
617       $this->assertTrue($node, 'Created node "' . $node->label() . '"');
618       $retval[$node->id()] = $value['label'];
619     }
620     return $retval;
621   }
622
623   /**
624    * Set up the ief_test_nested1 node add form.
625    *
626    * Sets the nested fields' required settings.
627    * Gets the form.
628    * Opens the inline entity forms if they are not required.
629    *
630    * @param bool $required
631    *   Whether the fields are required.
632    * @param array $permissions
633    *   (optional) Permissions to sign testing user in with. You may pass in an
634    *   empty array (default) to use the all the permissions necessary create and
635    *   edit nodes on the form.
636    */
637   protected function setupNestedComplexForm($required, $permissions = []) {
638     /** @var \Drupal\Core\Field\FieldConfigInterface $ief_test_nested1 */
639     $ief_test_nested1 = $this->fieldConfigStorage->load('node.ief_test_nested1.test_ref_nested1');
640     $ief_test_nested1->setRequired($required);
641     $ief_test_nested1->save();
642     /** @var \Drupal\Core\Field\FieldConfigInterface $ief_test_nested2 */
643     $ief_test_nested2 = $this->fieldConfigStorage->load('node.ief_test_nested2.test_ref_nested2');
644     $ief_test_nested2->setRequired($required);
645     $ief_test_nested2->save();
646
647     if (!$permissions) {
648       $permissions = [
649         'create ief_test_nested1 content',
650         'create ief_test_nested2 content',
651         'create ief_test_nested3 content',
652         'edit any ief_test_nested1 content',
653         'edit any ief_test_nested2 content',
654         'edit any ief_test_nested3 content',
655       ];
656     }
657     $this->user = $this->createUser($permissions);
658     $this->drupalLogin($this->user);
659
660     $this->drupalGet('node/add/ief_test_nested1');
661
662     if (!$required) {
663       // Open inline forms if not required.
664       if (in_array('create ief_test_nested2 content', $permissions)) {
665         $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add new node 2"]'));
666       }
667       if (in_array('create ief_test_nested3 content', $permissions)) {
668         $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add new node 3"]'));
669       }
670     }
671   }
672
673   /**
674    * Closes the existing node form on the "multi" field.
675    */
676   protected function cancelExistingMultiForm($edit) {
677     $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-actions-ief-reference-cancel"]'));
678     $this->assertNoFieldByName('multi[form][entity_id]', NULL, 'Existing entity reference autocomplete field removed.');
679   }
680
681   /**
682    * Opens the existing node form on the "multi" field.
683    */
684   protected function openMultiExistingForm() {
685     $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add existing node" and @data-drupal-selector="edit-multi-actions-ief-add-existing"]'));
686     $this->assertResponse(200, 'Opening reference form was successful.');
687     $this->assertFieldByName('multi[form][entity_id]', NULL, 'Existing entity reference autocomplete field found.');
688   }
689
690   /**
691    * Checks that an invalid value for an existing node will be display the expected error.
692    *
693    * @param $existing_node_text
694    *   The text to enter into the existing node text field.
695    * @param $expected_error
696    *   The error message that is expected to be shown.
697    */
698   protected function checkExistingValidationExpectation($existing_node_text, $expected_error) {
699     $edit = [
700       'multi[form][entity_id]' => $existing_node_text,
701     ];
702     $this->openMultiExistingForm();
703
704     $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-actions-ief-reference-save"]'));
705     $this->assertText($expected_error);
706     $this->cancelExistingMultiForm($edit);
707   }
708
709   /**
710    * Tests entity create access is correct on nested IEF forms.
711    */
712   public function testNestedEntityCreateAccess() {
713     $permissions = [
714       'create ief_test_nested1 content',
715       'create ief_test_nested2 content',
716     ];
717     $this->setupNestedComplexForm(TRUE, $permissions);
718     $this->assertFieldByName('title[0][value]');
719     $this->assertFieldByName('test_ref_nested1[form][inline_entity_form][title][0][value]');
720     $this->assertNoFieldByName('test_ref_nested1[form][inline_entity_form][test_ref_nested2][form][inline_entity_form][title][0][value]', NULL);
721
722     $this->setupNestedComplexForm(FALSE, $permissions);
723     $this->assertNoFieldByXPath('//input[@type="submit" and @value="Add new node 3"]');
724   }
725
726   /**
727    * Tests create access on IEF Complex content type.
728    */
729   public function testComplexEntityCreate() {
730     $user = $this->createUser([
731       'create ief_test_complex content',
732     ]);
733     $this->drupalLogin($user);
734
735     $this->drupalGet('node/add/ief_test_complex');
736     $this->assertNoFieldByName('all_bundles[actions][bundle]', NULL, 'Bundle select is not shown when only one bundle is available.');
737     $this->assertNoFieldByName('multi[form][inline_entity_form][title][0][value]', NULL);
738
739     $user = $this->createUser([
740       'create ief_test_complex content',
741       'create ief_reference_type content'
742     ]);
743     $this->drupalLogin($user);
744
745     $this->drupalGet('node/add/ief_test_complex');
746     $this->assertFieldByName('all_bundles[actions][bundle]', NULL, 'Bundle select is shown when more than one bundle is available.');
747     $this->assertOption('edit-all-bundles-actions-bundle', 'ief_reference_type');
748     $this->assertOption('edit-all-bundles-actions-bundle', 'ief_test_complex');
749     $this->assertFieldByName('multi[form][inline_entity_form][title][0][value]');
750   }
751
752   /**
753    * Checks if nested nodes for ief_test_nested1 content were created correctly.
754    *
755    * @param $nested1_title
756    *   Expected title of top level node of the type ief_test_nested1
757    * @param $nested2_title
758    *   Expected title of second level node
759    * @param $nested3_title
760    *   Expected title of third level node
761    */
762   protected function checkNestedNodes($nested1_title, $nested2_title, $nested3_title) {
763     $nested1_node = $this->drupalGetNodeByTitle($nested1_title);
764     $this->assertEqual($nested1_title, $nested1_node->label(), "First node's title looks correct.");
765     $this->assertEqual('ief_test_nested1', $nested1_node->bundle(), "First node's type looks correct.");
766     if ($this->assertNotNull($nested1_node->test_ref_nested1->entity, 'Second node was created.')) {
767       $this->assertEqual($nested1_node->test_ref_nested1->count(), 1, 'Only 1 node created at first level.');
768       $this->assertEqual($nested2_title, $nested1_node->test_ref_nested1->entity->label(), "Second node's title looks correct.");
769       $this->assertEqual('ief_test_nested2', $nested1_node->test_ref_nested1->entity->bundle(), "Second node's type looks correct.");
770       if ($this->assertNotNull($nested1_node->test_ref_nested1->entity->test_ref_nested2->entity, 'Third node was created')) {
771         $this->assertEqual($nested1_node->test_ref_nested1->entity->test_ref_nested2->count(), 1, 'Only 1 node created at second level.');
772         $this->assertEqual($nested3_title, $nested1_node->test_ref_nested1->entity->test_ref_nested2->entity->label(), "Third node's title looks correct.");
773         $this->assertEqual('ief_test_nested3', $nested1_node->test_ref_nested1->entity->test_ref_nested2->entity->bundle(), "Third node's type looks correct.");
774
775         $this->checkNestedEntityEditing($nested1_node, TRUE);
776       }
777     }
778   }
779
780 }