diff --git a/core/modules/block/tests/src/Functional/BlockTest.php b/core/modules/block/tests/src/Functional/BlockTest.php
index dce57b896d3edb46c50127d6343afd93e35d2425..3d78821e9eba98d7619689df53cf53124afddf4d 100644
--- a/core/modules/block/tests/src/Functional/BlockTest.php
+++ b/core/modules/block/tests/src/Functional/BlockTest.php
@@ -162,11 +162,14 @@ public function testAddBlockFromLibraryWithWeight() {
         'plugin_id' => $block_name,
         'theme' => $default_theme,
       ]);
-      $links = $this->xpath('//a[contains(@href, :href)]', [':href' => $add_url->toString()]);
-      $this->assertCount(1, $links, 'Found one matching link.');
-      $this->assertEquals(t('Place block'), $links[0]->getText(), 'Found the expected link text.');
 
-      list($path, $query_string) = explode('?', $links[0]->getAttribute('href'), 2);
+      // Verify that one link is found, with the the expected link text.
+      $xpath = $this->assertSession()->buildXPathQuery('//a[contains(@href, :href)]', [':href' => $add_url->toString()]);
+      $this->assertSession()->elementsCount('xpath', $xpath, 1);
+      $this->assertSession()->elementTextEquals('xpath', $xpath, 'Place block');
+
+      $link = $this->getSession()->getPage()->find('xpath', $xpath);
+      list($path, $query_string) = explode('?', $link->getAttribute('href'), 2);
       parse_str($query_string, $query_parts);
       $this->assertEquals($weight, $query_parts['weight'], 'Found the expected weight query string.');
 
@@ -178,7 +181,7 @@ public function testAddBlockFromLibraryWithWeight() {
         'settings[label]' => $title,
       ];
       // Create the block using the link parsed from the library page.
-      $this->drupalGet($this->getAbsoluteUrl($links[0]->getAttribute('href')));
+      $this->drupalGet($this->getAbsoluteUrl($link->getAttribute('href')));
       $this->submitForm($edit, 'Save block');
 
       // Ensure that the block was created with the expected weight.
