Problem/Motivation

In #3167880: [meta] Convert assertions involving use of xpath to WebAssert, where possible we identified that use of $this->xpath() can be dangerous as we aren't always testing what we expect to test. In WebAssert we have more appropriate methods to find and assert HTML elements, so we should use them where possible.

This issue is scoped to find XPath selectors for <select> and <option> and convert those where possible.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

longwave created an issue. See original summary.

longwave’s picture

Status: Active » Needs review
FileSize
51.57 KB

Only two I couldn't figure out how to convert in FilterAdminTest:

    $elements = $this->xpath('//select[@name=:first]/following::select[@name=:second]', [
      ':first' => 'filters[' . $first_filter . '][weight]',
      ':second' => 'filters[' . $second_filter . '][weight]',
    ]);
    $this->assertNotEmpty($elements, 'Order confirmed in admin interface.');

Unsure if or how we can find the order that two elements appear in without using XPath like this.

longwave’s picture

+++ b/core/modules/language/tests/src/Functional/LanguageLocaleListTest.php
@@ -66,11 +66,10 @@ public function testLanguageLocaleList() {
-    $option_elements = $this->xpath('//select[@id="edit-predefined-langcode/option"]');
...
+    $options = $this->assertSession()->selectExists('edit-predefined-langcode')->findAll('css', 'option');

A subtle bug fix here - the XPath selector has a typo, it was looking for a select element with ID edit-predefined-langcode/option - which doesn't exist, so $option_elements was empty, and the rest of the code went on to successfully compare two empty arrays.

longwave’s picture

Assigned: longwave » Unassigned
longwave’s picture

FileSize
58.04 KB
10.84 KB

Some improvements, some more cases that I missed.

mondrake’s picture

Status: Needs review » Needs work

Great work! Readibility so much improved. I have put down the nits below walking through - more food for thought than anything, please do not take them as strict.

In general:

  • I wonder if we should check uniqueness of some of the selects, because it seems to me we were doing it before in some cases, and no longer
  • In some places taking away the $message should be balanced by some inline context comment

Walkthrough:

  1. +++ b/core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php
    @@ -130,9 +130,8 @@ public function testLoading() {
    -    $this->assertCount(1, $format_selector, 'A single text format selector exists on the page.');
    -    $specific_format_selector = $this->xpath('//select[contains(@class, "filter-list") and @data-editor-for="edit-body-0-value"]');
    -    $this->assertCount(1, $specific_format_selector, 'A single text format selector exists on the page and has a "data-editor-for" attribute with the correct value.');
    +    $select = $this->assertSession()->elementExists('css', 'select.js-filter-list');
    +    $this->assertSame('edit-body-0-value', $select->getAttribute('data-editor-for'));
    

    We're missing the uniqueness check here. Some of the $message info could be inlined as comment.

  2. +++ b/core/modules/editor/tests/src/Functional/EditorAdminTest.php
    @@ -248,13 +244,11 @@ protected function verifyUnicornEditorConfiguration($format_id, $ponies_too = TR
    -    $this->assertTrue($options[1]->hasAttribute('selected'), 'Option 2 ("Unicorn Editor") is selected.');
    ...
    +    $this->assertTrue($options[1]->isSelected());
    

    Message->comment

  3. +++ b/core/modules/editor/tests/src/Functional/EditorLoadingTest.php
    @@ -147,11 +147,11 @@ public function testLoading() {
    -    $this->assertCount(0, $format_selector, 'No text format selector exists on the page because the user only has access to a single format.');
    +    $this->assertSession()->elementNotExists('css', 'select.js-filter-list');
    

    Message->comment

  4. +++ b/core/modules/editor/tests/src/Functional/EditorLoadingTest.php
    @@ -175,9 +175,8 @@ public function testLoading() {
    -    $this->assertCount(1, $format_selector, 'A single text format selector exists on the page.');
    -    $specific_format_selector = $this->xpath('//select[contains(@class, "filter-list") and @data-editor-for="edit-body-0-value"]');
    -    $this->assertCount(1, $specific_format_selector, 'A single text format selector exists on the page and has a "data-editor-for" attribute with the correct value.');
    +    $select = $this->assertSession()->elementExists('css', 'select.js-filter-list');
    +    $this->assertSame('edit-body-0-value', $select->getAttribute('data-editor-for'));
    

    Message->comment, uniqueness?

  5. +++ b/core/modules/editor/tests/src/Functional/EditorLoadingTest.php
    @@ -270,13 +269,13 @@ public function testSupportedElementTypes() {
    -    $this->assertCount(1, $format_selector, 'A single text format selector exists on the page.');
    -    $specific_format_selector = $this->xpath('//select[contains(@class, "filter-list") and contains(@class, "editor") and @data-editor-for="edit-field-text-0-value"]');
    -    $this->assertCount(1, $specific_format_selector, 'A single text format selector exists on the page and has the "editor" class and a "data-editor-for" attribute with the correct value.');
    +    $select = $this->assertSession()->elementExists('css', 'select.js-filter-list');
    +    $this->assertStringContainsString('editor', $select->getAttribute('class'));
    +    $this->assertSame('edit-field-text-0-value', $select->getAttribute('data-editor-for'));
    

    Message->comment, uniqueness?

  6. +++ b/core/modules/editor/tests/src/Functional/EditorLoadingTest.php
    @@ -286,13 +285,13 @@ public function testSupportedElementTypes() {
    -    $this->assertCount(1, $format_selector, 'A single text format selector exists on the page.');
    -    $specific_format_selector = $this->xpath('//select[contains(@class, "filter-list") and contains(@class, "editor") and @data-editor-for="edit-field-text-0-value"]');
    -    $this->assertCount(0, $specific_format_selector, 'No text format selector exists on the page with the "editor" class and a "data-editor-for" attribute with the expected value.');
    +    $select = $this->assertSession()->elementExists('css', 'select.js-filter-list');
    +    $this->assertStringNotContainsString('editor', $select->getAttribute('class'));
    +    $this->assertNotSame('edit-field-text-0-value', $select->getAttribute('data-editor-for'));
    

    again

  7. +++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
    @@ -390,21 +390,11 @@ protected function createEntityReferenceField($target_type, $bundles = []) {
    -    $xpath = $this->assertSession()->buildXPathQuery('//select[@name=:name]', [':name' => $name]);
    -    $fields = $this->xpath($xpath);
    -    if ($fields) {
    -      $field = $fields[0];
    -      $options = $field->findAll('xpath', 'option');
    -      array_walk($options, function (NodeElement &$option) {
    -        $option = $option->getValue();
    -      });
    -      sort($options);
    -      sort($expected_options);
    -      $this->assertIdentical($options, $expected_options);
    -    }
    -    else {
    -      $this->fail('Unable to find field ' . $name);
    -    }
    +    $options = $this->assertSession()->selectExists($name)->findAll('xpath', 'option');
    +    array_walk($options, function (NodeElement &$option) {
    +      $option = $option->getValue();
    +    });
    +    $this->assertEqualsCanonicalizing($expected_options, $options);
       }
    

    very nice

longwave’s picture

Do you think it is worth adding a helper to extract option values/text from a select? We have the same loop repeated in a number of places.

Also, a helper to assert that an element exists only once might be useful too?

mondrake’s picture

#7.1 - yeah but not so many of them, and something like this

    $options = $this->assertSession()->selectExists('edit-settings-node-article-settings-language-langcode')->findAll('css', 'option');
    $options = array_map(function ($item) {
      return $item->getValue();
    }, $options);
    $this->assertSame($expected_elements, $options);

seems already quite readable to me. But as you prefer.

#7.2 - well we could use $this->assertSession()->elementsCount() with $count = 1, do we really need a separate method?

longwave’s picture

Status: Needs work » Needs review
FileSize
58.13 KB
4.36 KB

Rerolled and addressed review points.

For #6.3 and #6.4 I think the comment above the block of assertions already explains what we are doing so the message isn't needed. Everywhere else I added back the messages either as assertion message or comment.

I didn't change #7.1 and I also forgot about elementsCount() so we don't need anything new, added that here where needed.

mondrake’s picture

Status: Needs review » Needs work

Code style not passing.

Spokje’s picture

Status: Needs work » Needs review
FileSize
1.13 KB
58.62 KB

Created an issue for failing the Custom Commands script not setting the issue status back to Needs Work automagically.
(#3189649: Failing Custom Commands script doesn't change issue status from Needs Review to Needs Work)

Attached patch addresses Code Style failures in #9

Status: Needs review » Needs work

The last submitted patch, 11: 3187309-11.patch, failed testing. View results

Spokje’s picture

Status: Needs work » Needs review
FileSize
58.62 KB
636 bytes

Trying to keep up The Good Fight Against TestBot Failures with attached patch

mondrake’s picture

Status: Needs review » Reviewed & tested by the community

Looks all good! RTBC

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 13: 3187309-12.patch, failed testing. View results

Spokje’s picture

Random test fluke, back to RTBC per #14

Spokje’s picture

Status: Needs work » Reviewed & tested by the community
mondrake’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll
ayushmishra206’s picture

Assigned: Unassigned » ayushmishra206
ayushmishra206’s picture

Assigned: ayushmishra206 » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
58.63 KB

Rerolled patch #12. Please review.

ayushmishra206’s picture

Fixed the issue.

longwave’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Needs another reroll :(

adityasingh’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
58.43 KB

Reroll the patch.

mondrake’s picture

Status: Needs review » Reviewed & tested by the community

  • catch committed 3ce319d on 9.2.x
    Issue #3187309 by longwave, Spokje, ayushmishra206, adityasingh,...
catch’s picture

Version: 9.2.x-dev » 9.1.x-dev
Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

This is really nice clean-up, as evidenced by the diffstat:

28 files changed, 152 insertions, 243 deletions.

Committed/pushed to 9.2.x.

We should backport to 9.1.x to make other backports easier, but that patch doesn't apply so needs a re-roll.

paulocs’s picture

Assigned: Unassigned » paulocs
paulocs’s picture

Assigned: paulocs » Unassigned
kleiton_rodrigues’s picture

Assigned: Unassigned » kleiton_rodrigues

I m working

kleiton_rodrigues’s picture

Assigned: kleiton_rodrigues » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
58.6 KB

New rollout performed for version 9.1.x

ayushmishra206’s picture

Verified patch#30. RTBC +1.

longwave’s picture

Status: Needs review » Reviewed & tested by the community

Compared the patches and they look good, the reason this doesn't go back cleanly is #3132919: Replace assert*() involving equality comparison operators with assert(Not)(Equals|Same) was committed to 9.2.x but not backported to 9.1.x.

  • catch committed 184d293 on 9.1.x
    Issue #3187309 by longwave, Spokje, ayushmishra206, kleiton_rodrigues,...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 9.1.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.