3 namespace Drupal\inline_entity_form\Tests;
5 use Drupal\node\Entity\Node;
8 * IEF complex field widget tests.
10 * @group inline_entity_form
12 class ComplexWidgetWebTest extends InlineEntityFormTestBase {
19 public static $modules = [
20 'inline_entity_form_test',
26 * URL to add new content.
30 protected $formContentAddUrl;
33 * Entity form display storage.
35 * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
37 protected $entityFormDisplayStorage;
40 * Prepares environment for
42 protected function setUp() {
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',
58 $this->drupalLogin($this->user);
60 $this->formContentAddUrl = 'node/add/ief_test_complex';
61 $this->entityFormDisplayStorage = $this->container->get('entity_type.manager')->getStorage('entity_form_display');
65 * Tests if form behaves correctly when field is empty.
67 public function testEmptyFieldIEF() {
68 // Don't allow addition of existing nodes.
69 $this->setAllowExisting(FALSE);
70 $this->drupalGet($this->formContentAddUrl);
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');
77 // Allow addition of existing nodes.
78 $this->setAllowExisting(TRUE);
79 $this->drupalGet($this->formContentAddUrl);
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');
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"]'));
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');
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"]'));
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');
106 * Tests creation of entities.
108 public function testEntityCreation() {
109 // Allow addition of existing nodes.
110 $this->setAllowExisting(TRUE);
111 $this->drupalGet($this->formContentAddUrl);
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.');
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.');
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.');
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',
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.');
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');
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.');
143 // Test edit functionality.
144 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Edit"]'));
146 'multi[form][inline_entity_form][entities][0][form][title][0][value]' => 'Some changed reference',
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');
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.');
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.');
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');
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());
174 * Tests the entity creation with different bundles nested in each other.
176 * ief_test_nested1 -> ief_test_nested2 -> ief_test_nested3
178 public function testNestedEntityCreationWithDifferentBundlesAjaxSubmit() {
179 $required_possibilities = [
183 foreach ($required_possibilities as $required) {
184 $this->setupNestedComplexForm($required);
187 $nested3_title = 'nested3 title steps ' . ($required ? 'required' : 'not required');
188 $nested2_title = 'nested2 title steps ' . ($required ? 'required' : 'not required');
189 $nested1_title = 'nested1 title steps ' . ($required ? 'required' : 'not required');
191 'test_ref_nested1[form][inline_entity_form][test_ref_nested2][form][inline_entity_form][title][0][value]' => $nested3_title,
193 $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @value="Create node 3"]'));
194 $this->assertText($nested3_title, 'Title of second nested node found.');
195 $this->assertNoNodeByTitle($nested3_title, 'Second nested entity is not saved yet.');
198 'test_ref_nested1[form][inline_entity_form][title][0][value]' => $nested2_title,
200 $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @value="Create node 2"]'));
201 $this->assertText($nested2_title, 'Title of first nested node found.');
202 $this->assertNoNodeByTitle($nested2_title, 'First nested entity is not saved yet.');
205 'title[0][value]' => $nested1_title,
207 $this->drupalPostForm(NULL, $edit, t('Save'));
208 $this->checkNestedNodes($nested1_title, $nested2_title, $nested3_title);
213 * Checks that nested IEF entity references can be edit and saved.
215 * @param \Drupal\node\Entity\Node $node
216 * Top level node of type ief_test_nested1 to check.
217 * @param bool $ajax_submit
218 * Whether IEF form widgets should be submitted via AJax or left open.
221 protected function checkNestedEntityEditing(Node $node, $ajax_submit = TRUE) {
222 $this->drupalGet("node/{$node->id()}/edit");
223 /** @var \Drupal\node\Entity\Node $level_1_node */
224 $level_1_node = $node->test_ref_nested1->entity;
225 /** @var \Drupal\node\Entity\Node $level_2_node */
226 $level_2_node = $node->test_ref_nested1->entity->test_ref_nested2->entity;
227 $level_2_node_update_title = $level_2_node->getTitle() . ' - updated';
228 //edit-test-ref-nested1-entities-0-actions-ief-entity-edit
229 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-test-ref-nested1-entities-0-actions-ief-entity-edit"]'));
230 //edit-test-ref-nested1-form-inline-entity-form-entities-0-form-test-ref-nested2-entities-0-actions-ief-entity-edit
231 $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"]'));
232 $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;
234 // Close IEF Forms with AJAX posts
235 //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
236 $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"]'));
237 $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"]'));
238 $this->drupalPostForm(NULL, [], t('Save'));
241 $this->drupalPostForm(NULL, $edit, t('Save'));
243 $this->nodeStorage->resetCache([$level_2_node->id()]);
244 $level_2_node = $this->nodeStorage->load($level_2_node->id());
245 $this->assertEqual($level_2_node_update_title, $level_2_node->getTitle());
249 * Tests the entity creation with different bundles nested in each other.
251 * ief_test_nested1 -> ief_test_nested2 -> ief_test_nested3
253 public function testNestedEntityCreationWithDifferentBundlesNoAjaxSubmit() {
254 $required_possibilities = [
259 foreach ($required_possibilities as $required) {
260 $this->setupNestedComplexForm($required);
262 $nested3_title = 'nested3 title single ' . ($required ? 'required' : 'not required');
263 $nested2_title = 'nested2 title single ' . ($required ? 'required' : 'not required');
264 $nested1_title = 'nested1 title single ' . ($required ? 'required' : 'not required');
267 'title[0][value]' => $nested1_title,
268 'test_ref_nested1[form][inline_entity_form][title][0][value]' => $nested2_title,
269 'test_ref_nested1[form][inline_entity_form][test_ref_nested2][form][inline_entity_form][title][0][value]' => $nested3_title,
271 $this->drupalPostForm(NULL, $edit, t('Save'));
272 $this->checkNestedNodes($nested1_title, $nested2_title, $nested3_title);
277 * Tests if editing and removing entities work.
279 public function testEntityEditingAndRemoving() {
280 // Allow addition of existing nodes.
281 $this->setAllowExisting(TRUE);
283 // Create three ief_reference_type entities.
284 $referenceNodes = $this->createReferenceContent(3);
285 $this->drupalCreateNode([
286 'type' => 'ief_test_complex',
287 'title' => 'Some title',
288 'multi' => array_values($referenceNodes),
290 /** @var \Drupal\node\NodeInterface $node */
291 $parent_node = $this->drupalGetNodeByTitle('Some title');
293 // Edit the second entity.
294 $this->drupalGet('node/'. $parent_node->id() .'/edit');
295 $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"]');
296 $title = (string) $cell[0];
298 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @id="edit-multi-entities-1-actions-ief-entity-edit"]'));
299 $this->assertResponse(200, 'Opening inline edit form was successful.');
302 'multi[form][inline_entity_form][entities][1][form][first_name][0][value]' => 'John',
303 'multi[form][inline_entity_form][entities][1][form][last_name][0][value]' => 'Doe',
305 $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"]'));
306 $this->assertResponse(200, 'Saving inline edit form was successful.');
308 // Save the ief_test_complex node.
309 $this->drupalPostForm(NULL, [], t('Save'));
310 $this->assertResponse(200, 'Saving parent entity was successful.');
312 // Checks values of changed entities.
313 $node = $this->drupalGetNodeByTitle($title, TRUE);
314 $this->assertTrue($node->first_name->value == 'John', 'First name in reference node changed to John');
315 $this->assertTrue($node->last_name->value == 'Doe', 'Last name in reference node changed to Doe');
317 // Delete the second entity.
318 $this->drupalGet('node/'. $parent_node->id() .'/edit');
319 $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"]');
320 $title = (string) $cell[0];
322 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @id="edit-multi-entities-1-actions-ief-entity-remove"]'));
323 $this->assertResponse(200, 'Opening inline remove confirm form was successful.');
324 $this->assertText('Are you sure you want to remove', 'Remove warning message is displayed.');
326 $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"]'));
327 $this->assertResponse(200, 'Removing inline entity was successful.');
328 $this->assertNoText($title, 'Deleted inline entity is not present on the page.');
330 // Save the ief_test_complex node.
331 $this->drupalPostForm(NULL, [], t('Save'));
332 $this->assertResponse(200, 'Saving parent node was successful.');
334 $deleted_node = $this->drupalGetNodeByTitle($title);
335 $this->assertTrue(empty($deleted_node), 'The inline entity was deleted from the site.');
337 // Checks that entity does nor appear in IEF.
338 $this->drupalGet('node/'. $parent_node->id() .'/edit');
339 $this->assertNoText($title, 'Deleted inline entity is not present on the page after saving parent.');
341 // Delete the third entity reference only, don't delete the node. The third
342 // entity now is second referenced entity because the second one was deleted
344 $this->drupalGet('node/'. $parent_node->id() .'/edit');
345 $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"]');
346 $title = (string) $cell[0];
348 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @id="edit-multi-entities-1-actions-ief-entity-remove"]'));
349 $this->assertResponse(200, 'Opening inline remove confirm form was successful.');
351 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-entities-1-form-actions-ief-remove-confirm"]'));
352 $this->assertResponse(200, 'Removing inline entity was successful.');
354 // Save the ief_test_complex node.
355 $this->drupalPostForm(NULL, [], t('Save'));
356 $this->assertResponse(200, 'Saving parent node was successful.');
358 // Checks that entity does nor appear in IEF.
359 $this->drupalGet('node/'. $parent_node->id() . '/edit');
360 $this->assertNoText($title, 'Deleted inline entity is not present on the page after saving parent.');
362 // Checks that entity is not deleted.
363 $node = $this->drupalGetNodeByTitle($title, TRUE);
364 $this->assertTrue($node, 'Reference node not deleted');
368 * Tests if referencing existing entities work.
370 public function testReferencingExistingEntities() {
371 // Allow addition of existing nodes.
372 $this->setAllowExisting(TRUE);
374 // Create three ief_reference_type entities.
375 $referenceNodes = $this->createReferenceContent(3);
377 // Create a node for every bundle available.
378 $bundle_nodes = $this->createNodeForEveryBundle();
380 // Create ief_test_complex node with first ief_reference_type node and first
381 // node from bundle nodes.
382 $this->drupalCreateNode([
383 'type' => 'ief_test_complex',
384 'title' => 'Some title',
386 'all_bundles' => key($bundle_nodes),
388 // Remove first node since we already added it.
389 unset($bundle_nodes[key($bundle_nodes)]);
391 $parent_node = $this->drupalGetNodeByTitle('Some title', TRUE);
393 // Add remaining existing reference nodes.
394 $this->drupalGet('node/' . $parent_node->id() . '/edit');
395 for ($i = 2; $i <= 3; $i++) {
396 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add existing node" and @data-drupal-selector="edit-multi-actions-ief-add-existing"]'));
397 $this->assertResponse(200, 'Opening reference form was successful.');
398 $title = 'Some reference ' . $i;
400 'multi[form][entity_id]' => $title . ' (' . $referenceNodes[$title] . ')',
402 $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-actions-ief-reference-save"]'));
403 $this->assertResponse(200, 'Adding new referenced entity was successful.');
405 // Add all remaining nodes from all bundles.
406 foreach ($bundle_nodes as $id => $title) {
407 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add existing node" and @data-drupal-selector="edit-all-bundles-actions-ief-add-existing"]'));
408 $this->assertResponse(200, 'Opening reference form was successful.');
410 'all_bundles[form][entity_id]' => $title . ' (' . $id . ')',
412 $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-all-bundles-form-actions-ief-reference-save"]'));
413 $this->assertResponse(200, 'Adding new referenced entity was successful.');
416 $this->drupalPostForm(NULL, [], t('Save'));
417 $this->assertResponse(200, 'Saving parent for was successful.');
419 // Check if entities are referenced.
420 $this->drupalGet('node/'. $parent_node->id() .'/edit');
421 for ($i = 2; $i <= 3; $i++) {
422 $cell = $this->xpath('//table[@id="ief-entity-table-edit-multi-entities"]/tbody/tr[' . $i . ']/td[@class="inline-entity-form-node-label"]');
423 $this->assertTrue($cell[0] == 'Some reference ' . $i, 'Found reference node title "Some reference ' . $i .'" in the IEF table.');
425 // Check if all remaining nodes from all bundles are referenced.
427 foreach ($bundle_nodes as $id => $title) {
428 $cell = $this->xpath('//table[@id="ief-entity-table-edit-all-bundles-entities"]/tbody/tr[' . $count . ']/td[@class="inline-entity-form-node-label"]');
429 $this->assertTrue($cell[0] == $title, 'Found reference node title "' . $title . '" in the IEF table.');
435 * Test if invalid values get correct validation messages in reference existing entity form.
437 * Also checks if existing entity reference form can be canceled.
439 public function testReferenceExistingValidation() {
440 $this->setAllowExisting(TRUE);
442 $this->drupalGet('node/add/ief_test_complex');
443 $this->checkExistingValidationExpectation('', 'Node field is required.');
444 $this->checkExistingValidationExpectation('Fake Title', "There are no entities matching \"Fake Title\"");
445 // Check adding nodes that cannot be referenced by this field.
446 $bundle_nodes = $this->createNodeForEveryBundle();
447 foreach ($bundle_nodes as $id => $title) {
448 $node = $this->nodeStorage->load($id);
449 if ($node->bundle() != 'ief_reference_type') {
450 $this->checkExistingValidationExpectation("$title ($id)", "The referenced entity (node: $id) does not exist.");
454 $nodes = $this->createReferenceContent(2);
455 foreach ($nodes as $title => $id) {
456 $this->openMultiExistingForm();
458 'multi[form][entity_id]' => "$title ($id)",
460 // Add a node successfully.
461 $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-actions-ief-reference-save"]'));
462 $this->assertNoFieldByName('multi[form][entity_id]', NULL, 'Existing entity reference autocomplete field removed.');
463 // Try to add the same node again.
464 $this->checkExistingValidationExpectation("$title ($id)", 'The selected node has already been added.');
469 * Tests if a referenced content can be edited while the referenced content is
470 * newer than the referencing parent node.
472 public function testEditedInlineEntityValidation() {
473 $this->setAllowExisting(TRUE);
475 // Create referenced content.
476 $referenced_nodes = $this->createReferenceContent(1);
478 // Create first referencing node.
479 $this->drupalCreateNode([
480 'type' => 'ief_test_complex',
481 'title' => 'First referencing node',
482 'multi' => array_values($referenced_nodes),
484 $first_node = $this->drupalGetNodeByTitle('First referencing node');
486 // Create second referencing node.
487 $this->drupalCreateNode([
488 'type' => 'ief_test_complex',
489 'title' => 'Second referencing node',
490 'multi' => array_values($referenced_nodes),
492 $second_node = $this->drupalGetNodeByTitle('Second referencing node');
494 // Edit referenced content in first node.
495 $this->drupalGet('node/' . $first_node->id() . '/edit');
497 // Edit referenced node.
498 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Edit" and @data-drupal-selector="edit-multi-entities-0-actions-ief-entity-edit"]'));
500 'multi[form][inline_entity_form][entities][0][form][title][0][value]' => 'Some reference updated',
502 $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"]'));
504 // Save the first node after editing the reference.
505 $edit = ['title[0][value]' => 'First node updated'];
506 $this->drupalPostForm(NULL, $edit, t('Save'));
508 // The changed value of the referenced content is now newer than the
509 // changed value of the second node.
511 // Edit referenced content in second node.
512 $this->drupalGet('node/' . $second_node->id() . '/edit');
514 // Edit referenced node.
515 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Edit" and @data-drupal-selector="edit-multi-entities-0-actions-ief-entity-edit"]'));
517 'multi[form][inline_entity_form][entities][0][form][title][0][value]' => 'Some reference updated the second time',
519 $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"]'));
521 // Save the second node after editing the reference.
522 $edit = ['title[0][value]' => 'Second node updated'];
523 $this->drupalPostForm(NULL, $edit, t('Save'));
525 // Check if the referenced content could be edited.
526 $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.');
530 * Creates ief_reference_type nodes which shall serve as reference nodes.
532 * @param int $numNodes
533 * The number of nodes to create
535 * Array of created node ids keyed by labels.
537 protected function createReferenceContent($numNodes = 3) {
539 for ($i = 1; $i <= $numNodes; $i++) {
540 $this->drupalCreateNode([
541 'type' => 'ief_reference_type',
542 'title' => 'Some reference ' . $i,
543 'first_name' => 'First Name ' . $i,
544 'last_name' => 'Last Name ' . $i,
546 $node = $this->drupalGetNodeByTitle('Some reference ' . $i);
547 $this->assertTrue($node, 'Created ief_reference_type node "' . $node->label() . '"');
548 $retval[$node->label()] = $node->id();
554 * Sets allow_existing IEF setting.
557 * "allow_existing" flag to be set.
559 protected function setAllowExisting($flag) {
560 /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $display */
561 $display = $this->entityFormDisplayStorage->load('node.ief_test_complex.default');
562 $component = $display->getComponent('multi');
563 $component['settings']['allow_existing'] = $flag;
564 $display->setComponent('multi', $component)->save();
568 * Creates a node for every node bundle.
571 * Array of node titles keyed by ids.
573 protected function createNodeForEveryBundle() {
575 $bundles = $this->container->get('entity.manager')->getBundleInfo('node');
576 foreach ($bundles as $id => $value) {
577 $this->drupalCreateNode(['type' => $id, 'title' => $value['label']]);
578 $node = $this->drupalGetNodeByTitle($value['label']);
579 $this->assertTrue($node, 'Created node "' . $node->label() . '"');
580 $retval[$node->id()] = $value['label'];
586 * Set up the ief_test_nested1 node add form.
588 * Sets the nested fields' required settings.
590 * Opens the inline entity forms if they are not required.
592 * @param boolean $required
593 * Whether the fields are required.
594 * @param array $permissions
595 * (optional) Permissions to sign testing user in with. You may pass in an
596 * empty array (default) to use the all the permissions necessary create and
597 * edit nodes on the form.
599 protected function setupNestedComplexForm($required, $permissions = []) {
600 /** @var \Drupal\Core\Field\FieldConfigInterface $ief_test_nested1 */
601 $ief_test_nested1 = $this->fieldConfigStorage->load('node.ief_test_nested1.test_ref_nested1');
602 $ief_test_nested1->setRequired($required);
603 $ief_test_nested1->save();
604 /** @var \Drupal\Core\Field\FieldConfigInterface $ief_test_nested2 */
605 $ief_test_nested2 = $this->fieldConfigStorage->load('node.ief_test_nested2.test_ref_nested2');
606 $ief_test_nested2->setRequired($required);
607 $ief_test_nested2->save();
611 'create ief_test_nested1 content',
612 'create ief_test_nested2 content',
613 'create ief_test_nested3 content',
614 'edit any ief_test_nested1 content',
615 'edit any ief_test_nested2 content',
616 'edit any ief_test_nested3 content',
619 $this->user = $this->createUser($permissions);
620 $this->drupalLogin($this->user);
622 $this->drupalGet('node/add/ief_test_nested1');
625 // Open inline forms if not required.
626 if (in_array('create ief_test_nested2 content', $permissions)) {
627 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add new node 2"]'));
629 if (in_array('create ief_test_nested3 content', $permissions)) {
630 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add new node 3"]'));
636 * Closes the existing node form on the "multi" field.
638 protected function cancelExistingMultiForm($edit) {
639 $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-actions-ief-reference-cancel"]'));
640 $this->assertNoFieldByName('multi[form][entity_id]', NULL, 'Existing entity reference autocomplete field removed.');
644 * Opens the existing node form on the "multi" field.
646 protected function openMultiExistingForm() {
647 $this->drupalPostAjaxForm(NULL, [], $this->getButtonName('//input[@type="submit" and @value="Add existing node" and @data-drupal-selector="edit-multi-actions-ief-add-existing"]'));
648 $this->assertResponse(200, 'Opening reference form was successful.');
649 $this->assertFieldByName('multi[form][entity_id]', NULL, 'Existing entity reference autocomplete field found.');
653 * Checks that an invalid value for an existing node will be display the expected error.
655 * @param $existing_node_text
656 * The text to enter into the existing node text field.
657 * @param $expected_error
658 * The error message that is expected to be shown.
660 protected function checkExistingValidationExpectation($existing_node_text, $expected_error) {
662 'multi[form][entity_id]' => $existing_node_text,
664 $this->openMultiExistingForm();
666 $this->drupalPostAjaxForm(NULL, $edit, $this->getButtonName('//input[@type="submit" and @data-drupal-selector="edit-multi-form-actions-ief-reference-save"]'));
667 $this->assertText($expected_error);
668 $this->cancelExistingMultiForm($edit);
672 * Tests entity create access is correct on nested IEF forms.
674 public function testNestedEntityCreateAccess() {
676 'create ief_test_nested1 content',
677 'create ief_test_nested2 content',
679 $this->setupNestedComplexForm(TRUE, $permissions);
680 $this->assertFieldByName('title[0][value]');
681 $this->assertFieldByName('test_ref_nested1[form][inline_entity_form][title][0][value]');
682 $this->assertNoFieldByName('test_ref_nested1[form][inline_entity_form][test_ref_nested2][form][inline_entity_form][title][0][value]', NULL);
684 $this->setupNestedComplexForm(FALSE, $permissions);
685 $this->assertNoFieldByXPath('//input[@type="submit" and @value="Add new node 3"]');
689 * Tests create access on IEF Complex content type.
691 public function testComplexEntityCreate() {
692 $user = $this->createUser([
693 'create ief_test_complex content',
695 $this->drupalLogin($user);
697 $this->drupalGet('node/add/ief_test_complex');
698 $this->assertNoFieldByName('all_bundles[actions][bundle]', NULL, 'Bundle select is not shown when only one bundle is available.');
699 $this->assertNoFieldByName('multi[form][inline_entity_form][title][0][value]', NULL);
701 $user = $this->createUser([
702 'create ief_test_complex content',
703 'create ief_reference_type content'
705 $this->drupalLogin($user);
707 $this->drupalGet('node/add/ief_test_complex');
708 $this->assertFieldByName('all_bundles[actions][bundle]', NULL, 'Bundle select is shown when more than one bundle is available.');
709 $this->assertOption('edit-all-bundles-actions-bundle', 'ief_reference_type');
710 $this->assertOption('edit-all-bundles-actions-bundle', 'ief_test_complex');
711 $this->assertFieldByName('multi[form][inline_entity_form][title][0][value]');
715 * Checks if nested nodes for ief_test_nested1 content were created correctly.
717 * @param $nested1_title
718 * Expected title of top level node of the type ief_test_nested1
719 * @param $nested2_title
720 * Expected title of second level node
721 * @param $nested3_title
722 * Expected title of third level node
724 protected function checkNestedNodes($nested1_title, $nested2_title, $nested3_title) {
725 $nested1_node = $this->drupalGetNodeByTitle($nested1_title);
726 $this->assertEqual($nested1_title, $nested1_node->label(), "First node's title looks correct.");
727 $this->assertEqual('ief_test_nested1', $nested1_node->bundle(), "First node's type looks correct.");
728 if ($this->assertNotNull($nested1_node->test_ref_nested1->entity, 'Second node was created.')) {
729 $this->assertEqual($nested1_node->test_ref_nested1->count(), 1, 'Only 1 node created at first level.');
730 $this->assertEqual($nested2_title, $nested1_node->test_ref_nested1->entity->label(), "Second node's title looks correct.");
731 $this->assertEqual('ief_test_nested2', $nested1_node->test_ref_nested1->entity->bundle(), "Second node's type looks correct.");
732 if ($this->assertNotNull($nested1_node->test_ref_nested1->entity->test_ref_nested2->entity, 'Third node was created')) {
733 $this->assertEqual($nested1_node->test_ref_nested1->entity->test_ref_nested2->count(), 1, 'Only 1 node created at second level.');
734 $this->assertEqual($nested3_title, $nested1_node->test_ref_nested1->entity->test_ref_nested2->entity->label(), "Third node's title looks correct.");
735 $this->assertEqual('ief_test_nested3', $nested1_node->test_ref_nested1->entity->test_ref_nested2->entity->bundle(), "Third node's type looks correct.");
737 $this->checkNestedEntityEditing($nested1_node, TRUE);