diff --git a/core/modules/block/tests/src/Functional/Views/DisplayBlockTest.php b/core/modules/block/tests/src/Functional/Views/DisplayBlockTest.php
index 5c70f234368b4e0f9c84db312b42d7cd30c41259..62d015d70f9cf94cfb50a387b259e31195a5fe13 100644
--- a/core/modules/block/tests/src/Functional/Views/DisplayBlockTest.php
+++ b/core/modules/block/tests/src/Functional/Views/DisplayBlockTest.php
@@ -81,9 +81,6 @@ public function testBlockCategory() {
     $this->submitForm($edit, 'Save and edit');
 
     $pattern = '//tr[.//td[text()=:category] and .//td//a[contains(@href, :href)]]';
-
-    // Test that the block was given a default category corresponding to its
-    // base table.
     $arguments = [
       ':href' => Url::fromRoute('block.admin_add', [
         'plugin_id' => 'views_block:' . $edit['id'] . '-block_1',
@@ -91,10 +88,12 @@ public function testBlockCategory() {
       ])->toString(),
       ':category' => 'Lists (Views)',
     ];
+
+    // Test that the block was given a default category corresponding to its
+    // base table.
     $this->drupalGet('admin/structure/block');
     $this->clickLink('Place block');
-    $elements = $this->xpath($pattern, $arguments);
-    $this->assertTrue(!empty($elements), 'The test block appears in the category for its base table.');
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery($pattern, $arguments));
 
     // Duplicate the block before changing the category.
     $this->drupalGet('admin/structure/views/view/' . $edit['id'] . '/edit/block_1');
@@ -103,8 +102,7 @@ public function testBlockCategory() {
 
     // Change the block category to a random string.
     $this->drupalGet('admin/structure/views/view/' . $edit['id'] . '/edit/block_1');
-    $link = $this->xpath('//a[@id="views-block-1-block-category" and normalize-space(text())=:category]', $arguments);
-    $this->assertTrue(!empty($link));
+    $this->assertSession()->elementTextEquals('named', ['id', 'views-block-1-block-category'], 'Lists (Views)');
     $this->clickLink('Lists (Views)');
     $category = $this->randomString();
     $this->submitForm(['block_category' => $category], 'Apply');
@@ -119,9 +117,10 @@ public function testBlockCategory() {
     $arguments[':category'] = $category;
     $this->drupalGet('admin/structure/block');
     $this->clickLink('Place block');
-    $elements = $this->xpath($pattern, $arguments);
-    $this->assertTrue(!empty($elements), 'The test block appears in the custom category.');
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery($pattern, $arguments));
 
+    // Test that the first duplicated test block remains in the original
+    // category.
     $arguments = [
       ':href' => Url::fromRoute('block.admin_add', [
         'plugin_id' => 'views_block:' . $edit['id'] . '-block_2',
@@ -129,9 +128,10 @@ public function testBlockCategory() {
       ])->toString(),
       ':category' => 'Lists (Views)',
     ];
-    $elements = $this->xpath($pattern, $arguments);
-    $this->assertTrue(!empty($elements), 'The first duplicated test block remains in the original category.');
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery($pattern, $arguments));
 
+    // Test that the second duplicated test block appears in the custom
+    // category.
     $arguments = [
       ':href' => Url::fromRoute('block.admin_add', [
         'plugin_id' => 'views_block:' . $edit['id'] . '-block_3',
@@ -139,8 +139,7 @@ public function testBlockCategory() {
       ])->toString(),
       ':category' => $category,
     ];
-    $elements = $this->xpath($pattern, $arguments);
-    $this->assertTrue(!empty($elements), 'The second duplicated test block appears in the custom category.');
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery($pattern, $arguments));
   }
 
   /**
diff --git a/core/modules/book/tests/src/Functional/BookTest.php b/core/modules/book/tests/src/Functional/BookTest.php
index 5160ce30313824b3ac028ff65629dd7bf34cf7d3..11ca3bd9885036aadf9572c07f70106baa6dafdb 100644
--- a/core/modules/book/tests/src/Functional/BookTest.php
+++ b/core/modules/book/tests/src/Functional/BookTest.php
@@ -635,9 +635,11 @@ public function testAdminBookNodeListing() {
     $this->drupalGet('admin/structure/book/' . $this->book->id());
     $this->assertSession()->pageTextContains($this->book->label());
 
-    $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a');
-    $this->assertEquals('View', $elements[0]->getText(), 'View link is found from the list.');
-    $this->assertSameSize($nodes, $elements, 'All the book pages are displayed on the book outline page.');
+    // Test that the view link is found from the list.
+    $this->assertSession()->elementTextEquals('xpath', '//table//ul[@class="dropbutton"]/li/a', 'View');
+
+    // Test that all the book pages are displayed on the book outline page.
+    $this->assertSession()->elementsCount('xpath', '//table//ul[@class="dropbutton"]/li/a', count($nodes));
 
     // Unpublish a book in the hierarchy.
     $nodes[0]->setUnPublished();
@@ -645,8 +647,7 @@ public function testAdminBookNodeListing() {
 
     // Node should still appear on the outline for admins.
     $this->drupalGet('admin/structure/book/' . $this->book->id());
-    $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a');
-    $this->assertSameSize($nodes, $elements, 'All the book pages are displayed on the book outline page.');
+    $this->assertSession()->elementsCount('xpath', '//table//ul[@class="dropbutton"]/li/a', count($nodes));
 
     // Saving a book page not as the current version shouldn't effect the book.
     $old_title = $nodes[1]->getTitle();
@@ -656,8 +657,7 @@ public function testAdminBookNodeListing() {
     $nodes[1]->setTitle($new_title);
     $nodes[1]->save();
     $this->drupalGet('admin/structure/book/' . $this->book->id());
-    $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a');
-    $this->assertSameSize($nodes, $elements, 'All the book pages are displayed on the book outline page.');
+    $this->assertSession()->elementsCount('xpath', '//table//ul[@class="dropbutton"]/li/a', count($nodes));
     $this->assertSession()->responseNotContains($new_title);
     $this->assertSession()->responseContains($old_title);
   }
diff --git a/core/modules/comment/tests/src/Functional/CommentPreviewTest.php b/core/modules/comment/tests/src/Functional/CommentPreviewTest.php
index 32407a80537fbec88334eb6ad4daeded1adad16e..69afa90ea847a42d8638fdc6e35667de6f9e1b79 100644
--- a/core/modules/comment/tests/src/Functional/CommentPreviewTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentPreviewTest.php
@@ -129,8 +129,7 @@ public function testCommentPreviewDuplicateSubmission() {
     $submit_button = $this->assertSession()->buttonExists('Save');
     $submit_button->click();
     $this->assertSession()->pageTextContains('Your comment has been posted.');
-    $elements = $this->xpath('//section[contains(@class, "comment-wrapper")]/article');
-    $this->assertCount(2, $elements);
+    $this->assertSession()->elementsCount('xpath', '//section[contains(@class, "comment-wrapper")]/article', 2);
   }
 
   /**
diff --git a/core/modules/comment/tests/src/Functional/Views/CommentAdminTest.php b/core/modules/comment/tests/src/Functional/Views/CommentAdminTest.php
index 9dd27c14bbb69e241c16708d352adbe7da735f8d..b98c039a8cb6f5e1e19ffdbdb1902db122eee630 100644
--- a/core/modules/comment/tests/src/Functional/Views/CommentAdminTest.php
+++ b/core/modules/comment/tests/src/Functional/Views/CommentAdminTest.php
@@ -128,21 +128,23 @@ public function testApprovalAdminInterface() {
     $this->submitForm([], 'Apply to selected items');
     $this->assertSession()->pageTextContains('Select one or more comments to perform the update on.');
 
-    $subject_link = $this->xpath('//table/tbody/tr/td/a[contains(@href, :href) and contains(@title, :title) and text()=:text]', [
+    // Test that comment listing shows the correct subject link.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//table/tbody/tr/td/a[contains(@href, :href) and contains(@title, :title) and text()=:text]', [
       ':href' => $comments[0]->permalink()->toString(),
       ':title' => Unicode::truncate($comments[0]->get('comment_body')->value, 128),
       ':text' => $comments[0]->getSubject(),
-    ]);
-    $this->assertTrue(!empty($subject_link), 'Comment listing shows the correct subject link.');
+    ]));
+
     // Verify that anonymous author name is displayed correctly.
     $this->assertSession()->pageTextContains($author_name . ' (not verified)');
 
-    $subject_link = $this->xpath('//table/tbody/tr/td/a[contains(@href, :href) and contains(@title, :title) and text()=:text]', [
+    // Test that comment listing shows the correct subject link.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//table/tbody/tr/td/a[contains(@href, :href) and contains(@title, :title) and text()=:text]', [
       ':href' => $anonymous_comment4->permalink()->toString(),
       ':title' => Unicode::truncate($body, 128),
       ':text' => $subject,
-    ]);
-    $this->assertTrue(!empty($subject_link), 'Comment listing shows the correct subject link.');
+    ]));
+
     // Verify that anonymous author name is displayed correctly.
     $this->assertSession()->pageTextContains($author_name . ' (not verified)');
 
@@ -214,16 +216,16 @@ public function testCommentedEntityLabel() {
     $this->drupalLogin($this->adminUser);
     $this->drupalGet('admin/content/comment');
 
-    $comment_author_link = $this->xpath('//table/tbody/tr[1]/td/a[contains(@href, :href) and text()=:text]', [
+    // Test that comment listing links to comment author.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//table/tbody/tr[1]/td/a[contains(@href, :href) and text()=:text]', [
       ':href' => $this->webUser->toUrl()->toString(),
       ':text' => $this->webUser->label(),
-    ]);
-    $this->assertTrue(!empty($comment_author_link), 'Comment listing links to comment author.');
-    $comment_author_link = $this->xpath('//table/tbody/tr[2]/td/a[contains(@href, :href) and text()=:text]', [
+    ]));
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//table/tbody/tr[2]/td/a[contains(@href, :href) and text()=:text]', [
       ':href' => $this->webUser->toUrl()->toString(),
       ':text' => $this->webUser->label(),
-    ]);
-    $this->assertTrue(!empty($comment_author_link), 'Comment listing links to comment author.');
+    ]));
+
     // Admin page contains label of both entities.
     $this->assertSession()->pageTextContains(Html::escape($this->node->label()));
     $this->assertSession()->pageTextContains(Html::escape($block_content->label()));
diff --git a/core/modules/config_translation/tests/src/Functional/ConfigTranslationUiThemeTest.php b/core/modules/config_translation/tests/src/Functional/ConfigTranslationUiThemeTest.php
index 69b20f1c912865af13e432864e4ae77686c26e12..f04173eb4dce1ddc4f5c83134193e655b4e62b8e 100644
--- a/core/modules/config_translation/tests/src/Functional/ConfigTranslationUiThemeTest.php
+++ b/core/modules/config_translation/tests/src/Functional/ConfigTranslationUiThemeTest.php
@@ -69,11 +69,8 @@ public function testThemeDiscovery() {
     $this->drupalLogin($this->adminUser);
 
     $this->drupalGet('admin/appearance');
-    $elements = $this->xpath('//a[normalize-space()=:label and contains(@href, :theme)]', [
-      ':label' => 'Install and set as default',
-      ':theme' => $theme,
-    ]);
-    $this->drupalGet($GLOBALS['base_root'] . $elements[0]->getAttribute('href'), ['external' => TRUE]);
+    $element = $this->assertSession()->elementExists('xpath', "//a[normalize-space()='Install and set as default' and contains(@href, '{$theme}')]");
+    $this->drupalGet($GLOBALS['base_root'] . $element->getAttribute('href'), ['external' => TRUE]);
 
     $translation_base_url = 'admin/config/development/performance/translate';
     $this->drupalGet($translation_base_url);
diff --git a/core/modules/contact/tests/src/Functional/ContactSitewideTest.php b/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
index 336b7b78ad3965ea65269fa17344f4c1a2b9d2cb..6f74dd8e6b772e833083648a1322a99d6ba388fd 100644
--- a/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
+++ b/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
@@ -118,12 +118,8 @@ public function testSiteWideContact() {
     // Cannot use ::assertNoLinkByHref as it does partial url matching and with
     // field_ui enabled admin/structure/contact/manage/personal/fields exists.
     // @todo: See https://www.drupal.org/node/2031223 for the above.
-    $edit_link = $this->xpath('//a[@href=:href]', [
-      ':href' => Url::fromRoute('entity.contact_form.edit_form', ['contact_form' => 'personal'])->toString(),
-    ]);
-    $this->assertTrue(empty($edit_link), new FormattableMarkup('No link containing href %href found.',
-      ['%href' => 'admin/structure/contact/manage/personal']
-    ));
+    $url = Url::fromRoute('entity.contact_form.edit_form', ['contact_form' => 'personal'])->toString();
+    $this->assertSession()->elementNotExists('xpath', "//a[@href='{$url}']");
     $this->assertSession()->linkByHrefNotExists('admin/structure/contact/manage/personal/delete');
 
     $this->drupalGet('admin/structure/contact/manage/personal');
@@ -299,12 +295,11 @@ public function testSiteWideContact() {
     // Test field UI and field integration.
     $this->drupalGet('admin/structure/contact');
 
-    $view_link = $this->xpath('//table/tbody/tr/td/a[contains(@href, :href) and text()=:text]', [
+    // Test contact listing links to contact form.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//table/tbody/tr/td/a[contains(@href, :href) and text()=:text]', [
       ':href' => Url::fromRoute('entity.contact_form.canonical', ['contact_form' => $contact_form])->toString(),
       ':text' => $label,
-      ]
-    );
-    $this->assertTrue(!empty($view_link), 'Contact listing links to contact form.');
+    ]));
 
     // Find out in which row the form we want to add a field to is.
     foreach ($this->xpath('//table/tbody/tr') as $row) {
diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
index c0e6b5837c489dc1cc78156d734a70ccaa137fcd..c5f132bd2615c906e43dd37ed2b64d2eb1aa0eed 100644
--- a/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationUITestBase.php
@@ -226,12 +226,12 @@ protected function doTestTranslationOverview() {
     foreach ($this->langcodes as $langcode) {
       if ($entity->hasTranslation($langcode)) {
         $language = new Language(['id' => $langcode]);
+        // Test that label is correctly shown for translation.
         $view_url = $entity->toUrl('canonical', ['language' => $language])->toString();
-        $elements = $this->xpath('//table//a[@href=:href]', [':href' => $view_url]);
-        $this->assertEquals($entity->getTranslation($langcode)->label(), $elements[0]->getText(), new FormattableMarkup('Label correctly shown for %language translation.', ['%language' => $langcode]));
+        $this->assertSession()->elementTextEquals('xpath', "//table//a[@href='{$view_url}']", $entity->getTranslation($langcode)->label() ?? '');
+        // Test that edit link is correct for translation.
         $edit_path = $entity->toUrl('edit-form', ['language' => $language])->toString();
-        $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', [':href' => $edit_path]);
-        $this->assertEquals(t('Edit'), $elements[0]->getText(), new FormattableMarkup('Edit link correct for %language translation.', ['%language' => $langcode]));
+        $this->assertSession()->elementTextEquals('xpath', "//table//ul[@class='dropbutton']/li/a[@href='{$edit_path}']", 'Edit');
       }
     }
   }
diff --git a/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php b/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php
index ebe9ff86be96c69c8ed8eff8d40e239652b45cb8..5c073ae739006e857c3c35f61288a4c2442f5781 100644
--- a/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php
+++ b/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php
@@ -74,7 +74,8 @@ public function testFieldUIRoutes() {
     $this->drupalGet('admin/config/people/accounts/form-display/register');
     $this->assertSession()->titleEquals('Manage form display | Drupal');
     $this->assertLocalTasks();
-    $this->assertCount(1, $this->xpath('//ul/li[1]/a[contains(text(), :text)]', [':text' => 'Default']), 'Default secondary tab is in first position.');
+    // Test that default secondary tab is in first position.
+    $this->assertSession()->elementsCount('xpath', "//ul/li[1]/a[contains(text(), 'Default')]", 1);
 
     // Create new view mode and verify it's available on the Manage Display
     // screen after enabling it.
diff --git a/core/modules/language/tests/src/Functional/LanguageSwitchingTest.php b/core/modules/language/tests/src/Functional/LanguageSwitchingTest.php
index d9b7ce5af6842d411e02605df59392ebb2ff1337..1f5ec54495a329bda1671852d12eb13f1df9ba2c 100644
--- a/core/modules/language/tests/src/Functional/LanguageSwitchingTest.php
+++ b/core/modules/language/tests/src/Functional/LanguageSwitchingTest.php
@@ -315,19 +315,15 @@ protected function doTestLanguageLinkActiveClassAuthenticated() {
     $this->drupalGet($path);
 
     // Language code 'none' link should be active.
-    $langcode = 'none';
-    $links = $this->xpath('//a[@id = :id and @data-drupal-link-system-path = :path]', [':id' => 'no_lang_link', ':path' => $path]);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode has the correct attributes that will allow the drupal.active-link library to mark it as active.');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'no_lang_link'], 'data-drupal-link-system-path', $path);
 
     // Language code 'en' link should be active.
-    $langcode = 'en';
-    $links = $this->xpath('//a[@id = :id and @hreflang = :lang and @data-drupal-link-system-path = :path]', [':id' => 'en_link', ':lang' => 'en', ':path' => $path]);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode has the correct attributes that will allow the drupal.active-link library to mark it as active.');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'en_link'], 'hreflang', 'en');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'en_link'], 'data-drupal-link-system-path', $path);
 
     // Language code 'fr' link should not be active.
-    $langcode = 'fr';
-    $links = $this->xpath('//a[@id = :id and @hreflang = :lang and @data-drupal-link-system-path = :path]', [':id' => 'fr_link', ':lang' => 'fr', ':path' => $path]);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode has the correct attributes that will allow the drupal.active-link library to NOT mark it as active.');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'fr_link'], 'hreflang', 'fr');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'fr_link'], 'data-drupal-link-system-path', $path);
 
     // Verify that drupalSettings contains the correct values.
     $settings = $this->getDrupalSettings();
@@ -340,19 +336,15 @@ protected function doTestLanguageLinkActiveClassAuthenticated() {
     $this->drupalGet('fr/language_test/type-link-active-class');
 
     // Language code 'none' link should be active.
-    $langcode = 'none';
-    $links = $this->xpath('//a[@id = :id and @data-drupal-link-system-path = :path]', [':id' => 'no_lang_link', ':path' => $path]);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode has the correct attributes that will allow the drupal.active-link library to mark it as active.');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'no_lang_link'], 'data-drupal-link-system-path', $path);
 
     // Language code 'en' link should not be active.
-    $langcode = 'en';
-    $links = $this->xpath('//a[@id = :id and @hreflang = :lang and @data-drupal-link-system-path = :path]', [':id' => 'en_link', ':lang' => 'en', ':path' => $path]);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode has the correct attributes that will allow the drupal.active-link library to NOT mark it as active.');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'en_link'], 'hreflang', 'en');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'en_link'], 'data-drupal-link-system-path', $path);
 
     // Language code 'fr' link should be active.
-    $langcode = 'fr';
-    $links = $this->xpath('//a[@id = :id and @hreflang = :lang and @data-drupal-link-system-path = :path]', [':id' => 'fr_link', ':lang' => 'fr', ':path' => $path]);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode has the correct attributes that will allow the drupal.active-link library to mark it as active.');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'fr_link'], 'hreflang', 'fr');
+    $this->assertSession()->elementAttributeContains('named', ['id', 'fr_link'], 'data-drupal-link-system-path', $path);
 
     // Verify that drupalSettings contains the correct values.
     $settings = $this->getDrupalSettings();
@@ -376,38 +368,26 @@ protected function doTestLanguageLinkActiveClassAnonymous() {
     $this->drupalGet('language_test/type-link-active-class');
 
     // Language code 'none' link should be active.
-    $langcode = 'none';
-    $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', [':id' => 'no_lang_link', ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode is marked active.');
+    $this->assertSession()->elementExists('xpath', "//a[@id = 'no_lang_link' and contains(@class, 'is-active')]");
 
     // Language code 'en' link should be active.
-    $langcode = 'en';
-    $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', [':id' => 'en_link', ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode is marked active.');
+    $this->assertSession()->elementExists('xpath', "//a[@id = 'en_link' and contains(@class, 'is-active')]");
 
     // Language code 'fr' link should not be active.
-    $langcode = 'fr';
-    $links = $this->xpath('//a[@id = :id and not(contains(@class, :class))]', [':id' => 'fr_link', ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode is NOT marked active.');
+    $this->assertSession()->elementExists('xpath', "//a[@id = 'fr_link' and not(contains(@class, 'is-active'))]");
 
     // Test links generated by the link generator on a French page.
     $current_language = 'French';
     $this->drupalGet('fr/language_test/type-link-active-class');
 
     // Language code 'none' link should be active.
-    $langcode = 'none';
-    $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', [':id' => 'no_lang_link', ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode is marked active.');
+    $this->assertSession()->elementExists('xpath', "//a[@id = 'no_lang_link' and contains(@class, 'is-active')]");
 
     // Language code 'en' link should not be active.
-    $langcode = 'en';
-    $links = $this->xpath('//a[@id = :id and not(contains(@class, :class))]', [':id' => 'en_link', ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode is NOT marked active.');
+    $this->assertSession()->elementExists('xpath', "//a[@id = 'en_link' and not(contains(@class, 'is-active'))]");
 
     // Language code 'fr' link should be active.
-    $langcode = 'fr';
-    $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', [':id' => 'fr_link', ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by $function_name to the current $current_language page with langcode $langcode is marked active.');
+    $this->assertSession()->elementExists('xpath', "//a[@id = 'fr_link' and contains(@class, 'is-active')]");
   }
 
   /**
diff --git a/core/modules/locale/tests/src/Functional/LocalePathTest.php b/core/modules/locale/tests/src/Functional/LocalePathTest.php
index 635181e5e6258b167a1def2bb4ec13d6cd869885..02683cdef0cedecc03f58b2740600cd0f3649b12 100644
--- a/core/modules/locale/tests/src/Functional/LocalePathTest.php
+++ b/core/modules/locale/tests/src/Functional/LocalePathTest.php
@@ -138,10 +138,14 @@ public function testPathLanguageConfiguration() {
     // Test that both node titles link to our path alias.
     $this->drupalGet('admin/content');
     $custom_path_url = Url::fromUserInput('/' . $custom_path)->toString();
-    $elements = $this->xpath('//a[@href=:href and normalize-space(text())=:title]', [':href' => $custom_path_url, ':title' => $first_node->label()]);
-    $this->assertTrue(!empty($elements), 'First node links to the path alias.');
-    $elements = $this->xpath('//a[@href=:href and normalize-space(text())=:title]', [':href' => $custom_path_url, ':title' => $second_node->label()]);
-    $this->assertTrue(!empty($elements), 'Second node links to the path alias.');
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//a[@href=:href and normalize-space(text())=:title]', [
+      ':href' => $custom_path_url,
+      ':title' => $first_node->label(),
+    ]));
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//a[@href=:href and normalize-space(text())=:title]', [
+      ':href' => $custom_path_url,
+      ':title' => $second_node->label(),
+    ]));
 
     // Confirm that the custom path leads to the first node.
     $this->drupalGet($custom_path);
diff --git a/core/modules/node/tests/src/Functional/NodeAdminTest.php b/core/modules/node/tests/src/Functional/NodeAdminTest.php
index 67134179cab7e14d166e4ce27c3344164938f22e..4b0412cc9b50de8ee8ebdb48a5a5420963027fa9 100644
--- a/core/modules/node/tests/src/Functional/NodeAdminTest.php
+++ b/core/modules/node/tests/src/Functional/NodeAdminTest.php
@@ -103,8 +103,11 @@ public function testContentAdminSort() {
 
     $this->drupalGet('admin/content');
     foreach ($nodes_query as $delta => $string) {
-      $elements = $this->xpath('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [':class' => 'views-table', ':label' => $string]);
-      $this->assertTrue(!empty($elements), 'The node was found in the correct order.');
+      // Verify that the node was found in the correct order.
+      $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [
+        ':class' => 'views-table',
+        ':label' => $string,
+      ]));
     }
 
     // Compare the rendered HTML node list to a query for the nodes ordered by
@@ -117,8 +120,11 @@ public function testContentAdminSort() {
 
     $this->drupalGet('admin/content', ['query' => ['sort' => 'asc', 'order' => 'title']]);
     foreach ($nodes_query as $delta => $string) {
-      $elements = $this->xpath('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [':class' => 'views-table', ':label' => $string]);
-      $this->assertTrue(!empty($elements), 'The node was found in the correct order.');
+      // Verify that the node was found in the correct order.
+      $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [
+        ':class' => 'views-table',
+        ':label' => $string,
+      ]));
     }
   }
 
diff --git a/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php b/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
index 31d8f2f2815caddb142b250c2a41bcdac9039c07..fb5961beda807fe72fbba4d89bdb48c1f92fbe75 100644
--- a/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
+++ b/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
@@ -171,10 +171,9 @@ public function testNodeRevisionsTabWithDefaultRevision() {
     // Verify that the latest affected revision having been a default revision
     // is displayed as the current one.
     $this->assertSession()->linkByHrefNotExists('/node/' . $node_id . '/revisions/1/revert');
-    $elements = $this->xpath('//tr[contains(@class, "revision-current")]/td/a[1]');
     // The site may be installed in a subdirectory, so check if the URL is
     // contained in the retrieved one.
-    $this->assertStringContainsString('/node/1', current($elements)->getAttribute('href'));
+    $this->assertSession()->elementAttributeContains('xpath', '//tr[contains(@class, "revision-current")]/td/a[1]', 'href', '/node/1');
 
     // Verify that the default revision can be an older revision than the latest
     // one.
diff --git a/core/modules/path/tests/src/Functional/PathAliasTest.php b/core/modules/path/tests/src/Functional/PathAliasTest.php
index 18f518eb1040caf889f32fc4c89e2ea4e075f162..d508a5f8dd8579365ae34979e5752d3e513364fd 100644
--- a/core/modules/path/tests/src/Functional/PathAliasTest.php
+++ b/core/modules/path/tests/src/Functional/PathAliasTest.php
@@ -394,9 +394,7 @@ public function testNodeAlias() {
     // using \Drupal\Tests\BrowserTestBase::assertSession()->addressEquals()
     // would actually make the test pass unconditionally on the testbot (or
     // anywhere else where Drupal is installed in a subdirectory).
-    $link_xpath = $this->xpath('//a[normalize-space(text())=:label]', [':label' => $node6->getTitle()]);
-    $link_href = $link_xpath[0]->getAttribute('href');
-    $this->assertEquals($link_href, base_path() . $alias);
+    $this->assertSession()->elementAttributeContains('xpath', "//a[normalize-space(text())='{$node6->getTitle()}']", 'href', base_path() . $alias);
     $this->clickLink($node6->getTitle());
     $this->assertSession()->statusCodeEquals(404);
   }
diff --git a/core/modules/search/tests/src/Functional/SearchLanguageTest.php b/core/modules/search/tests/src/Functional/SearchLanguageTest.php
index 1a6cc4dae3b35b57f771653e3d6a2e8f1d84c89b..24d5d2cb8cbc05ff8002db183befcc274ed6384d 100644
--- a/core/modules/search/tests/src/Functional/SearchLanguageTest.php
+++ b/core/modules/search/tests/src/Functional/SearchLanguageTest.php
@@ -163,8 +163,7 @@ public function testLanguageAttributes() {
 
     $node = $this->searchableNodes[1]->getTranslation('es');
     $this->assertSession()->elementExists('xpath', '//div[@class="layout-content"]//ol/li/h3[contains(@lang, "es")]');
-    $result = $this->xpath('//div[@class="layout-content"]//ol/li/h3[contains(@lang, "es")]/a');
-    $this->assertEquals($node->getTitle(), $result[0]->getText());
+    $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//ol/li/h3[contains(@lang, "es")]/a', $node->getTitle());
     $this->assertSession()->elementExists('xpath', '//div[@class="layout-content"]//ol/li/p[contains(@lang, "es")]');
 
     // Visit the search form in Spanish language.
@@ -172,8 +171,7 @@ public function testLanguageAttributes() {
     $this->submitForm(['keys' => 'First node'], 'Search');
     $this->assertSession()->elementExists('xpath', '//div[@class="layout-content"]//ol/li/h3[contains(@lang, "en")]');
     $node = $this->searchableNodes[0]->getTranslation('en');
-    $result = $this->xpath('//div[@class="layout-content"]//ol/li/h3[contains(@lang, "en")]/a');
-    $this->assertEquals($node->getTitle(), $result[0]->getText());
+    $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//ol/li/h3[contains(@lang, "en")]/a', $node->getTitle());
     $this->assertSession()->elementExists('xpath', '//div[@class="layout-content"]//ol/li/p[contains(@lang, "en")]');
   }
 
diff --git a/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php b/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php
index 3234764952842615815c5f26a30526ece610e619..e4182490220ec49ec893106a278e127b504c08fb 100644
--- a/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php
+++ b/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php
@@ -192,8 +192,7 @@ public function testShortcutQuickLink() {
     $this->assertSession()->pageTextContains('Added a shortcut for Create Basic page.');
     // Assure that Article does not have its shortcut indicated as set.
     $this->drupalGet('node/add/article');
-    $link = $this->xpath('//a[normalize-space()=:label]', [':label' => 'Remove from Default shortcuts']);
-    $this->assertTrue(empty($link), 'Link Remove to Default shortcuts not found for Create Article page.');
+    $this->assertSession()->elementNotExists('xpath', "//a[normalize-space()='Remove from Default shortcuts']");
     // Add Shortcut for Article.
     $this->clickLink('Add to Default shortcuts');
     $this->assertSession()->pageTextContains('Added a shortcut for Create Article.');
@@ -325,24 +324,21 @@ public function testNoShortcutLink() {
       ->save();
 
     $this->drupalGet('page-that-does-not-exist');
-    $result = $this->xpath('//a[contains(@class, "shortcut-action--add")]');
-    $this->assertTrue(empty($result), 'Add to shortcuts link was not shown on a page not found.');
+    // Test that add to shortcuts link is not shown on a page not found.
+    $this->assertSession()->elementNotExists('xpath', '//a[contains(@class, "shortcut-action--add")]');
 
     // The user does not have access to this path.
     $this->drupalGet('admin/modules');
-    $result = $this->xpath('//a[contains(@class, "shortcut-action--add")]');
-    $this->assertTrue(empty($result), 'Add to shortcuts link was not shown on a page the user does not have access to.');
+    $this->assertSession()->elementNotExists('xpath', '//a[contains(@class, "shortcut-action--add")]');
 
     // Verify that the testing mechanism works by verifying the shortcut link
     // appears on admin/content.
     $this->drupalGet('admin/content');
-    $result = $this->xpath('//a[contains(@class, "shortcut-action--remove")]');
-    $this->assertTrue(!empty($result), 'Remove from shortcuts link was shown on a page the user does have access to.');
+    $this->assertSession()->elementExists('xpath', '//a[contains(@class, "shortcut-action--remove")]');
 
     // Verify that the shortcut link appears on routing only pages.
     $this->drupalGet('router_test/test2');
-    $result = $this->xpath('//a[contains(@class, "shortcut-action--add")]');
-    $this->assertTrue(!empty($result), 'Add to shortcuts link was shown on a page the user does have access to.');
+    $this->assertSession()->elementExists('xpath', '//a[contains(@class, "shortcut-action--add")]');
   }
 
   /**
diff --git a/core/modules/shortcut/tests/src/Functional/ShortcutTranslationUITest.php b/core/modules/shortcut/tests/src/Functional/ShortcutTranslationUITest.php
index 75b0a9378dbf17a2622039d7ded2ec3b7c1563e5..a32eedc705e2f5dc18bc0777c2a0c66e594ed96b 100644
--- a/core/modules/shortcut/tests/src/Functional/ShortcutTranslationUITest.php
+++ b/core/modules/shortcut/tests/src/Functional/ShortcutTranslationUITest.php
@@ -83,8 +83,7 @@ protected function doTestBasicTranslation() {
         $this->drupalGet('<front>', ['language' => $language]);
         $expected_path = \Drupal::urlGenerator()->generateFromRoute('user.page', [], ['language' => $language]);
         $label = $entity->getTranslation($langcode)->label();
-        $elements = $this->xpath('//nav[contains(@class, "toolbar-lining")]/ul[@class="toolbar-menu"]/li/a[contains(@href, :href) and normalize-space(text())=:label]', [':href' => $expected_path, ':label' => $label]);
-        $this->assertTrue(!empty($elements), new FormattableMarkup('Translated @language shortcut link @label found.', ['@label' => $label, '@language' => $language->getName()]));
+        $this->assertSession()->elementExists('xpath', "//nav[contains(@class, 'toolbar-lining')]/ul[@class='toolbar-menu']/li/a[contains(@href, '{$expected_path}') and normalize-space(text())='{$label}']");
       }
     }
   }
diff --git a/core/modules/system/tests/src/Functional/Common/UrlTest.php b/core/modules/system/tests/src/Functional/Common/UrlTest.php
index 8384d142606b78a86fbd71e4d8c5f2ace26588f2..e79dfe45eb776da63363ae64307f85e2c270301f 100644
--- a/core/modules/system/tests/src/Functional/Common/UrlTest.php
+++ b/core/modules/system/tests/src/Functional/Common/UrlTest.php
@@ -41,21 +41,48 @@ public function testActiveLinkAttributes() {
     $path = 'common-test/type-link-active-class';
 
     $this->drupalGet($path, $options_no_query);
-    $links = $this->xpath('//a[@href = :href and contains(@class, :class)]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_no_query)->toString(), ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page is marked active.');
 
-    $links = $this->xpath('//a[@href = :href and not(contains(@class, :class))]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_query)->toString(), ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page with a query string when the current page has no query string is not marked active.');
+    // Test that a link generated by the link generator to the current page is
+    // marked active.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery(
+      '//a[@href = :href and contains(@class, "is-active")]', [
+        ':href' => Url::fromRoute('common_test.l_active_class', [], $options_no_query)->toString(),
+      ]
+    ));
+    // Test that a link generated by the link generator to the current page
+    // with a query string when the current page has no query string is not
+    // marked active.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery(
+      '//a[@href = :href and not(contains(@class, "is-active"))]', [
+        ':href' => Url::fromRoute('common_test.l_active_class', [], $options_query)->toString(),
+      ]
+    ));
 
     $this->drupalGet($path, $options_query);
-    $links = $this->xpath('//a[@href = :href and contains(@class, :class)]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_query)->toString(), ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page with a query string that matches the current query string is marked active.');
 
-    $links = $this->xpath('//a[@href = :href and contains(@class, :class)]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_query_reverse)->toString(), ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page with a query string that has matching parameters to the current query string but in a different order is marked active.');
-
-    $links = $this->xpath('//a[@href = :href and not(contains(@class, :class))]', [':href' => Url::fromRoute('common_test.l_active_class', [], $options_no_query)->toString(), ':class' => 'is-active']);
-    $this->assertTrue(isset($links[0]), 'A link generated by the link generator to the current page without a query string when the current page has a query string is not marked active.');
+    // Test that a link generated by the link generator to the current page with
+    // a query string that matches the current query string is marked active.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery(
+      '//a[@href = :href and contains(@class, "is-active")]', [
+        ':href' => Url::fromRoute('common_test.l_active_class', [], $options_query)->toString(),
+      ]
+    ));
+    // Test that a link generated by the link generator to the current page with
+    // a query string that has matching parameters to the current query string
+    // but in a different order is marked active.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery(
+      '//a[@href = :href and contains(@class, "is-active")]', [
+        ':href' => Url::fromRoute('common_test.l_active_class', [], $options_query_reverse)->toString(),
+      ]
+    ));
+    // Test that a link generated by the link generator to the current page
+    // without a query string when the current page has a query string is not
+    // marked active.
+    $this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery(
+      '//a[@href = :href and not(contains(@class, "is-active"))]', [
+        ':href' => Url::fromRoute('common_test.l_active_class', [], $options_no_query)->toString(),
+      ]
+    ));
   }
 
 }
diff --git a/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php b/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
index 23ea8bf52d6b25c4a73449e6fc397fff8c2a37ee..7df11671adba266a4c15fb353ded900c131034dd 100644
--- a/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
+++ b/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
@@ -127,9 +127,8 @@ public function testPluginLocalTask() {
     $this->assertLocalTaskAppears($title);
 
     // Ensure the view tab is active.
-    $result = $this->xpath('//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a');
-    $this->assertCount(1, $result, 'There is just a single active tab.');
-    $this->assertEquals('View(active tab)', $result[0]->getText(), 'The view tab is active.');
+    $this->assertSession()->elementsCount('xpath', '//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a', 1);
+    $this->assertSession()->elementTextEquals('xpath', '//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a', 'View(active tab)');
 
     // Verify that local tasks in the second level appear.
     $sub_tasks = [
@@ -142,17 +141,17 @@ public function testPluginLocalTask() {
     $this->drupalGet(Url::fromRoute('menu_test.local_task_test_tasks_settings'));
     $this->assertLocalTasks($sub_tasks, 1);
 
-    $result = $this->xpath('//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a');
-    $this->assertCount(1, $result, 'There is just a single active tab.');
-    $this->assertEquals('Settings(active tab)', $result[0]->getText(), 'The settings tab is active.');
+    $this->assertSession()->elementsCount('xpath', '//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a', 1);
+    $this->assertSession()->elementTextEquals('xpath', '//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a', 'Settings(active tab)');
 
     $this->drupalGet(Url::fromRoute('menu_test.local_task_test_tasks_settings_sub1'));
     $this->assertLocalTasks($sub_tasks, 1);
 
-    $result = $this->xpath('//ul[contains(@class, "tabs")]//a[contains(@class, "active")]');
-    $this->assertCount(2, $result, 'There are tabs active on both levels.');
-    $this->assertEquals('Settings(active tab)', $result[0]->getText(), 'The settings tab is active.');
-    $this->assertEquals('Dynamic title for TestTasksSettingsSub1(active tab)', $result[1]->getText(), 'The sub1 tab is active.');
+    $xpath = '//ul[contains(@class, "tabs")]//a[contains(@class, "active")]';
+    $this->assertSession()->elementsCount('xpath', $xpath, 2);
+    $links = $this->xpath($xpath);
+    $this->assertEquals('Settings(active tab)', $links[0]->getText(), 'The settings tab is active.');
+    $this->assertEquals('Dynamic title for TestTasksSettingsSub1(active tab)', $links[1]->getText(), 'The sub1 tab is active.');
 
     $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'kittens:ragdoll');
     $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'kittens:dwarf-cat');
diff --git a/core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php b/core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php
index 57415a37d3dc3cd4b0497f43afd78e7ce3af495d..ac85028b51d0719a1b92dea212258281dd998756 100644
--- a/core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php
+++ b/core/modules/system/tests/src/Functional/Menu/MenuAccessTest.php
@@ -61,11 +61,12 @@ public function testMenuBlockLinksAccessCheck() {
     // Attempt to access a restricted local task.
     $this->drupalGet('foo/asdf/c');
     $this->assertSession()->statusCodeEquals(403);
-    $elements = $this->xpath('//ul[@class=:class]/li/a[@href=:href]', [
-      ':class' => 'tabs primary',
-      ':href' => Url::fromRoute('menu_test.router_test1', ['bar' => 'asdf'])->toString(),
-    ]);
-    $this->assertTrue(empty($elements), 'No tab linking to foo/asdf found');
+    // No tab linking to foo/asdf should be found.
+    $this->assertSession()->elementNotExists('xpath', $this->assertSession()->buildXPathQuery(
+      '//ul[@class="tabs primary"]/li/a[@href=:href]', [
+        ':href' => Url::fromRoute('menu_test.router_test1', ['bar' => 'asdf'])->toString(),
+      ]
+    ));
     $this->assertSession()->linkByHrefNotExists('foo/asdf/b');
     $this->assertSession()->linkByHrefNotExists('foo/asdf/c');
   }
diff --git a/core/modules/system/tests/src/Functional/Pager/PagerTest.php b/core/modules/system/tests/src/Functional/Pager/PagerTest.php
index 04d90d6d1ddfa59e4d487488f68eda0d5e6d0716..504f6e6a3003d518525919c4c61addc73564f703 100644
--- a/core/modules/system/tests/src/Functional/Pager/PagerTest.php
+++ b/core/modules/system/tests/src/Functional/Pager/PagerTest.php
@@ -67,10 +67,10 @@ public function testActiveClass() {
     $this->assertPagerItems($current_page);
 
     // Verify last page.
-    $elements = $this->xpath('//li[contains(@class, :class)]/a', [':class' => 'pager__item--last']);
-    preg_match('@page=(\d+)@', $elements[0]->getAttribute('href'), $matches);
+    $element = $this->assertSession()->elementExists('xpath', '//li[contains(@class, "pager__item--last")]/a');
+    preg_match('@page=(\d+)@', $element->getAttribute('href'), $matches);
     $current_page = (int) $matches[1];
-    $this->drupalGet($GLOBALS['base_root'] . parse_url($this->getUrl())['path'] . $elements[0]->getAttribute('href'), ['external' => TRUE]);
+    $this->drupalGet($GLOBALS['base_root'] . parse_url($this->getUrl())['path'] . $element->getAttribute('href'), ['external' => TRUE]);
     $this->assertPagerItems($current_page);
 
     // Verify the pager does not render on a list without pagination.
@@ -90,8 +90,7 @@ public function testPagerQueryParametersAndCacheContext() {
     $this->assertCacheContext('url.query_args');
 
     // Go to last page, the count of pager calls need to go to 1.
-    $elements = $this->xpath('//li[contains(@class, :class)]/a', [':class' => 'pager__item--last']);
-    $elements[0]->click();
+    $this->assertSession()->elementExists('xpath', '//li[contains(@class, "pager__item--last")]/a')->click();
     $this->assertSession()->pageTextContains('Pager calls: 1');
     $this->assertSession()->pageTextContains('[url.query_args.pagers:0]=0.60');
     $this->assertCacheContext('url.query_args');
@@ -99,10 +98,8 @@ public function testPagerQueryParametersAndCacheContext() {
     // Reset counter to 0.
     $this->drupalGet('pager-test/query-parameters');
     // Go back to first page, the count of pager calls need to go to 2.
-    $elements = $this->xpath('//li[contains(@class, :class)]/a', [':class' => 'pager__item--last']);
-    $elements[0]->click();
-    $elements = $this->xpath('//li[contains(@class, :class)]/a', [':class' => 'pager__item--first']);
-    $elements[0]->click();
+    $this->assertSession()->elementExists('xpath', '//li[contains(@class, "pager__item--last")]/a')->click();
+    $this->assertSession()->elementExists('xpath', '//li[contains(@class, "pager__item--first")]/a')->click();
     $this->assertSession()->pageTextContains('Pager calls: 2');
     $this->assertSession()->pageTextContains('[url.query_args.pagers:0]=0.0');
     $this->assertCacheContext('url.query_args');
diff --git a/core/modules/system/tests/src/Functional/System/ThemeTest.php b/core/modules/system/tests/src/Functional/System/ThemeTest.php
index b41b76584b54eb7e613c3dfd7d7d2eb3d1b4864d..d481a1d17d91a4ba5e05d304a61ad47bc249f17a 100644
--- a/core/modules/system/tests/src/Functional/System/ThemeTest.php
+++ b/core/modules/system/tests/src/Functional/System/ThemeTest.php
@@ -144,11 +144,7 @@ public function testThemeSettings() {
       // branding block.
       $this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
       $this->drupalGet('');
-      $elements = $this->xpath('//header//a[@rel=:rel]/img', [
-          ':rel' => 'home',
-        ]
-      );
-      $this->assertEquals($expected['src'], $elements[0]->getAttribute('src'));
+      $this->assertSession()->elementAttributeContains('xpath', '//header//a[@rel="home"]/img', 'src', $expected['src']);
     }
     $unsupported_paths = [
       // Stream wrapper URI to non-existing file.
@@ -196,11 +192,7 @@ public function testThemeSettings() {
 
     $this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
     $this->drupalGet('');
-    $elements = $this->xpath('//header//a[@rel=:rel]/img', [
-        ':rel' => 'home',
-      ]
-    );
-    $this->assertEquals($file_url_generator->generateString($uploaded_filename), $elements[0]->getAttribute('src'));
+    $this->assertSession()->elementAttributeContains('xpath', '//header//a[@rel="home"]/img', 'src', $file_url_generator->generateString($uploaded_filename));
 
     $this->container->get('theme_installer')->install(['bartik']);
 
diff --git a/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php b/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
index 37c9eb05c3c738a96d7f4fea58cc4aa35f05e2df..c6b31078c2479059de315621f732a174bb33b40e 100644
--- a/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
+++ b/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
@@ -509,7 +509,7 @@ public function testNoUpdateFunctionality() {
     $this->clickLink('Continue');
     $this->assertSession()->pageTextContains('No pending updates.');
     $this->assertSession()->linkNotExists('Administration pages');
-    $this->assertEmpty($this->xpath('//main//a[contains(@href, :href)]', [':href' => 'update.php']));
+    $this->assertSession()->elementNotExists('xpath', '//main//a[contains(@href, "update.php")]');
     $this->clickLink('Front page');
     $this->assertSession()->statusCodeEquals(200);
 
@@ -524,7 +524,7 @@ public function testNoUpdateFunctionality() {
     $this->clickLink('Continue');
     $this->assertSession()->pageTextContains('No pending updates.');
     $this->assertSession()->linkExists('Administration pages');
-    $this->assertEmpty($this->xpath('//main//a[contains(@href, :href)]', [':href' => 'update.php']));
+    $this->assertSession()->elementNotExists('xpath', '//main//a[contains(@href, "update.php")]');
     $this->clickLink('Administration pages');
     $this->assertSession()->statusCodeEquals(200);
   }
@@ -568,7 +568,7 @@ public function testSuccessfulUpdateFunctionality() {
     $this->assertSession()->pageTextContains('Updates were attempted.');
     $this->assertSession()->linkExists('logged');
     $this->assertSession()->linkExists('Administration pages');
-    $this->assertEmpty($this->xpath('//main//a[contains(@href, :href)]', [':href' => 'update.php']));
+    $this->assertSession()->elementNotExists('xpath', '//main//a[contains(@href, "update.php")]');
     $this->clickLink('Administration pages');
     $this->assertSession()->statusCodeEquals(200);
   }
@@ -644,7 +644,7 @@ public function testSuccessfulMultilingualUpdateFunctionality() {
     $this->assertSession()->pageTextContains('Updates were attempted.');
     $this->assertSession()->linkExists('logged');
     $this->assertSession()->linkExists('Administration pages');
-    $this->assertEmpty($this->xpath('//main//a[contains(@href, :href)]', [':href' => 'update.php']));
+    $this->assertSession()->elementNotExists('xpath', '//main//a[contains(@href, "update.php")]');
     $this->clickLink('Administration pages');
     $this->assertSession()->statusCodeEquals(200);
   }
@@ -714,7 +714,7 @@ protected function runUpdates($maintenance_mode) {
 
     // Verify that there are no links to different parts of the workflow.
     $this->assertSession()->linkNotExists('Administration pages');
-    $this->assertEmpty($this->xpath('//main//a[contains(@href, :href)]', [':href' => 'update.php']));
+    $this->assertSession()->elementNotExists('xpath', '//main//a[contains(@href, "update.php")]');
     $this->assertSession()->linkNotExists('logged');
 
     // Verify the front page can be visited following the upgrade.
diff --git a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldAllTermsTest.php b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldAllTermsTest.php
index f4e55c645ae24a02230cf8541ac292623f38fb69..f3de53a3a0917ed71d87a0f030d14f4482340ba0 100644
--- a/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldAllTermsTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyFieldAllTermsTest.php
@@ -33,16 +33,20 @@ public function testViewsHandlerAllTermsField() {
     $this->executeView($view);
     $this->drupalGet('taxonomy_all_terms_test');
 
-    $actual = $this->xpath('//a[@href="' . $this->term1->toUrl()->toString() . '"]');
-    $this->assertCount(2, $actual, 'Correct number of taxonomy term1 links');
-    $this->assertEquals($this->term1->label(), $actual[0]->getText());
-    $this->assertEquals($this->term1->label(), $actual[1]->getText());
+    // Test term1 links.
+    $xpath = '//a[@href="' . $this->term1->toUrl()->toString() . '"]';
+    $this->assertSession()->elementsCount('xpath', $xpath, 2);
+    $links = $this->xpath($xpath);
+    $this->assertEquals($this->term1->label(), $links[0]->getText());
+    $this->assertEquals($this->term1->label(), $links[1]->getText());
     $this->assertSession()->assertEscaped($this->term1->label());
 
-    $actual = $this->xpath('//a[@href="' . $this->term2->toUrl()->toString() . '"]');
-    $this->assertCount(2, $actual, 'Correct number of taxonomy term2 links');
-    $this->assertEquals($this->term2->label(), $actual[0]->getText());
-    $this->assertEquals($this->term2->label(), $actual[1]->getText());
+    // Test term2 links.
+    $xpath = '//a[@href="' . $this->term2->toUrl()->toString() . '"]';
+    $this->assertSession()->elementsCount('xpath', $xpath, 2);
+    $links = $this->xpath($xpath);
+    $this->assertEquals($this->term2->label(), $links[0]->getText());
+    $this->assertEquals($this->term2->label(), $links[1]->getText());
   }
 
   /**
diff --git a/core/modules/toolbar/tests/src/Functional/ToolbarMenuTranslationTest.php b/core/modules/toolbar/tests/src/Functional/ToolbarMenuTranslationTest.php
index f7889240ace493216dd6e7421a977b403175dd8c..0fb7fbeb25bdb85a87f8e0d1b1f27490b0039d6a 100644
--- a/core/modules/toolbar/tests/src/Functional/ToolbarMenuTranslationTest.php
+++ b/core/modules/toolbar/tests/src/Functional/ToolbarMenuTranslationTest.php
@@ -77,8 +77,7 @@ public function testToolbarClasses() {
     $this->assertSession()->pageTextNotContains('No strings available.');
 
     // Check that the class is on the item before we translate it.
-    $xpath = $this->xpath('//a[contains(@class, "icon-system-admin-structure")]');
-    $this->assertCount(1, $xpath, 'The menu item class ok before translation.');
+    $this->assertSession()->elementsCount('xpath', '//a[contains(@class, "icon-system-admin-structure")]', 1);
 
     // Translate the menu item.
     $menu_item_translated = $this->randomMachineName();
@@ -108,8 +107,7 @@ public function testToolbarClasses() {
 
     // Toolbar icons are included based on the presence of a specific class on
     // the menu item. Ensure that class also exists for a translated menu item.
-    $xpath = $this->xpath('//a[contains(@class, "icon-system-admin-structure")]');
-    $this->assertCount(1, $xpath, 'The menu item class is the same.');
+    $this->assertSession()->elementsCount('xpath', '//a[contains(@class, "icon-system-admin-structure")]', 1);
   }
 
 }
diff --git a/core/modules/tracker/tests/src/Functional/TrackerRecentContentLinkTest.php b/core/modules/tracker/tests/src/Functional/TrackerRecentContentLinkTest.php
index f0b85b34decff3d4747d238892690a2e9d3d2fdd..c704b42d165f9c110689b532d68743e56984d1d2 100644
--- a/core/modules/tracker/tests/src/Functional/TrackerRecentContentLinkTest.php
+++ b/core/modules/tracker/tests/src/Functional/TrackerRecentContentLinkTest.php
@@ -35,13 +35,7 @@ public function testRecentContentLink() {
     // Log in and get the homepage.
     $this->drupalLogin($user);
     $this->drupalGet('<front>');
-
-    $link = $this->xpath('//ul/li/a[contains(@href, :href) and text()=:text]', [
-      ':menu_class' => 'menu-item',
-      ':href' => '/activity',
-      ':text' => 'Recent content',
-    ]);
-    $this->assertCount(1, $link);
+    $this->assertSession()->elementsCount('xpath', '//ul/li/a[contains(@href, "/activity") and text()="Recent content"]', 1);
   }
 
 }
diff --git a/core/modules/user/tests/src/Functional/UserAccountLinksTest.php b/core/modules/user/tests/src/Functional/UserAccountLinksTest.php
index 8fb5ba353d4f49b1d4cd888ab9b6f6e94afc4eb6..48c58d6af1472227bcd54facf20513bbdae17116 100644
--- a/core/modules/user/tests/src/Functional/UserAccountLinksTest.php
+++ b/core/modules/user/tests/src/Functional/UserAccountLinksTest.php
@@ -46,31 +46,15 @@ public function testSecondaryMenu() {
 
     // For a logged-in user, expect the secondary menu to have links for "My
     // account" and "Log out".
-    $link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
-      ':menu_class' => 'menu',
-      ':href' => 'user',
-      ':text' => 'My account',
-    ]);
-    $this->assertCount(1, $link, 'My account link is in secondary menu.');
-
-    $link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
-      ':menu_class' => 'menu',
-      ':href' => 'user/logout',
-      ':text' => 'Log out',
-    ]);
-    $this->assertCount(1, $link, 'Log out link is in secondary menu.');
+    $this->assertSession()->elementsCount('xpath', '//ul[@class="menu"]/li/a[contains(@href, "user") and text()="My account"]', 1);
+    $this->assertSession()->elementsCount('xpath', '//ul[@class="menu"]/li/a[contains(@href, "user/logout") and text()="Log out"]', 1);
 
     // Log out and get the homepage.
     $this->drupalLogout();
     $this->drupalGet('<front>');
 
     // For a logged-out user, expect the secondary menu to have a "Log in" link.
-    $link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
-      ':menu_class' => 'menu',
-      ':href' => 'user/login',
-      ':text' => 'Log in',
-    ]);
-    $this->assertCount(1, $link, 'Log in link is in secondary menu.');
+    $this->assertSession()->elementsCount('xpath', '//ul[@class="menu"]/li/a[contains(@href, "user/login") and text()="Log in"]', 1);
   }
 
   /**
@@ -85,12 +69,7 @@ public function testDisabledAccountLink() {
 
     // Verify that the 'My account' link exists before we check for its
     // disappearance.
-    $link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
-      ':menu_class' => 'menu',
-      ':href' => 'user',
-      ':text' => 'My account',
-    ]);
-    $this->assertCount(1, $link, 'My account link is in the secondary menu.');
+    $this->assertSession()->elementsCount('xpath', '//ul[@class="menu"]/li/a[contains(@href, "user") and text()="My account"]', 1);
 
     // Verify that the 'My account' link is enabled. Do not assume the value of
     // auto-increment is 1. Use XPath to obtain input element id and name using
@@ -108,12 +87,7 @@ public function testDisabledAccountLink() {
     $this->drupalGet('<front>');
 
     // Verify that the 'My account' link does not appear when disabled.
-    $link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
-      ':menu_class' => 'menu',
-      ':href' => 'user',
-      ':text' => 'My account',
-    ]);
-    $this->assertCount(0, $link, 'My account link is not in the secondary menu.');
+    $this->assertSession()->elementNotExists('xpath', '//ul[@class="menu"]/li/a[contains(@href, "user") and text()="My account"]');
   }
 
   /**
diff --git a/core/modules/user/tests/src/Functional/UserRoleAdminTest.php b/core/modules/user/tests/src/Functional/UserRoleAdminTest.php
index 263b3a576fdbd04fc9b4466186732d92dccd36fb..6ec8b156810a257ade436f7ef719f72819e43a40 100644
--- a/core/modules/user/tests/src/Functional/UserRoleAdminTest.php
+++ b/core/modules/user/tests/src/Functional/UserRoleAdminTest.php
@@ -52,11 +52,7 @@ public function testRoleAdministration() {
     $default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
     // Test presence of tab.
     $this->drupalGet('admin/people/permissions');
-    $tabs = $this->xpath('//ul[@class=:classes and //a[contains(., :text)]]', [
-      ':classes' => 'tabs primary',
-      ':text' => 'Roles',
-    ]);
-    $this->assertCount(1, $tabs, 'Found roles tab');
+    $this->assertSession()->elementsCount('xpath', '//ul[@class="tabs primary" and //a[contains(., "Roles")]]', 1);
 
     // Test adding a role. (In doing so, we use a role name that happens to
     // correspond to an integer, to test that the role administration pages
diff --git a/core/modules/views/tests/src/Functional/GlossaryTest.php b/core/modules/views/tests/src/Functional/GlossaryTest.php
index 1496c96bff5617c65630089597280ac89683a9c9..3b1b8966f5215c787e681e6396e17ca10ba2c0d4 100644
--- a/core/modules/views/tests/src/Functional/GlossaryTest.php
+++ b/core/modules/views/tests/src/Functional/GlossaryTest.php
@@ -116,11 +116,10 @@ public function testGlossaryView() {
       $label = mb_strtoupper($char);
       // Get the summary link for a certain character. Filter by label and href
       // to ensure that both of them are correct.
-      $result = $this->xpath('//a[contains(@href, :href) and normalize-space(text())=:label]/..', [':href' => $href, ':label' => $label]);
-      $this->assertNotEmpty(count($result));
+      $result = $this->assertSession()->elementExists('xpath', "//a[contains(@href, '{$href}') and normalize-space(text())='{$label}']/..");
       // The rendered output looks like "<a href=''>X</a> | (count)" so let's
       // figure out the int.
-      $result_count = explode(' ', trim(str_replace(['|', '(', ')'], '', $result[0]->getText())))[1];
+      $result_count = explode(' ', trim(str_replace(['|', '(', ')'], '', $result->getText())))[1];
       $this->assertEquals($count, $result_count, 'The expected number got rendered.');
     }
   }
diff --git a/core/modules/views/tests/src/Functional/Handler/FieldDropButtonTest.php b/core/modules/views/tests/src/Functional/Handler/FieldDropButtonTest.php
index ff2b9d689beadea04c66709c2c2601bee240386b..494048fd786cd8052c3327293e92c84740437d3d 100644
--- a/core/modules/views/tests/src/Functional/Handler/FieldDropButtonTest.php
+++ b/core/modules/views/tests/src/Functional/Handler/FieldDropButtonTest.php
@@ -57,10 +57,10 @@ public function testDropbutton() {
 
     $this->drupalGet('test-dropbutton');
     foreach ($nodes as $node) {
-      $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[contains(@href, :path) and text()=:title]', [':path' => '/node/' . $node->id(), ':title' => $node->label()]);
-      $this->assertCount(1, $result, 'Just one node title link was found.');
-      $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[contains(@href, :path) and text()=:title]', [':path' => '/node/' . $node->id(), ':title' => 'Custom Text']);
-      $this->assertCount(1, $result, 'Just one custom link was found.');
+      // Test that only one node title link was found.
+      $this->assertSession()->elementsCount('xpath', "//ul[contains(@class, dropbutton)]/li/a[contains(@href, '/node/{$node->id()}') and text()='{$node->label()}']", 1);
+      // Test that only one custom link was found.
+      $this->assertSession()->elementsCount('xpath', "//ul[contains(@class, dropbutton)]/li/a[contains(@href, '/node/{$node->id()}') and text()='Custom Text']", 1);
     }
 
     // Check if the dropbutton.js library is available.
diff --git a/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php b/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php
index b88ef63b1b26cb92c4ae316ef4b93fa0e97818f4..8741e81e4f818bd06ef8ceb8140e64fb4f8c5459 100644
--- a/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php
+++ b/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php
@@ -86,8 +86,7 @@ public function testEntityOperations() {
           // Update destination property of the URL as generating it in the
           // test would by default point to the frontpage.
           $operation['url']->setOption('query', ['destination' => $expected_destination]);
-          $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[@href=:path and text()=:title]', [':path' => $operation['url']->toString(), ':title' => (string) $operation['title']]);
-          $this->assertCount(1, $result, t('Found entity @operation link with destination parameter.', ['@operation' => $operation['title']]));
+          $this->assertSession()->elementsCount('xpath', "//ul[contains(@class, dropbutton)]/li/a[@href='{$operation['url']->toString()}' and text()='{$operation['title']}']", 1);
           // Entities which were created in Hungarian should link to the Hungarian
           // edit form, others to the English one (which has no path prefix here).
           $base_path = \Drupal::request()->getBasePath();
diff --git a/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php b/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php
index 240ec8f5faa64a7c4a5d574a233d79926a3447ba..520db9cadcf97e54cbadf85023e73a6a75932ee7 100644
--- a/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php
+++ b/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php
@@ -91,8 +91,7 @@ public function testClickSorting() {
     $ids = $this->clickSortLoadIdsFromOutput();
     $this->assertEquals(range(1, 5), $ids);
     // Check that the rel attribute has the correct value.
-    $result = $this->xpath('//a[@href="' . $href . '"]');
-    $this->assertEquals('nofollow', $result[0]->getAttribute('rel'));
+    $this->assertSession()->elementAttributeContains('xpath', "//a[@href='$href']", 'rel', 'nofollow');
 
     $this->clickLink('ID Sort descending');
     // Check that the output has the expected order (desc).
diff --git a/core/modules/views/tests/src/Functional/Plugin/DisplayPageWebTest.php b/core/modules/views/tests/src/Functional/Plugin/DisplayPageWebTest.php
index d8e86e5692f7342a6d5686b461561b0fd4ac3c74..38f371799b459b9c34e8e78604164b347d07b3bd 100644
--- a/core/modules/views/tests/src/Functional/Plugin/DisplayPageWebTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/DisplayPageWebTest.php
@@ -96,11 +96,7 @@ public function testPageDisplayMenu() {
     // Check local tasks.
     $this->drupalGet('test_page_display_menu');
     $this->assertSession()->statusCodeEquals(200);
-    $element = $this->xpath('//ul[contains(@class, :ul_class)]//a[contains(@class, :a_class)]/child::text()', [
-      ':ul_class' => 'tabs primary',
-      ':a_class' => 'is-active',
-    ]);
-    $this->assertEquals(t('Test default tab'), $element[0]->getText());
+    $this->assertSession()->elementTextEquals('xpath', "//ul[contains(@class, 'tabs primary')]//a[contains(@class, 'is-active')]/child::text()", 'Test default tab');
     $this->assertSession()->titleEquals('Test default page | Drupal');
 
     $this->drupalGet('test_page_display_menu/default');
@@ -108,11 +104,7 @@ public function testPageDisplayMenu() {
 
     $this->drupalGet('test_page_display_menu/local');
     $this->assertSession()->statusCodeEquals(200);
-    $element = $this->xpath('//ul[contains(@class, :ul_class)]//a[contains(@class, :a_class)]/child::text()', [
-      ':ul_class' => 'tabs primary',
-      ':a_class' => 'is-active',
-    ]);
-    $this->assertEquals(t('Test local tab'), $element[0]->getText());
+    $this->assertSession()->elementTextEquals('xpath', "//ul[contains(@class, 'tabs primary')]//a[contains(@class, 'is-active')]/child::text()", 'Test local tab');
     $this->assertSession()->titleEquals('Test local page | Drupal');
 
     // Check an ordinary menu link.
diff --git a/core/modules/views/tests/src/Functional/Plugin/StyleTableTest.php b/core/modules/views/tests/src/Functional/Plugin/StyleTableTest.php
index 0e3b30e6a2589097a85eb8d35d4b9a0234b553e9..fa452d2559913c1396c7933b007c7bf7b584fd1e 100644
--- a/core/modules/views/tests/src/Functional/Plugin/StyleTableTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/StyleTableTest.php
@@ -157,8 +157,8 @@ public function testEmptyColumn() {
     $this->drupalGet('test-table');
 
     // Test that only one of the job columns still shows.
-    $result = $this->xpath('//thead/tr/th/a[text()="Job"]');
-    $this->assertCount(1, $result, 'Ensure that empty column header is hidden.');
+    // Ensure that empty column header is hidden.
+    $this->assertSession()->elementsCount('xpath', '//thead/tr/th/a[text()="Job"]', 1);
 
     $result = $this->xpath('//tbody/tr/td[contains(concat(" ", @class, " "), " views-field-job-1 ")]');
     $this->assertCount(0, $result, 'Ensure the empty table cells are hidden.');
diff --git a/core/modules/views_ui/tests/src/Functional/DisplayAttachmentTest.php b/core/modules/views_ui/tests/src/Functional/DisplayAttachmentTest.php
index 758e8c064923020f4c1eacfe57611ec5f2aa1042..0ad459e2de78d37336edbc0becdcc8f729567410 100644
--- a/core/modules/views_ui/tests/src/Functional/DisplayAttachmentTest.php
+++ b/core/modules/views_ui/tests/src/Functional/DisplayAttachmentTest.php
@@ -46,8 +46,7 @@ public function testAttachmentUI() {
     // Options summary should be escaped.
     $this->assertSession()->assertEscaped('<em>Page</em>');
     $this->assertNoRaw('<em>Page</em>');
-    $result = $this->xpath('//a[@id = :id]', [':id' => 'views-attachment-1-displays']);
-    $this->assertEquals(t('Page'), $result[0]->getAttribute('title'));
+    $this->assertSession()->elementAttributeContains('xpath', '//a[@id = "views-attachment-1-displays"]', 'title', 'Page');
     $this->submitForm([], 'Save');
 
     $view = Views::getView('test_attachment_ui');
@@ -59,8 +58,7 @@ public function testAttachmentUI() {
       'displays[default]' => 1,
       'displays[page_1]' => 1,
     ], 'Apply');
-    $result = $this->xpath('//a[@id = :id]', [':id' => 'views-attachment-1-displays']);
-    $this->assertEquals(t('Multiple displays'), $result[0]->getAttribute('title'));
+    $this->assertSession()->elementAttributeContains('xpath', '//a[@id = "views-attachment-1-displays"]', 'title', 'Multiple displays');
     $this->submitForm([], 'Save');
 
     $view = Views::getView('test_attachment_ui');
diff --git a/core/modules/views_ui/tests/src/Functional/DisplayCRUDTest.php b/core/modules/views_ui/tests/src/Functional/DisplayCRUDTest.php
index 4a944e18f3dfc7546d7535478a7b20196c1ffb3d..6bdd1440503a2c7cdcd294e2fb301aea95f8ca98 100644
--- a/core/modules/views_ui/tests/src/Functional/DisplayCRUDTest.php
+++ b/core/modules/views_ui/tests/src/Functional/DisplayCRUDTest.php
@@ -73,11 +73,8 @@ public function testRemoveDisplay() {
     $this->drupalGet($path_prefix . '/page_1');
     $this->submitForm([], 'Delete Page');
     $this->assertSession()->buttonExists('edit-displays-settings-settings-content-tab-content-details-top-actions-undo-delete');
-    $element = $this->xpath('//a[contains(@href, :href) and contains(@class, :class)]', [':href' => $path_prefix . '/page_1', ':class' => 'views-display-deleted-link']);
-    $this->assertTrue(!empty($element), 'Make sure the display link is marked as to be deleted.');
-
-    $element = $this->xpath('//a[contains(@href, :href) and contains(@class, :class)]', [':href' => $path_prefix . '/page_1', ':class' => 'views-display-deleted-link']);
-    $this->assertTrue(!empty($element), 'Make sure the display link is marked as to be deleted.');
+    // Test that the display link is marked as to be deleted.
+    $this->assertSession()->elementExists('xpath', "//a[contains(@href, '{$path_prefix}/page_1') and contains(@class, 'views-display-deleted-link')]");
 
     // Undo the deleting of the display.
     $this->drupalGet($path_prefix . '/page_1');
diff --git a/core/modules/views_ui/tests/src/Functional/DisplayTest.php b/core/modules/views_ui/tests/src/Functional/DisplayTest.php
index a02269c3dacf93c83e13994a744e7b39c8a24d6b..e09bd698d847161edd8cd5e3a4bc9ac1214d498e 100644
--- a/core/modules/views_ui/tests/src/Functional/DisplayTest.php
+++ b/core/modules/views_ui/tests/src/Functional/DisplayTest.php
@@ -161,8 +161,7 @@ public function testLinkDisplay() {
 
     // Test the link text displays 'None' and not 'Block 1'
     $this->drupalGet($path);
-    $result = $this->xpath("//a[contains(@href, :path)]", [':path' => $link_display_path]);
-    $this->assertEquals(t('None'), $result[0]->getHtml(), 'Make sure that the link option summary shows "None" by default.');
+    $this->assertSession()->elementTextEquals('xpath', "//a[contains(@href, '{$link_display_path}')]", 'None');
 
     $this->drupalGet($link_display_path);
     $this->assertSession()->checkboxChecked('edit-link-display-0');
@@ -173,8 +172,8 @@ public function testLinkDisplay() {
     // The form redirects to the default display.
     $this->drupalGet($path);
 
-    $result = $this->xpath("//a[contains(@href, :path)]", [':path' => $link_display_path]);
-    $this->assertEquals('Page', $result[0]->getHtml(), 'Make sure that the link option summary shows the right linked display.');
+    // Test that the link option summary shows the right linked display.
+    $this->assertSession()->elementTextEquals('xpath', "//a[contains(@href, '{$link_display_path}')]", 'Page');
 
     $this->drupalGet($link_display_path);
     $this->submitForm([
diff --git a/core/modules/views_ui/tests/src/Functional/HandlerTest.php b/core/modules/views_ui/tests/src/Functional/HandlerTest.php
index 0c7fc4b936a5621d37f09b624308a6668133bbb8..e9b4458b2be6e5398d46a16b458656166ee58f63 100644
--- a/core/modules/views_ui/tests/src/Functional/HandlerTest.php
+++ b/core/modules/views_ui/tests/src/Functional/HandlerTest.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\Tests\views_ui\Functional;
 
-use Drupal\Component\Render\FormattableMarkup;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\views\Tests\ViewTestData;
@@ -134,10 +133,9 @@ public function testUICRUD() {
 
       // Verify that the user got redirected to the views edit form.
       $this->assertSession()->addressEquals('admin/structure/views/view/test_view_empty/edit/default');
-
       $this->assertSession()->linkByHrefExists($edit_handler_url, 0, 'The handler edit link appears in the UI.');
-      $links = $this->xpath('//a[starts-with(normalize-space(text()), :label)]', [':label' => $random_label]);
-      $this->assertTrue(isset($links[0]), 'The handler edit link has the right label');
+      // Test that the  handler edit link has the right label.
+      $this->assertSession()->elementExists('xpath', "//a[starts-with(normalize-space(text()), '{$random_label}')]");
 
       // Save the view and have a look whether the handler was added as expected.
       $this->submitForm([], 'Save');
@@ -228,17 +226,14 @@ public function testBrokenHandlers() {
       $this->drupalGet('admin/structure/views/view/test_view_broken/edit');
 
       $href = "admin/structure/views/nojs/handler/test_view_broken/default/$type/id_broken";
-
-      $result = $this->xpath('//a[contains(@href, :href)]', [':href' => $href]);
-      $this->assertCount(1, $result, new FormattableMarkup('Handler (%type) edit link found.', ['%type' => $type]));
-
       $text = 'Broken/missing handler';
 
-      $this->assertSame($text, $result[0]->getText(), 'Ensure the broken handler text was found.');
+      // Test that the handler edit link is present.
+      $this->assertSession()->elementsCount('xpath', "//a[contains(@href, '{$href}')]", 1);
+      $result = $this->assertSession()->elementTextEquals('xpath', "//a[contains(@href, '{$href}')]", $text);
 
       $this->drupalGet($href);
-      $result = $this->xpath('//h1[@class="page-title"]');
-      $this->assertStringContainsString($text, $result[0]->getText(), 'Ensure the broken handler text was found.');
+      $this->assertSession()->elementTextContains('xpath', '//h1[@class="page-title"]', $text);
 
       $original_configuration = [
         'field' => 'id_broken',
diff --git a/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php b/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
index 14b25404bf43e8287dea2311ab3d090fd279c384..43eab572b48baf4c9d02bab10fb60c0eaff16d8e 100644
--- a/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
+++ b/core/modules/views_ui/tests/src/FunctionalJavascript/PreviewTest.php
@@ -155,8 +155,8 @@ public function testPreviewWithPagersUI() {
     $this->assertNotEmpty($elements[4]->find('css', 'a'), 'Link to last page found.');
 
     // Navigate to next page.
-    $elements = $this->xpath('//li[contains(@class, :class)]/a', [':class' => 'pager__item--next']);
-    $this->clickPreviewLinkAJAX($elements[0], 5);
+    $element = $this->assertSession()->elementExists('xpath', '//li[contains(@class, "pager__item--next")]/a');
+    $this->clickPreviewLinkAJAX($element, 5);
 
     // Test that the pager is present and rendered.
     $elements = $this->xpath('//ul[contains(@class, :class)]/li', [':class' => 'pager__items']);
@@ -203,8 +203,8 @@ public function testPreviewWithPagersUI() {
     $this->assertNotEmpty($elements[1]->find('css', 'a'), 'Link to next page found.');
 
     // Navigate to next page.
-    $elements = $this->xpath('//li[contains(@class, :class)]/a', [':class' => 'pager__item--next']);
-    $this->clickPreviewLinkAJAX($elements[0], 3);
+    $element = $this->assertSession()->elementExists('xpath', '//li[contains(@class, "pager__item--next")]/a');
+    $this->clickPreviewLinkAJAX($element, 3);
 
     // Test that the pager is present and rendered.
     $elements = $this->xpath('//ul[contains(@class, :class)]/li', [':class' => 'pager__items']);
@@ -231,14 +231,13 @@ public function testPreviewSortLink() {
     $this->getPreviewAJAX('test_click_sort_ajax', 'page_1', 0);
 
     // Test that the header label is present.
-    $elements = $this->xpath('//th[contains(@class, :class)]/a', [':class' => 'views-field views-field-name']);
-    $this->assertTrue(!empty($elements), 'The header label is present.');
+    $element = $this->assertSession()->elementExists('xpath', '//th[contains(@class, "views-field views-field-name")]/a');
 
     // Verify link.
     $this->assertSession()->linkByHrefExists('preview/page_1?_wrapper_format=drupal_ajax&order=name&sort=desc', 0, 'The output URL is as expected.');
 
     // Click link to sort.
-    $elements[0]->click();
+    $element->click();
     $sort_link = $this->assertSession()->waitForElement('xpath', '//th[contains(@class, \'views-field views-field-name is-active\')]/a');
 
     $this->assertNotEmpty($sort_link);
diff --git a/core/tests/Drupal/Tests/WebAssert.php b/core/tests/Drupal/Tests/WebAssert.php
index 1c83ce366ff6b233212d2919d8837197dfda04e7..a5d80a6e3c0e38946f034b1329a38871e38f69a9 100644
--- a/core/tests/Drupal/Tests/WebAssert.php
+++ b/core/tests/Drupal/Tests/WebAssert.php
@@ -989,7 +989,8 @@ public function elementNotExists($selectorType, $selector, ElementInterface $con
    *   Expected text.
    */
   public function elementTextEquals(string $selectorType, $selector, string $text): void {
-    $message = "Failed asserting that the text of the element identified by '$selector' equals '$text'.";
+    $selector_string = is_array($selector) ? '[' . implode(', ', $selector) . ']' : $selector;
+    $message = "Failed asserting that the text of the element identified by '$selector_string' equals '$text'.";
     $constraint = new IsEqual($text);
     Assert::assertThat($this->elementExists($selectorType, $selector)->getText(), $constraint, $message);
   }