diff --git a/core/modules/system/tests/modules/test_page_test/src/Form/TestForm.php b/core/modules/system/tests/modules/test_page_test/src/Form/TestForm.php index 24a9bdb58b..59dcb35391 100644 --- a/core/modules/system/tests/modules/test_page_test/src/Form/TestForm.php +++ b/core/modules/system/tests/modules/test_page_test/src/Form/TestForm.php @@ -64,6 +64,24 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => 2, ]; + $form['duplicate_button'] = [ + '#type' => 'submit', + '#name' => 'duplicate_button', + '#value' => 'Duplicate button 1', + ]; + + $form['duplicate_button_2'] = [ + '#type' => 'submit', + '#name' => 'duplicate_button', + '#value' => 'Duplicate button 2', + ]; + + $form['test_textarea_with_newline'] =[ + '#type' => 'textarea', + '#title' => 'Textarea with newline', + '#default_value' => "Test text with\nnewline", + ]; + $form['save'] = [ '#type' => 'submit', '#value' => $this->t('Save'), diff --git a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php index 08a2d8ea19..f768b71ad5 100644 --- a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php +++ b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php @@ -2,7 +2,6 @@ namespace Drupal\FunctionalTests; -use Behat\Mink\Exception\ElementNotFoundException; use Behat\Mink\Exception\ExpectationException; use Behat\Mink\Selector\Xpath\Escaper; use Drupal\Component\Render\FormattableMarkup; @@ -220,13 +219,11 @@ protected function assertResponse($code) { * * @deprecated Scheduled for removal in Drupal 9.0.0. * Use $this->assertSession()->fieldExists() or + * $this->assertSession()->buttonExists() or * $this->assertSession()->fieldValueEquals() instead. */ protected function assertFieldByName($name, $value = NULL) { - $this->assertSession()->fieldExists($name); - if ($value !== NULL) { - $this->assertSession()->fieldValueEquals($name, (string) $value); - } + $this->assertFieldByXPath($this->constructFieldXpath('name', $name), $value); } /** @@ -242,15 +239,11 @@ protected function assertFieldByName($name, $value = NULL) { * * @deprecated Scheduled for removal in Drupal 9.0.0. * Use $this->assertSession()->fieldNotExists() or + * $this->assertSession()->buttonNotExists() or * $this->assertSession()->fieldValueNotEquals() instead. */ protected function assertNoFieldByName($name, $value = '') { - if ($this->getSession()->getPage()->findField($name) && isset($value)) { - $this->assertSession()->fieldValueNotEquals($name, (string) $value); - } - else { - $this->assertSession()->fieldNotExists($name); - } + $this->assertNoFieldByXPath($this->constructFieldXpath('name', $name), $value); } /** @@ -268,19 +261,11 @@ protected function assertNoFieldByName($name, $value = '') { * * @deprecated Scheduled for removal in Drupal 9.0.0. * Use $this->assertSession()->fieldExists() or + * $this->assertSession()->buttonExists() or * $this->assertSession()->fieldValueEquals() instead. */ protected function assertFieldById($id, $value = '') { - $xpath = $this->assertSession()->buildXPathQuery('//textarea[@id=:value]|//input[@id=:value]|//select[@id=:value]', [':value' => $id]); - $field = $this->getSession()->getPage()->find('xpath', $xpath); - - if (empty($field)) { - throw new ElementNotFoundException($this->getSession()->getDriver(), 'form field', 'id', $field); - } - - if ($value !== NULL) { - $this->assertEquals($value, $field->getValue()); - } + $this->assertFieldByXPath($this->constructFieldXpath('id', $id), $value); } /** @@ -290,23 +275,25 @@ protected function assertFieldById($id, $value = '') { * Name or ID of field to assert. * * @deprecated Scheduled for removal in Drupal 9.0.0. - * Use $this->assertSession()->fieldExists() instead. + * Use $this->assertSession()->fieldExists() or + * $this->assertSession()->buttonExists() instead. */ protected function assertField($field) { - $this->assertSession()->fieldExists($field); + $this->assertFieldByXPath($this->constructFieldXpath('name', $field) . '|' . $this->constructFieldXpath('id', $field)); } /** - * Asserts that a field exists with the given name or ID does NOT exist. + * Asserts that a field does NOT exist with the given name or ID. * * @param string $field * Name or ID of field to assert. * * @deprecated Scheduled for removal in Drupal 9.0.0. - * Use $this->assertSession()->fieldNotExists() instead. + * Use $this->assertSession()->fieldNotExists() or + * $this->assertSession()->buttonNotExists() instead. */ protected function assertNoField($field) { - $this->assertSession()->fieldNotExists($field); + $this->assertNoFieldByXPath($this->constructFieldXpath('name', $field) . '|' . $this->constructFieldXpath('id', $field)); } /** @@ -427,23 +414,11 @@ protected function assertNoLinkByHref($href) { * * @deprecated Scheduled for removal in Drupal 9.0.0. * Use $this->assertSession()->fieldNotExists() or + * $this->assertSession()->buttonNotExists() or * $this->assertSession()->fieldValueNotEquals() instead. */ protected function assertNoFieldById($id, $value = '') { - $xpath = $this->assertSession()->buildXPathQuery('//textarea[@id=:value]|//input[@id=:value]|//select[@id=:value]', [':value' => $id]); - $field = $this->getSession()->getPage()->find('xpath', $xpath); - - // Return early if the field could not be found as expected. - if ($field === NULL) { - return; - } - - if (!isset($value)) { - throw new ExpectationException(sprintf('Id "%s" appears on this page, but it should not.', $id), $this->getSession()->getDriver()); - } - elseif ($value === $field->getValue()) { - throw new ExpectationException(sprintf('Failed asserting that %s is not equal to %s', $field->getValue(), $value), $this->getSession()->getDriver()); - } + $this->assertNoFieldByXPath($this->constructFieldXpath('id', $id), $value); } /** @@ -585,25 +560,32 @@ protected function assertFieldByXPath($xpath, $value = NULL, $message = '') { * (optional) A message to display with the assertion. Do not translate * messages with t(). * + * @throws \Behat\Mink\Exception\ExpectationException + * * @deprecated Scheduled for removal in Drupal 9.0.0. * Use $this->xpath() instead and assert that the result is empty. */ protected function assertNoFieldByXPath($xpath, $value = NULL, $message = '') { $fields = $this->xpath($xpath); - // If value specified then check array for match. - $found = TRUE; - if (isset($value)) { - $found = FALSE; - if ($fields) { - foreach ($fields as $field) { - if ($field->getAttribute('value') == $value) { - $found = TRUE; - } + if (!empty($fields)) { + if (isset($value)) { + $found = FALSE; + try { + $this->assertFieldsByValue($fields, $value); + $found = TRUE; + } + catch (\Exception $e) { + } + + if ($found) { + throw new ExpectationException(sprintf('The field resulting from %s was found with the provided value %s.', $xpath, $value), $this->getSession()->getDriver()); } } + else { + throw new ExpectationException(sprintf('The field resulting from %s was found.', $xpath), $this->getSession()->getDriver()); + } } - return $this->assertFalse($fields && $found, $message); } /** @@ -629,7 +611,15 @@ protected function assertFieldsByValue($fields, $value = NULL, $message = '') { $found = FALSE; if ($fields) { foreach ($fields as $field) { - if ($field->getAttribute('value') == $value) { + if ($field->getAttribute('type') == 'checkbox') { + if (is_bool($value)) { + $found = $field->isChecked() == $value; + } + else { + $found = TRUE; + } + } + elseif ($field->getAttribute('value') == $value) { // Input element with correct value. $found = TRUE; } @@ -637,7 +627,7 @@ protected function assertFieldsByValue($fields, $value = NULL, $message = '') { // Select element with an option. $found = TRUE; } - elseif ($field->getText() == $value) { + elseif ($field->getTagName() !== 'input' && $field->getText() == $value) { // Text area with correct text. $found = TRUE; } @@ -789,6 +779,25 @@ protected function buildXPathQuery($xpath, array $args = []) { } /** + * Helper: Constructs an XPath for the given set of attributes and value. + * + * @param string $attribute + * Field attributes. + * @param string $value + * Value of field. + * + * @return string + * XPath for specified values. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->getSession()->getPage()->findField() instead. + */ + protected function constructFieldXpath($attribute, $value) { + $xpath = '//textarea[@' . $attribute . '=:value]|//input[@' . $attribute . '=:value]|//select[@' . $attribute . '=:value]'; + return $this->buildXPathQuery($xpath, [':value' => $value]); + } + + /** * Gets the current raw content. * * @deprecated Scheduled for removal in Drupal 9.0.0. diff --git a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php index 2701dcb0bf..4a0e1956bf 100644 --- a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php +++ b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php @@ -223,7 +223,7 @@ public function testLegacyXpathAsserts() { $this->assertNoFieldByXPath("//input[@id = 'edit-name']"); $this->fail('The "edit-name" field was not found.'); } - catch (\PHPUnit_Framework_ExpectationFailedException $e) { + catch (ExpectationException $e) { $this->pass('assertNoFieldByXPath correctly failed. The "edit-name" field was found.'); } @@ -239,7 +239,7 @@ public function testLegacyXpathAsserts() { /** * Tests legacy field asserts using textfields. */ - public function testLegacyFieldAssertsWithTextfields() { + public function testLegacyFieldAssertsForTextfields() { $this->drupalGet('test-field-xpath'); // *** 1. assertNoField(). @@ -272,7 +272,7 @@ public function testLegacyFieldAssertsWithTextfields() { $this->assertField('invalid_name_and_id'); $this->fail('The "invalid_name_and_id" field was found.'); } - catch (ExpectationException $e) { + catch (\PHPUnit_Framework_ExpectationFailedException $e) { $this->pass('assertField correctly failed. The "invalid_name_and_id" field was not found.'); } @@ -361,7 +361,7 @@ public function testLegacyFieldAssertsWithTextfields() { $this->assertFieldByName('non-existing-name'); $this->fail('The "non-existing-name" field was found.'); } - catch (ExpectationException $e) { + catch (\PHPUnit_Framework_ExpectationFailedException $e) { $this->pass('The "non-existing-name" field was not found'); } @@ -370,15 +370,18 @@ public function testLegacyFieldAssertsWithTextfields() { $this->assertFieldByName('name', 'not the value'); $this->fail('The "name" field with incorrect value was found.'); } - catch (ExpectationException $e) { + catch (\PHPUnit_Framework_ExpectationFailedException $e) { $this->pass('assertFieldByName correctly failed. The "name" field with incorrect value was not found.'); } + + // Test that text areas can contain new lines. + $this->assertFieldsByValue($this->xpath("//textarea[@id = 'edit-test-textarea-with-newline']"), "Test text with\nnewline"); } /** - * Tests legacy field asserts on other types of field. + * Tests legacy field asserts for options field type. */ - public function testLegacyFieldAssertsWithNonTextfields() { + public function testLegacyFieldAssertsForOptions() { $this->drupalGet('test-field-xpath'); // Option field type. @@ -425,8 +428,14 @@ public function testLegacyFieldAssertsWithNonTextfields() { catch (\PHPUnit_Framework_ExpectationFailedException $e) { $this->pass($e->getMessage()); } + } + + /** + * Tests legacy field asserts for button field type. + */ + public function testLegacyFieldAssertsForButton() { + $this->drupalGet('test-field-xpath'); - // Button field type. $this->assertFieldById('edit-save', NULL); // Test that the assertion fails correctly if the field value is passed in // rather than the id. @@ -434,7 +443,7 @@ public function testLegacyFieldAssertsWithNonTextfields() { $this->assertFieldById('Save', NULL); $this->fail('The field with id of "Save" was found.'); } - catch (ExpectationException $e) { + catch (\PHPUnit_Framework_ExpectationFailedException $e) { $this->pass($e->getMessage()); } @@ -449,7 +458,27 @@ public function testLegacyFieldAssertsWithNonTextfields() { $this->pass($e->getMessage()); } - // Checkbox field type. + // Test that multiple fields with the same name are validated correctly. + $this->assertFieldByName('duplicate_button', 'Duplicate button 1'); + $this->assertFieldByName('duplicate_button', 'Duplicate button 2'); + $this->assertNoFieldByName('duplicate_button', 'Rabbit'); + + try { + $this->assertNoFieldByName('duplicate_button', 'Duplicate button 2'); + $this->fail('The "duplicate_button" field with the value Duplicate button 2 was not found.'); + } + catch (ExpectationException $e) { + $this->pass('assertNoFieldByName correctly failed. The "duplicate_button" field with the value Duplicate button 2 was found.'); + } + } + + /** + * Tests legacy field asserts for checkbox field type. + */ + public function testLegacyFieldAssertsForCheckbox() { + $this->drupalGet('test-field-xpath'); + + // Part 1 - Test by name. // Test that checkboxes are found/not found correctly by name, when using // TRUE or FALSE to match their 'checked' state. $this->assertFieldByName('checkbox_enabled', TRUE); @@ -462,6 +491,24 @@ public function testLegacyFieldAssertsWithNonTextfields() { $this->assertFieldByName('checkbox_enabled', NULL); $this->assertFieldByName('checkbox_disabled', NULL); + // Test that checkboxes are found by name when passing no second parameter. + $this->assertFieldByName('checkbox_enabled'); + $this->assertFieldByName('checkbox_disabled'); + + // Test that we have legacy support. + $this->assertFieldByName('checkbox_enabled', '1'); + $this->assertFieldByName('checkbox_disabled', ''); + + // Test that the assertion fails correctly when using NULL to ignore state. + try { + $this->assertNoFieldByName('checkbox_enabled', NULL); + $this->fail('The "checkbox_enabled" field was not found by name, using NULL value.'); + } + catch (ExpectationException $e) { + $this->pass('assertNoFieldByName failed correctly. The "checkbox_enabled" field was found using NULL value.'); + } + + // Part 2 - Test by ID. // Test that checkboxes are found/not found correctly by ID, when using // TRUE or FALSE to match their 'checked' state. $this->assertFieldById('edit-checkbox-enabled', TRUE); @@ -469,19 +516,18 @@ public function testLegacyFieldAssertsWithNonTextfields() { $this->assertNoFieldById('edit-checkbox-enabled', FALSE); $this->assertNoFieldById('edit-checkbox-disabled', TRUE); - // Test that checkboxes are found by by ID, when using NULL to ignore the + // Test that checkboxes are found by ID, when using NULL to ignore the // 'checked' state. $this->assertFieldById('edit-checkbox-enabled', NULL); $this->assertFieldById('edit-checkbox-disabled', NULL); - // Test that the assertion fails correctly when using NULL to ignore state. - try { - $this->assertNoFieldByName('checkbox_enabled', NULL); - $this->fail('The "checkbox_enabled" field was not found by name, using NULL value.'); - } - catch (ExpectationException $e) { - $this->pass('assertNoFieldByName failed correctly. The "checkbox_enabled" field was found using NULL value.'); - } + // Test that checkboxes are found by ID when passing no second parameter. + $this->assertFieldById('edit-checkbox-enabled'); + $this->assertFieldById('edit-checkbox-disabled'); + + // Test that we have legacy support. + $this->assertFieldById('edit-checkbox-enabled', '1'); + $this->assertFieldById('edit-checkbox-disabled', ''); // Test that the assertion fails correctly when using NULL to ignore state. try { @@ -492,7 +538,7 @@ public function testLegacyFieldAssertsWithNonTextfields() { $this->pass('assertNoFieldById failed correctly. The "edit-checkbox-disabled" field was found by ID using NULL value.'); } - // Test the specific 'checked' assertions. + // Part 3 - Test the specific 'checked' assertions. $this->assertFieldChecked('edit-checkbox-enabled'); $this->assertNoFieldChecked('edit-checkbox-disabled');