MultiFormTest.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <?php
  2. namespace Drupal\FunctionalJavascriptTests\Ajax;
  3. use Drupal\Component\Render\FormattableMarkup;
  4. use Drupal\Core\Field\FieldStorageDefinitionInterface;
  5. use Drupal\field\Entity\FieldConfig;
  6. use Drupal\field\Entity\FieldStorageConfig;
  7. use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
  8. /**
  9. * Tests that AJAX-enabled forms work when multiple instances of the same form
  10. * are on a page.
  11. *
  12. * @group Ajax
  13. */
  14. class MultiFormTest extends WebDriverTestBase {
  15. /**
  16. * {@inheritdoc}
  17. */
  18. public static $modules = ['node', 'form_test'];
  19. /**
  20. * {@inheritdoc}
  21. */
  22. protected $defaultTheme = 'stark';
  23. /**
  24. * {@inheritdoc}
  25. */
  26. protected function setUp() {
  27. parent::setUp();
  28. $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
  29. // Create a multi-valued field for 'page' nodes to use for Ajax testing.
  30. $field_name = 'field_ajax_test';
  31. FieldStorageConfig::create([
  32. 'entity_type' => 'node',
  33. 'field_name' => $field_name,
  34. 'type' => 'text',
  35. 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
  36. ])->save();
  37. FieldConfig::create([
  38. 'field_name' => $field_name,
  39. 'entity_type' => 'node',
  40. 'bundle' => 'page',
  41. ])->save();
  42. \Drupal::service('entity_display.repository')->getFormDisplay('node', 'page', 'default')
  43. ->setComponent($field_name, ['type' => 'text_textfield'])
  44. ->save();
  45. // Log in a user who can create 'page' nodes.
  46. $this->drupalLogin($this->drupalCreateUser(['create page content']));
  47. }
  48. /**
  49. * Tests that pages with the 'node_page_form' included twice work correctly.
  50. */
  51. public function testMultiForm() {
  52. // HTML IDs for elements within the field are potentially modified with
  53. // each Ajax submission, but these variables are stable and help target the
  54. // desired elements.
  55. $field_name = 'field_ajax_test';
  56. $form_xpath = '//form[starts-with(@id, "node-page-form")]';
  57. $field_xpath = '//div[contains(@class, "field--name-field-ajax-test")]';
  58. $button_name = $field_name . '_add_more';
  59. $button_value = t('Add another item');
  60. $button_xpath_suffix = '//input[@name="' . $button_name . '"]';
  61. $field_items_xpath_suffix = '//input[@type="text"]';
  62. // Ensure the initial page contains both node forms and the correct number
  63. // of field items and "add more" button for the multi-valued field within
  64. // each form.
  65. $this->drupalGet('form-test/two-instances-of-same-form');
  66. // Wait for javascript on the page to prepare the form attributes.
  67. $this->assertSession()->assertWaitOnAjaxRequest();
  68. $session = $this->getSession();
  69. $page = $session->getPage();
  70. $fields = $page->findAll('xpath', $form_xpath . $field_xpath);
  71. $this->assertCount(2, $fields);
  72. foreach ($fields as $field) {
  73. $this->assertCount(1, $field->findAll('xpath', '.' . $field_items_xpath_suffix), 'Found the correct number of field items on the initial page.');
  74. $this->assertFieldsByValue($field->find('xpath', '.' . $button_xpath_suffix), NULL, 'Found the "add more" button on the initial page.');
  75. }
  76. $this->assertNoDuplicateIds();
  77. // Submit the "add more" button of each form twice. After each corresponding
  78. // page update, ensure the same as above.
  79. for ($i = 0; $i < 2; $i++) {
  80. $forms = $page->find('xpath', $form_xpath);
  81. foreach ($forms as $offset => $form) {
  82. $button = $form->findButton($button_value);
  83. $this->assertNotNull($button, 'Add Another Item button exists');
  84. $button->press();
  85. // Wait for page update.
  86. $this->assertSession()->assertWaitOnAjaxRequest();
  87. // After AJAX request and response page will update.
  88. $page_updated = $session->getPage();
  89. $field = $page_updated->findAll('xpath', '.' . $field_xpath);
  90. $this->assertCount($i + 2, $field[0]->find('xpath', '.' . $field_items_xpath_suffix), 'Found the correct number of field items after an AJAX submission.');
  91. $this->assertFieldsByValue($field[0]->find('xpath', '.' . $button_xpath_suffix), NULL, 'Found the "add more" button after an AJAX submission.');
  92. $this->assertNoDuplicateIds();
  93. }
  94. }
  95. }
  96. /**
  97. * Asserts that each HTML ID is used for just a single element on the page.
  98. *
  99. * @param string $message
  100. * (optional) A message to display with the assertion.
  101. */
  102. protected function assertNoDuplicateIds($message = '') {
  103. $args = ['@url' => $this->getUrl()];
  104. if (!$elements = $this->xpath('//*[@id]')) {
  105. $this->fail(new FormattableMarkup('The page @url contains no HTML IDs.', $args));
  106. return;
  107. }
  108. $message = $message ?: new FormattableMarkup('The page @url does not contain duplicate HTML IDs', $args);
  109. $seen_ids = [];
  110. foreach ($elements as $element) {
  111. $id = $element->getAttribute('id');
  112. if (isset($seen_ids[$id])) {
  113. $this->fail($message);
  114. return;
  115. }
  116. $seen_ids[$id] = TRUE;
  117. }
  118. $this->assertTrue(TRUE, $message);
  119. }
  120. }