From 3cd6934a2e199393f4012a9db263b99a1f6a2f37 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Thu, 5 Jan 2017 12:53:18 +0000
Subject: [PATCH] Issue #2784537 by klausi, mpdonadio, dawehner: Add legacy
 assertFieldByXPath()/assertNoFieldByXPath() method for browser tests

---
 .../test_page_test/src/Form/TestForm.php      | 66 +++++++++++++
 .../test_page_test/test_page_test.routing.yml |  8 ++
 .../FunctionalTests/AssertLegacyTrait.php     | 97 +++++++++++++++++++
 .../FunctionalTests/BrowserTestBaseTest.php   | 22 ++++-
 4 files changed, 191 insertions(+), 2 deletions(-)
 create mode 100644 core/modules/system/tests/modules/test_page_test/src/Form/TestForm.php

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
new file mode 100644
index 000000000000..c619a7d47c48
--- /dev/null
+++ b/core/modules/system/tests/modules/test_page_test/src/Form/TestForm.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\test_page_test\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Defines a test form for testing assertions.
+ */
+class TestForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['test_table'] = [
+      '#type' => 'table',
+      '#header' => ['Column 1', 'Column 2', 'Column 3'],
+      'row_1' => [
+        'col_1' => ['#plain_text' => 'foo'],
+        'col_2' => ['#plain_text' => 'bar'],
+        'col_3' => ['#plain_text' => 'baz'],
+      ],
+      'row_2' => [
+        'col_1' => ['#plain_text' => 'one'],
+        'col_2' => ['#plain_text' => 'two'],
+        'col_3' => ['#plain_text' => 'three'],
+      ],
+    ];
+
+    $form['name'] = [
+      '#type' => 'textfield',
+      '#title' => 'Name',
+      '#default_value' => 'Test name',
+    ];
+
+    $form['options'] = [
+      '#type' => 'select',
+      '#title' => 'Options',
+      '#options' => [
+        1 => 'one',
+        2 => 'two',
+        3 => 'three',
+      ],
+      '#default_value' => 2,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'test_page_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    // Empty on purpose, we just want to test the rendered form elements.
+  }
+
+}
diff --git a/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml b/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml
index 9d07a3407641..5d84695f8b97 100644
--- a/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml
+++ b/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml
@@ -74,3 +74,11 @@ test_page_test.pipe:
     _controller: '\Drupal\test_page_test\Controller\Test::renderPipeInLink'
   requirements:
     _access: 'TRUE'
+
+test_page_test.field_xpath:
+  path: '/test-field-xpath'
+  defaults:
+    _title: 'Table and form elements for field xpath assertion testing'
+    _form: '\Drupal\test_page_test\Form\TestForm'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
index 2393f61ca2da..5e9caf4886e9 100644
--- a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
+++ b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\FunctionalTests;
 
+use Behat\Mink\Selector\Xpath\Escaper;
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Component\Utility\Xss;
 use Drupal\KernelTests\AssertLegacyTrait as BaseAssertLegacyTrait;
@@ -509,6 +510,102 @@ protected function assertNoFieldChecked($id) {
     $this->assertSession()->checkboxNotChecked($id);
   }
 
+  /**
+   * Asserts that a field exists in the current page by the given XPath.
+   *
+   * @param string $xpath
+   *   XPath used to find the field.
+   * @param string $value
+   *   (optional) Value of the field to assert. You may pass in NULL (default)
+   *   to skip checking the actual value, while still checking that the field
+   *   exists.
+   * @param string $message
+   *   (optional) A message to display with the assertion. Do not translate
+   *   messages with t().
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->xpath() instead and check the values directly in the test.
+   */
+  protected function assertFieldByXPath($xpath, $value = NULL, $message = '') {
+    $fields = $this->xpath($xpath);
+
+    $this->assertFieldsByValue($fields, $value, $message);
+  }
+
+  /**
+   * Asserts that a field does not exist or its value does not match, by XPath.
+   *
+   * @param string $xpath
+   *   XPath used to find the field.
+   * @param string $value
+   *   (optional) Value of the field, to assert that the field's value on the
+   *   page does not match it.
+   * @param string $message
+   *   (optional) A message to display with the assertion. Do not translate
+   *   messages with t().
+   *
+   * @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;
+          }
+        }
+      }
+    }
+    return $this->assertFalse($fields && $found, $message);
+  }
+
+  /**
+   * Asserts that a field exists in the current page with a given Xpath result.
+   *
+   * @param \Behat\Mink\Element\NodeElement[] $fields
+   *   Xml elements.
+   * @param string $value
+   *   (optional) Value of the field to assert. You may pass in NULL (default) to skip
+   *   checking the actual value, while still checking that the field exists.
+   * @param string $message
+   *   (optional) A message to display with the assertion. Do not translate
+   *   messages with t().
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Iterate over the fields yourself instead and directly check the values in
+   *   the test.
+   */
+  protected function assertFieldsByValue($fields, $value = NULL, $message = '') {
+    // 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) {
+            // Input element with correct value.
+            $found = TRUE;
+          }
+          elseif ($field->find('xpath', '//option[@value = ' . (new Escaper())->escapeLiteral($value) . ' and @selected = "selected"]')) {
+            // Select element with an option.
+            $found = TRUE;
+          }
+          elseif ($field->getText() == $value) {
+            // Text area with correct text.
+            $found = TRUE;
+          }
+        }
+      }
+    }
+    $this->assertTrue($fields && $found, $message);
+  }
+
   /**
    * Passes if the raw text IS found escaped on the loaded page, fail otherwise.
    *
diff --git a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
index eb315af066bd..ef9c6b59ef64 100644
--- a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
+++ b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
@@ -134,9 +134,9 @@ public function testPipeCharInLocator() {
   }
 
   /**
-   * Tests legacy asserts.
+   * Tests legacy text asserts.
    */
-  public function testLegacyAsserts() {
+  public function testLegacyTextAsserts() {
     $this->drupalGet('test-encoded');
     $dangerous = 'Bad html <script>alert(123);</script>';
     $sanitized = Html::escape($dangerous);
@@ -144,4 +144,22 @@ public function testLegacyAsserts() {
     $this->assertText($sanitized);
   }
 
+  /**
+   * Tests legacy XPath asserts.
+   */
+  public function testLegacyXPathAsserts() {
+    $this->drupalGet('test-field-xpath');
+    $this->assertFieldsByValue($this->xpath("//h1[@class = 'page-title']"), NULL);
+    $this->assertFieldsByValue($this->xpath('//table/tbody/tr[2]/td[1]'), 'one');
+    $this->assertFieldByXPath('//table/tbody/tr[2]/td[1]', 'one');
+
+    $this->assertFieldsByValue($this->xpath("//input[@id = 'edit-name']"), 'Test name');
+    $this->assertFieldByXPath("//input[@id = 'edit-name']", 'Test name');
+    $this->assertFieldsByValue($this->xpath("//select[@id = 'edit-options']"), '2');
+    $this->assertFieldByXPath("//select[@id = 'edit-options']", '2');
+
+    $this->assertNoFieldByXPath('//notexisting');
+    $this->assertNoFieldByXPath("//input[@id = 'edit-name']", 'wrong value');
+  }
+
 }
-- 
GitLab