diff --git a/core/modules/action/tests/src/Functional/ConfigurationTest.php b/core/modules/action/tests/src/Functional/ConfigurationTest.php
index ab91d5fb978a7bf37a0cdac22173524875bd8d98..99ce8f972700655d450681f32d5ad7aefaab0894 100644
--- a/core/modules/action/tests/src/Functional/ConfigurationTest.php
+++ b/core/modules/action/tests/src/Functional/ConfigurationTest.php
@@ -74,9 +74,9 @@ public function testActionConfiguration() {
     // the complex action.
     $this->assertText($new_action_label);
 
+    // Make sure the URL appears when re-editing the action.
     $this->clickLink(t('Configure'));
-    $element = $this->xpath('//input[@type="text" and @value="admin"]');
-    $this->assertTrue(!empty($element), 'Make sure the URL appears when re-editing the action.');
+    $this->assertSession()->fieldValueEquals('url', 'admin');
 
     // Make sure that deletions work properly.
     $this->drupalGet('admin/config/system/actions');
diff --git a/core/modules/block/tests/src/Functional/Views/DisplayBlockTest.php b/core/modules/block/tests/src/Functional/Views/DisplayBlockTest.php
index 8eb96447d25e455dc70a1cac729c26420e12b7e1..a52119724cd9902580ea70be1a6d3375d08ba263 100644
--- a/core/modules/block/tests/src/Functional/Views/DisplayBlockTest.php
+++ b/core/modules/block/tests/src/Functional/Views/DisplayBlockTest.php
@@ -201,8 +201,7 @@ public function testViewsBlockForm() {
     $this->drupalLogin($this->drupalCreateUser(['administer blocks']));
     $default_theme = $this->config('system.theme')->get('default');
     $this->drupalGet('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme);
-    $elements = $this->xpath('//input[@name="label"]');
-    $this->assertTrue(empty($elements), 'The label field is not found for Views blocks.');
+    $this->assertSession()->fieldNotExists('label');
     // Test that the machine name field is hidden from display and has been
     // saved as expected from the default value.
     $this->assertSession()->fieldNotExists('edit-machine-name', NULL);
diff --git a/core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php b/core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php
index 4e6d77e5c68c43d7ae06e84e15c66aa563849883..c5a3045533b9d3d97dae9d545f705355e3da696e 100644
--- a/core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php
+++ b/core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php
@@ -98,8 +98,8 @@ public function testLoading() {
     $this->assertFalse($editor_js_present, 'No Text Editor JavaScript.');
     $this->assertCount(1, $body, 'A body field exists.');
     $this->assertSession()->elementNotExists('css', 'select.js-filter-list');
-    $hidden_input = $this->xpath('//input[@type="hidden" and contains(@class, "editor")]');
-    $this->assertCount(0, $hidden_input, 'A single text format hidden input does not exist on the page.');
+    // Verify that a single text format hidden input does not exist on the page.
+    $this->assertSession()->elementNotExists('xpath', '//input[@type="hidden" and contains(@class, "editor")]');
     // Verify that CKEditor glue JS is absent.
     $this->assertNoRaw(drupal_get_path('module', 'ckeditor') . '/js/ckeditor.js');
 
diff --git a/core/modules/comment/tests/src/Functional/CommentPreviewTest.php b/core/modules/comment/tests/src/Functional/CommentPreviewTest.php
index a36dc17a28b150cfd60a64b8e839727be456bb77..d8f2f9267a6f8dda9c4279b39be887237398f45e 100644
--- a/core/modules/comment/tests/src/Functional/CommentPreviewTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentPreviewTest.php
@@ -182,11 +182,11 @@ public function testCommentEditPreviewSave() {
 
     // Submit the form using the displayed values.
     $displayed = [];
-    $displayed['subject[0][value]'] = current($this->xpath("//input[@id='edit-subject-0-value']"))->getValue();
+    $displayed['subject[0][value]'] = $this->assertSession()->fieldExists('edit-subject-0-value')->getValue();
     $displayed['comment_body[0][value]'] = current($this->xpath("//textarea[@id='edit-comment-body-0-value']"))->getValue();
-    $displayed['uid'] = current($this->xpath("//input[@id='edit-uid']"))->getValue();
-    $displayed['date[date]'] = current($this->xpath("//input[@id='edit-date-date']"))->getValue();
-    $displayed['date[time]'] = current($this->xpath("//input[@id='edit-date-time']"))->getValue();
+    $displayed['uid'] = $this->assertSession()->fieldExists('edit-uid')->getValue();
+    $displayed['date[date]'] = $this->assertSession()->fieldExists('edit-date-date')->getValue();
+    $displayed['date[time]'] = $this->assertSession()->fieldExists('edit-date-time')->getValue();
     $this->drupalPostForm('comment/' . $comment->id() . '/edit', $displayed, 'Save');
 
     // Check that the saved comment is still correct.
diff --git a/core/modules/config/tests/src/Functional/ConfigEntityFormOverrideTest.php b/core/modules/config/tests/src/Functional/ConfigEntityFormOverrideTest.php
index a405a760006a45257af46d2c8caa0a0aa8861a07..a5e8c215e9d9780c49b0b215d6cbfd11aefb9c45 100644
--- a/core/modules/config/tests/src/Functional/ConfigEntityFormOverrideTest.php
+++ b/core/modules/config/tests/src/Functional/ConfigEntityFormOverrideTest.php
@@ -52,8 +52,7 @@ public function testFormsWithOverrides() {
 
     // Test that the original label on the editing page is intact.
     $this->drupalGet('admin/structure/config_test/manage/dotted.default');
-    $elements = $this->xpath('//input[@name="label"]');
-    $this->assertSame($original_label, $elements[0]->getValue());
+    $this->assertSession()->fieldValueEquals('label', $original_label);
     $this->assertNoText($overridden_label);
 
     // Change to a new label and test that the listing now has the edited label.
@@ -67,8 +66,7 @@ public function testFormsWithOverrides() {
 
     // Test that the editing page now has the edited label.
     $this->drupalGet('admin/structure/config_test/manage/dotted.default');
-    $elements = $this->xpath('//input[@name="label"]');
-    $this->assertSame($edited_label, $elements[0]->getValue());
+    $this->assertSession()->fieldValueEquals('label', $edited_label);
 
     // Test that the overridden label is still loaded with the entity.
     $this->assertEquals($overridden_label, $config_test_storage->load('dotted.default')->label());
diff --git a/core/modules/config/tests/src/Functional/ConfigFormOverrideTest.php b/core/modules/config/tests/src/Functional/ConfigFormOverrideTest.php
index 64fb0a03ccc83be962ef21fdc907331ca25ebf1a..b52d675992771c3bfa59718f02c31141d9682d4f 100644
--- a/core/modules/config/tests/src/Functional/ConfigFormOverrideTest.php
+++ b/core/modules/config/tests/src/Functional/ConfigFormOverrideTest.php
@@ -39,8 +39,7 @@ public function testFormsWithOverrides() {
     // worked for the actual site name.
     $this->drupalGet('admin/config/system/site-information');
     $this->assertSession()->titleEquals('Basic site settings | ' . $overridden_name);
-    $elements = $this->xpath('//input[@name="site_name"]');
-    $this->assertSame('Drupal', $elements[0]->getValue());
+    $this->assertSession()->fieldValueEquals("site_name", 'Drupal');
 
     // Submit the form and ensure the site name is not changed.
     $edit = [
@@ -48,8 +47,7 @@ public function testFormsWithOverrides() {
     ];
     $this->drupalPostForm('admin/config/system/site-information', $edit, 'Save configuration');
     $this->assertSession()->titleEquals('Basic site settings | ' . $overridden_name);
-    $elements = $this->xpath('//input[@name="site_name"]');
-    $this->assertSame($edit['site_name'], $elements[0]->getValue());
+    $this->assertSession()->fieldValueEquals("site_name", $edit['site_name']);
   }
 
 }
diff --git a/core/modules/contact/tests/src/Functional/ContactSitewideTest.php b/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
index 9621f16a38350e831320d25dd1fa5c1ccdcf7583..e2cf7867132eb810fe550f135ed60893677aeb20 100644
--- a/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
+++ b/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
@@ -66,10 +66,10 @@ public function testSiteWideContact() {
     $this->drupalGet('contact');
 
     // Ensure that there is no textfield for name.
-    $this->assertEmpty($this->xpath('//input[@name=:name]', [':name' => 'name']));
+    $this->assertSession()->fieldNotExists('name');
 
     // Ensure that there is no textfield for email.
-    $this->assertEmpty($this->xpath('//input[@name=:name]', [':name' => 'mail']));
+    $this->assertSession()->fieldNotExists('mail');
 
     // Logout and retrieve the page as an anonymous user
     $this->drupalLogout();
@@ -77,10 +77,10 @@ public function testSiteWideContact() {
     $this->drupalGet('contact');
 
     // Ensure that there is textfield for name.
-    $this->assertNotEmpty($this->xpath('//input[@name=:name]', [':name' => 'name']));
+    $this->assertSession()->fieldExists('name');
 
     // Ensure that there is textfield for email.
-    $this->assertNotEmpty($this->xpath('//input[@name=:name]', [':name' => 'mail']));
+    $this->assertSession()->fieldExists('mail');
 
     // Create and log in administrative user.
     $admin_user = $this->drupalCreateUser([
diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationUntranslatableFieldsTest.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationUntranslatableFieldsTest.php
index 4c06baf8bdc0d8c5e58535aa3895eb43e8e06530..7f0d6c4ef333ad26e741556b2a427d939a5f656d 100644
--- a/core/modules/content_translation/tests/src/Functional/ContentTranslationUntranslatableFieldsTest.php
+++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationUntranslatableFieldsTest.php
@@ -171,12 +171,12 @@ public function testHiddenWidgets() {
     // Verify that checkboxes on the language content settings page are checked
     // and disabled for moderated bundles.
     $this->drupalGet($settings_url);
-    $input_xpath = '//input[@name="settings[' . $this->entityTypeId . '][' . $this->bundle . '][settings][content_translation][untranslatable_fields_hide]" and @value=1 and @disabled="disabled"]';
-    $elements = $this->xpath($input_xpath);
-    $this->assertNotEmpty($elements);
+    $field_name = "settings[{$this->entityTypeId}][{$this->bundle}][settings][content_translation][untranslatable_fields_hide]";
+    $this->assertSession()->fieldValueEquals($field_name, 1);
+    $this->assertSession()->fieldDisabled($field_name);
     $this->submitForm([$settings_key => 0], 'Save configuration');
-    $elements = $this->xpath($input_xpath);
-    $this->assertNotEmpty($elements);
+    $this->assertSession()->fieldValueEquals($field_name, 1);
+    $this->assertSession()->fieldDisabled($field_name);
   }
 
 }
diff --git a/core/modules/editor/tests/src/Functional/EditorLoadingTest.php b/core/modules/editor/tests/src/Functional/EditorLoadingTest.php
index 9fcb82b268860fd6848cc73cecd8af6e0d7f8764..cf43620d9b9a8929235da66a3f5d38f8a93e0a23 100644
--- a/core/modules/editor/tests/src/Functional/EditorLoadingTest.php
+++ b/core/modules/editor/tests/src/Functional/EditorLoadingTest.php
@@ -214,8 +214,11 @@ public function testLoading() {
     $this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
     $this->assertCount(1, $body, 'A body field exists.');
     $this->assertSession()->elementNotExists('css', 'select.js-filter-list');
-    $hidden_input = $this->xpath('//input[@type="hidden" and @value="plain_text" and @data-editor-for="edit-body-0-value"]');
-    $this->assertCount(1, $hidden_input, 'A single text format hidden input exists on the page and has a "data-editor-for" attribute with the correct value.');
+    // Verify that a single text format hidden input exists on the page and has
+    // a "data-editor-for" attribute with the correct value.
+    $hidden_input = $this->assertSession()->hiddenFieldExists('body[0][format]');
+    $this->assertSame('plain_text', $hidden_input->getValue());
+    $this->assertSame('edit-body-0-value', $hidden_input->getAttribute('data-editor-for'));
 
     // Create an "article" node that uses the full_html text format, then try
     // to let the untrusted user edit it.
@@ -237,8 +240,8 @@ public function testLoading() {
     $this->assertSession()->fieldDisabled("edit-body-0-value");
     $this->assertSession()->fieldValueEquals("edit-body-0-value", 'This field has been disabled because you do not have sufficient permissions to edit it.');
     $this->assertSession()->elementNotExists('css', 'select.js-filter-list');
-    $hidden_input = $this->xpath('//input[@type="hidden" and contains(@class, "editor")]');
-    $this->assertCount(0, $hidden_input, 'A single text format hidden input does not exist on the page.');
+    // Verify that no single text format hidden input exists on the page.
+    $this->assertSession()->elementNotExists('xpath', '//input[@type="hidden" and contains(@class, "editor")]');
   }
 
   /**
diff --git a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
index 4e23dd8d878c9faf7899258cfe056e0795db04b4..b599f2138128f3f6403f11579bd0cf241b63659a 100644
--- a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
+++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
@@ -148,8 +148,9 @@ public function testFieldAdminHandler() {
 
     // Try to add a new node and fill the entity reference field.
     $this->drupalGet('node/add/' . $this->type);
-    $result = $this->xpath('//input[@name="field_test_entity_ref_field[0][target_id]" and contains(@data-autocomplete-path, "/entity_reference_autocomplete/node/views/")]');
-    $target_url = $this->getAbsoluteUrl($result[0]->getAttribute('data-autocomplete-path'));
+    $field = $this->assertSession()->fieldExists('field_test_entity_ref_field[0][target_id]');
+    $this->assertStringContainsString("/entity_reference_autocomplete/node/views/", $field->getAttribute('data-autocomplete-path'));
+    $target_url = $this->getAbsoluteUrl($field->getAttribute('data-autocomplete-path'));
     $this->drupalGet($target_url, ['query' => ['q' => 'Foo']]);
     $this->assertRaw($node1->getTitle() . ' (' . $node1->id() . ')');
     $this->assertRaw($node2->getTitle() . ' (' . $node2->id() . ')');
diff --git a/core/modules/field/tests/src/Functional/NestedFormTest.php b/core/modules/field/tests/src/Functional/NestedFormTest.php
index 129b98f6b048fd71d0e2bb36f9746796b0da3a44..a9467dc831d21c8ff4f00090b912d74281a82270 100644
--- a/core/modules/field/tests/src/Functional/NestedFormTest.php
+++ b/core/modules/field/tests/src/Functional/NestedFormTest.php
@@ -129,15 +129,17 @@ public function testNestedFieldForm() {
     ];
     $this->drupalPostForm('test-entity/nested/1/2', $edit, 'Save');
     $this->assertRaw(t('%label does not accept the value -1', ['%label' => 'Unlimited field']));
-    $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', [':id' => 'edit-field-unlimited-1-value']);
-    $this->assertCount(1, $error_field, 'Entity 1: the error was flagged on the correct element.');
+    // Entity 1: check that the error was flagged on the correct element.
+    $error_field = $this->assertSession()->fieldExists('edit-field-unlimited-1-value');
+    $this->assertTrue($error_field->hasClass('error'));
     $edit = [
       'entity_2[field_unlimited][1][value]' => -1,
     ];
     $this->drupalPostForm('test-entity/nested/1/2', $edit, 'Save');
     $this->assertRaw(t('%label does not accept the value -1', ['%label' => 'Unlimited field']));
-    $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', [':id' => 'edit-entity-2-field-unlimited-1-value']);
-    $this->assertCount(1, $error_field, 'Entity 2: the error was flagged on the correct element.');
+    // Entity 2: check that the error was flagged on the correct element.
+    $error_field = $this->assertSession()->fieldExists('edit-entity-2-field-unlimited-1-value');
+    $this->assertTrue($error_field->hasClass('error'));
 
     // Test that reordering works on both entities.
     $edit = [
diff --git a/core/modules/file/tests/src/Functional/FileFieldWidgetTest.php b/core/modules/file/tests/src/Functional/FileFieldWidgetTest.php
index 2f268e0bd746f979034b4f7130125d229bd3578b..4a49cf6563e5aea067de307ed324581c178877ca 100644
--- a/core/modules/file/tests/src/Functional/FileFieldWidgetTest.php
+++ b/core/modules/file/tests/src/Functional/FileFieldWidgetTest.php
@@ -105,8 +105,8 @@ public function testSingleValuedWidget() {
     $this->assertSession()->buttonNotExists('Remove');
     $this->assertSession()->buttonExists('Upload');
     // Test label has correct 'for' attribute.
-    $input = $this->xpath('//input[@name="files[' . $field_name . '_0]"]');
-    $label = $this->xpath('//label[@for="' . $input[0]->getAttribute('id') . '"]');
+    $input = $this->assertSession()->fieldExists("files[{$field_name}_0]");
+    $label = $this->xpath('//label[@for="' . $input->getAttribute('id') . '"]');
     $this->assertTrue(isset($label[0]), 'Label for upload found.');
 
     // Save the node and ensure it does not have the file.
@@ -186,13 +186,12 @@ public function testMultiValuedWidget() {
         // Ensure an "Upload" button for the current field is displayed with the
         // correct name.
         $upload_button_name = $current_field_name . '_' . $remaining . '_upload_button';
-        $buttons = $this->xpath('//input[@type="submit" and @value="Upload" and @name=:name]', [':name' => $upload_button_name]);
-        $this->assertCount(1, $buttons, 'The upload button is displayed with the correct name.');
+        $button = $this->assertSession()->buttonExists($upload_button_name);
+        $this->assertSame('Upload', $button->getValue());
 
         // Ensure only at most one button per field is displayed.
-        $buttons = $this->xpath('//input[@type="submit" and @value="Upload"]');
         $expected = $current_field_name == $field_name ? 1 : 2;
-        $this->assertCount($expected, $buttons, 'After removing a file, only one "Upload" button for each possible field is displayed.');
+        $this->assertSession()->elementsCount('xpath', '//input[@type="submit" and @value="Upload"]', $expected);
       }
     }
 
diff --git a/core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php b/core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php
index 42786da28ddc3c7c405dc708d8ba7922ec58eb5f..a5fb67d265e06fd38962d497b70771dc7530fffb 100644
--- a/core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php
+++ b/core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php
@@ -122,13 +122,13 @@ public function testMultiValuedWidget() {
         // correct name.
         $upload_button_name = $current_field_name . '_' . $remaining . '_upload_button';
         $this->assertNotNull($assert_session->waitForButton($upload_button_name));
-        $buttons = $this->xpath('//input[@type="submit" and @value="Upload" and @name=:name]', [':name' => $upload_button_name]);
-        $this->assertCount(1, $buttons, 'The upload button is displayed with the correct name.');
+        $button = $this->assertSession()->buttonExists($upload_button_name);
+        $this->assertSame('Upload', $button->getValue());
 
-        // Ensure only at most one button per field is displayed.
-        $buttons = $this->xpath('//input[@type="submit" and @value="Upload"]');
+        // Verify that after removing a file, only one "Upload" button for each
+        // possible field is displayed.
         $expected = $current_field_name == $field_name ? 1 : 2;
-        $this->assertCount($expected, $buttons, 'After removing a file, only one "Upload" button for each possible field is displayed.');
+        $this->assertSession()->elementsCount('xpath', '//input[@type="submit" and @value="Upload"]', $expected);
       }
     }
   }
diff --git a/core/modules/image/tests/src/Functional/ImageFieldWidgetTest.php b/core/modules/image/tests/src/Functional/ImageFieldWidgetTest.php
index 451beee9a6ac050b6531847b7d2133543f216084..3391209ffe3266a56294c08cf620c9679d1bd37b 100644
--- a/core/modules/image/tests/src/Functional/ImageFieldWidgetTest.php
+++ b/core/modules/image/tests/src/Functional/ImageFieldWidgetTest.php
@@ -32,7 +32,8 @@ public function testWidgetElement() {
     $this->createImageField($field_name, 'article', [], $field_settings, [], [], 'Image test on [site:name]');
     $this->drupalGet('node/add/article');
     $this->assertNotCount(0, $this->xpath('//div[contains(@class, "field--widget-image-image")]'), 'Image field widget found on add/node page', NULL);
-    $this->assertNotCount(0, $this->xpath('//input[contains(@accept, "image/*")]'), 'Image field widget limits accepted files.', NULL);
+    // Verify that the image field widget limits accepted files.
+    $this->assertSession()->elementExists('xpath', '//input[contains(@accept, "image/*")]');
     $this->assertNoText('Image test on [site:name]');
 
     // Check for allowed image file extensions - default.
diff --git a/core/modules/image/tests/src/FunctionalJavascript/ImageFieldWidgetMultipleTest.php b/core/modules/image/tests/src/FunctionalJavascript/ImageFieldWidgetMultipleTest.php
index 0638c03b5af687dd95453fc9b709e443ebfb3f66..60a647b75e360fc4f0910369db28d5e0c64146cc 100644
--- a/core/modules/image/tests/src/FunctionalJavascript/ImageFieldWidgetMultipleTest.php
+++ b/core/modules/image/tests/src/FunctionalJavascript/ImageFieldWidgetMultipleTest.php
@@ -43,7 +43,7 @@ public function testWidgetElementMultipleUploads(): void {
     $this->createImageField($field_name, 'article', $storage_settings, $field_settings);
     $this->drupalLogin($this->drupalCreateUser(['access content', 'create article content']));
     $this->drupalGet('node/add/article');
-    $this->xpath('//input[@name="title[0][value]"]')[0]->setValue('Test');
+    $this->assertSession()->fieldExists('title[0][value]')->setValue('Test');
 
     $images = $this->getTestFiles('image');
     $images = array_slice($images, 0, 5);
@@ -58,7 +58,7 @@ public function testWidgetElementMultipleUploads(): void {
       $remote_paths[] = $web_driver->uploadFileAndGetRemoteFilePath($path);
     }
 
-    $multiple_field = $this->xpath('//input[@multiple]')[0];
+    $multiple_field = $this->assertSession()->elementExists('xpath', '//input[@multiple]');
     $multiple_field->setValue(implode("\n", $remote_paths));
     $this->assertSession()->waitForElementVisible('css', '[data-drupal-selector="edit-images-4-preview"]');
     $this->getSession()->getPage()->findButton('Save')->click();
diff --git a/core/modules/node/tests/src/Functional/NodeCreationTest.php b/core/modules/node/tests/src/Functional/NodeCreationTest.php
index 765324e4c6e8497307bd62af0726b84ccce432d0..b041abf2fdb0b31ef8c0cd627e93d8cf8a3751bd 100644
--- a/core/modules/node/tests/src/Functional/NodeCreationTest.php
+++ b/core/modules/node/tests/src/Functional/NodeCreationTest.php
@@ -248,8 +248,8 @@ public function testAuthorAutocomplete() {
 
     $this->drupalGet('node/add/page');
 
-    $result = $this->xpath('//input[@id="edit-uid-0-value" and contains(@data-autocomplete-path, "user/autocomplete")]');
-    $this->assertCount(0, $result, 'No autocompletion without access user profiles.');
+    // Verify that no autocompletion exists without access user profiles.
+    $this->assertSession()->elementNotExists('xpath', '//input[@id="edit-uid-0-value" and contains(@data-autocomplete-path, "user/autocomplete")]');
 
     $admin_user = $this->drupalCreateUser([
       'administer nodes',
@@ -260,8 +260,8 @@ public function testAuthorAutocomplete() {
 
     $this->drupalGet('node/add/page');
 
-    $result = $this->xpath('//input[@id="edit-uid-0-target-id" and contains(@data-autocomplete-path, "/entity_reference_autocomplete/user/default")]');
-    $this->assertCount(1, $result, 'Ensure that the user does have access to the autocompletion');
+    // Ensure that the user does have access to the autocompletion.
+    $this->assertSession()->elementsCount('xpath', '//input[@id="edit-uid-0-target-id" and contains(@data-autocomplete-path, "/entity_reference_autocomplete/user/default")]', 1);
   }
 
   /**
diff --git a/core/modules/search/tests/src/Functional/SearchAdvancedSearchFormTest.php b/core/modules/search/tests/src/Functional/SearchAdvancedSearchFormTest.php
index 50165e7922fc0b04b7657b0357fc980e9fe96464..b17664e986365e39d8becc1e2f5b62ef7daeb73f 100644
--- a/core/modules/search/tests/src/Functional/SearchAdvancedSearchFormTest.php
+++ b/core/modules/search/tests/src/Functional/SearchAdvancedSearchFormTest.php
@@ -116,8 +116,7 @@ public function testFormRefill() {
     $this->assertText('Search for cat dog OR gerbil -fish -snake');
     foreach ($edit as $key => $value) {
       if ($key != 'type[page]') {
-        $elements = $this->xpath('//input[@name=:name]', [':name' => $key]);
-        $this->assertFalse(isset($elements[0]) && $elements[0]->getValue() == $value, "Field $key is not set to $value");
+        $this->assertSession()->fieldValueNotEquals($key, $value);
       }
     }
   }
diff --git a/core/modules/search/tests/src/Functional/SearchPageTextTest.php b/core/modules/search/tests/src/Functional/SearchPageTextTest.php
index 71cc8038acd151d3e030c0d94d18281365492cef..b6ecdde9418261518227b05d753139ef3574c308 100644
--- a/core/modules/search/tests/src/Functional/SearchPageTextTest.php
+++ b/core/modules/search/tests/src/Functional/SearchPageTextTest.php
@@ -117,8 +117,7 @@ public function testSearchText() {
     // from the GET params and displayed in the search form.
     $arg = $this->randomMachineName() . '/' . $this->randomMachineName();
     $this->drupalGet('search/node', ['query' => ['keys' => $arg]]);
-    $input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']");
-    $this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.');
+    $this->assertSession()->elementExists('xpath', "//input[@id='edit-keys' and @value='{$arg}']");
 
     // Test a search input exceeding the limit of AND/OR combinations to test
     // the Denial-of-Service protection.
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityFormTest.php b/core/modules/system/tests/src/Functional/Entity/EntityFormTest.php
index 84edf2e3507eeb0370b3fbb350d5849773a31f3b..c9399c5e6ef7fa0d1788d5c92dbda2bdbffc3aca 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityFormTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityFormTest.php
@@ -96,12 +96,14 @@ public function testEntityFormModeAlter() {
   /**
    * Tests hook_entity_form_display_alter().
    *
+   * Verify that the altered field has the correct size value.
+   *
    * @see entity_test_entity_form_display_alter()
    */
   public function testEntityFormDisplayAlter() {
     $this->drupalGet('entity_test/add');
-    $altered_field = $this->xpath('//input[@name="field_test_text[0][value]" and @size="42"]');
-    $this->assertCount(1, $altered_field, 'The altered field has the correct size value.');
+    $altered_field = $this->assertSession()->fieldExists('field_test_text[0][value]');
+    $this->assertEquals(42, $altered_field->getAttribute('size'));
   }
 
   /**
diff --git a/core/modules/system/tests/src/Functional/Form/ElementTest.php b/core/modules/system/tests/src/Functional/Form/ElementTest.php
index 3f9f90a4940f3db5feb6c6d3d8e5f82b559d0a99..0665c437ebf4206507e7a0ac07784ccbe9abe0d9 100644
--- a/core/modules/system/tests/src/Functional/Form/ElementTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ElementTest.php
@@ -32,11 +32,8 @@ public function testPlaceHolderText() {
     $expected = 'placeholder-text';
     // Test to make sure non-textarea elements have the proper placeholder text.
     foreach (['textfield', 'tel', 'url', 'password', 'email', 'number'] as $type) {
-      $element = $this->xpath('//input[@id=:id and @placeholder=:expected]', [
-        ':id' => 'edit-' . $type,
-        ':expected' => $expected,
-      ]);
-      $this->assertTrue(!empty($element), new FormattableMarkup('Placeholder text placed in @type.', ['@type' => $type]));
+      $field = $this->assertSession()->fieldExists("edit-$type");
+      $this->assertSame($expected, $field->getAttribute('placeholder'));
     }
 
     // Test to make sure textarea has the proper placeholder text.
@@ -84,13 +81,7 @@ public function testOptions() {
     }
     // Verify that custom #description properties are output.
     foreach (['checkboxes', 'radios'] as $type) {
-      $elements = $this->xpath('//input[@id=:id]/following-sibling::div[@class=:class]', [
-        ':id' => 'edit-' . $type . '-foo',
-        ':class' => 'description',
-      ]);
-      $this->assertGreaterThan(0, count($elements), new FormattableMarkup('Custom %type option description found.', [
-        '%type' => $type,
-      ]));
+      $this->assertSession()->elementExists('xpath', "//input[@id='edit-$type-foo']/following-sibling::div[@class='description']");
     }
   }
 
@@ -100,38 +91,27 @@ public function testOptions() {
   public function testRadiosChecked() {
     // Verify that there is only one radio option checked.
     $this->drupalGet('form-test/radios-checked');
-    $elements = $this->xpath('//input[@name="radios" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('0', $elements[0]->getValue());
-    $elements = $this->xpath('//input[@name="radios-string" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('bar', $elements[0]->getValue());
-    $elements = $this->xpath('//input[@name="radios-boolean-true" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('1', $elements[0]->getValue());
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios", '0');
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios-string" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios-string", 'bar');
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios-boolean-true" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios-boolean-true", '1');
     // A default value of FALSE indicates that nothing is set.
-    $elements = $this->xpath('//input[@name="radios-boolean-false" and @checked]');
-    $this->assertCount(0, $elements);
-    $elements = $this->xpath('//input[@name="radios-boolean-any" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('All', $elements[0]->getValue());
-    $elements = $this->xpath('//input[@name="radios-string-zero" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('0', $elements[0]->getValue());
-    $elements = $this->xpath('//input[@name="radios-int-non-zero" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('10', $elements[0]->getValue());
-    $elements = $this->xpath('//input[@name="radios-int-non-zero-as-string" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('100', $elements[0]->getValue());
-    $elements = $this->xpath('//input[@name="radios-empty-string" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('0', $elements[0]->getValue());
-    $elements = $this->xpath('//input[@name="radios-empty-array" and @checked]');
-    $this->assertCount(0, $elements);
-    $elements = $this->xpath('//input[@name="radios-key-FALSE" and @checked]');
-    $this->assertCount(1, $elements);
-    $this->assertSame('0', $elements[0]->getValue());
+    $this->assertSession()->elementNotExists('xpath', '//input[@name="radios-boolean-false" and @checked]');
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios-boolean-any" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios-boolean-any", 'All');
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios-string-zero" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios-string-zero", '0');
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios-int-non-zero" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios-int-non-zero", '10');
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios-int-non-zero-as-string" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios-int-non-zero-as-string", '100');
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios-empty-string" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios-empty-string", '0');
+    $this->assertSession()->elementNotExists('xpath', '//input[@name="radios-empty-array" and @checked]');
+    $this->assertSession()->elementsCount('xpath', '//input[@name="radios-key-FALSE" and @checked]', 1);
+    $this->assertSession()->fieldValueEquals("radios-key-FALSE", '0');
   }
 
   /**
@@ -203,10 +183,9 @@ public function testRequiredFieldsetsAndDetails() {
   public function testFormAutocomplete() {
     $this->drupalGet('form-test/autocomplete');
 
-    $result = $this->xpath('//input[@id="edit-autocomplete-1" and contains(@data-autocomplete-path, "form-test/autocomplete-1")]');
-    $this->assertCount(0, $result, 'Ensure that the user does not have access to the autocompletion');
-    $result = $this->xpath('//input[@id="edit-autocomplete-2" and contains(@data-autocomplete-path, "form-test/autocomplete-2/value")]');
-    $this->assertCount(0, $result, 'Ensure that the user does not have access to the autocompletion');
+    // Ensure that the user does not have access to the autocompletion.
+    $this->assertSession()->elementNotExists('xpath', '//input[@id="edit-autocomplete-1" and contains(@data-autocomplete-path, "form-test/autocomplete-1")]');
+    $this->assertSession()->elementNotExists('xpath', '//input[@id="edit-autocomplete-2" and contains(@data-autocomplete-path, "form-test/autocomplete-2/value")]');
 
     $user = $this->drupalCreateUser(['access autocomplete test']);
     $this->drupalLogin($user);
@@ -215,10 +194,9 @@ public function testFormAutocomplete() {
     // Make sure that the autocomplete library is added.
     $this->assertRaw('core/misc/autocomplete.js');
 
-    $result = $this->xpath('//input[@id="edit-autocomplete-1" and contains(@data-autocomplete-path, "form-test/autocomplete-1")]');
-    $this->assertCount(1, $result, 'Ensure that the user does have access to the autocompletion');
-    $result = $this->xpath('//input[@id="edit-autocomplete-2" and contains(@data-autocomplete-path, "form-test/autocomplete-2/value")]');
-    $this->assertCount(1, $result, 'Ensure that the user does have access to the autocompletion');
+    // Ensure that the user does have access to the autocompletion.
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-autocomplete-1" and contains(@data-autocomplete-path, "form-test/autocomplete-1")]');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-autocomplete-2" and contains(@data-autocomplete-path, "form-test/autocomplete-2/value")]');
   }
 
   /**
diff --git a/core/modules/system/tests/src/Functional/Form/ElementsLabelsTest.php b/core/modules/system/tests/src/Functional/Form/ElementsLabelsTest.php
index 0364bd9d5469b78734aff731566d0a93b3ccbc8c..8fe16f2d7a82878098ee45bdcc67725641db87db 100644
--- a/core/modules/system/tests/src/Functional/Form/ElementsLabelsTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ElementsLabelsTest.php
@@ -33,41 +33,30 @@ public function testFormLabels() {
 
     // Check that the checkbox/radio processing is not interfering with
     // basic placement.
-    $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), 'Label follows field and label option class correct for regular checkboxes.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]');
 
     // Make sure the label is rendered for checkboxes.
-    $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-0"]/following-sibling::label[@for="edit-form-checkboxes-test-0" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), 'Label 0 found checkbox.');
-
-    $elements = $this->xpath('//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), 'Label follows field and label option class correct for regular radios.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-form-checkboxes-test-0"]/following-sibling::label[@for="edit-form-checkboxes-test-0" and @class="option"]');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]');
 
     // Make sure the label is rendered for radios.
-    $elements = $this->xpath('//input[@id="edit-form-radios-test-0"]/following-sibling::label[@for="edit-form-radios-test-0" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), 'Label 0 found radios.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-form-radios-test-0"]/following-sibling::label[@for="edit-form-radios-test-0" and @class="option"]');
 
     // Exercise various defaults for checkboxes and modifications to ensure
     // appropriate override and correct behavior.
-    $elements = $this->xpath('//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), 'Label follows field and label option class correct for a checkbox by default.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]');
 
     // Exercise various defaults for textboxes and modifications to ensure
     // appropriate override and correct behavior.
     $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required" and @class="js-form-required form-required"]/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]');
     $this->assertTrue(isset($elements[0]), 'Label precedes textfield, with required marker inside label.');
 
-    $elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required" and @class="js-form-required form-required"]');
-    $this->assertTrue(isset($elements[0]), 'Label tag with required marker precedes required textfield with no title.');
-
-    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/preceding-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="visually-hidden"]');
-    $this->assertTrue(isset($elements[0]), 'Label preceding field and label class is visually-hidden.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required" and @class="js-form-required form-required"]');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-form-textfield-test-title-invisible"]/preceding-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="visually-hidden"]');
 
-    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="js-form-required form-required"]');
-    $this->assertFalse(isset($elements[0]), 'No required marker on non-required field.');
+    $this->assertSession()->elementNotExists('xpath', '//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="js-form-required form-required"]');
 
-    $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]');
-    $this->assertTrue(isset($elements[0]), 'Label after field and label option class correct for text field.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]');
 
     $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-no-show"]');
     $this->assertFalse(isset($elements[0]), 'No label tag when title set not to display.');
@@ -120,22 +109,19 @@ public function testFormDescriptions() {
     // Check #description placement with #description_display='after'.
     $field_id = 'edit-form-textfield-test-description-after';
     $description_id = $field_id . '--description';
-    $elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/following-sibling::div[@id="' . $description_id . '"]');
-    $this->assertTrue(isset($elements[0]), 'Properly places the #description element after the form item.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/following-sibling::div[@id="' . $description_id . '"]');
 
     // Check #description placement with #description_display='before'.
     $field_id = 'edit-form-textfield-test-description-before';
     $description_id = $field_id . '--description';
-    $elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/preceding-sibling::div[@id="' . $description_id . '"]');
-    $this->assertTrue(isset($elements[0]), 'Properly places the #description element before the form item.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/preceding-sibling::div[@id="' . $description_id . '"]');
 
     // Check if the class is 'visually-hidden' on the form element description
     // for the option with #description_display='invisible' and also check that
     // the description is placed after the form element.
     $field_id = 'edit-form-textfield-test-description-invisible';
     $description_id = $field_id . '--description';
-    $elements = $this->xpath('//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/following-sibling::div[contains(@class, "visually-hidden")]');
-    $this->assertTrue(isset($elements[0]), 'Properly renders the #description element visually-hidden.');
+    $this->assertSession()->elementExists('xpath', '//input[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]/following-sibling::div[contains(@class, "visually-hidden")]');
   }
 
   /**
diff --git a/core/modules/system/tests/src/Functional/Form/FormStoragePageCacheTest.php b/core/modules/system/tests/src/Functional/Form/FormStoragePageCacheTest.php
index 0b9024c3230d1cf4780b58c61e6685c5d68219d0..34c534d22d5f3829ac89cae4d4f5e3c36226b226 100644
--- a/core/modules/system/tests/src/Functional/Form/FormStoragePageCacheTest.php
+++ b/core/modules/system/tests/src/Functional/Form/FormStoragePageCacheTest.php
@@ -36,9 +36,9 @@ protected function setUp(): void {
    * Return the build id of the current form.
    */
   protected function getFormBuildId() {
-    $build_id_fields = $this->xpath('//input[@name="form_build_id"]');
-    $this->assertCount(1, $build_id_fields, 'One form build id field on the page');
-    return (string) $build_id_fields[0]->getAttribute('value');
+    // Ensure the hidden 'form_build_id' field is unique.
+    $this->assertSession()->elementsCount('xpath', '//input[@name="form_build_id"]', 1);
+    return (string) $this->assertSession()->hiddenFieldExists('form_build_id')->getAttribute('value');
   }
 
   /**
diff --git a/core/modules/system/tests/src/Functional/Form/FormTest.php b/core/modules/system/tests/src/Functional/Form/FormTest.php
index a2172a9c5f2b3997954fca5798f90c79037a0eb5..b2117938149f5f8e380a2be3db69302ae952528a 100644
--- a/core/modules/system/tests/src/Functional/Form/FormTest.php
+++ b/core/modules/system/tests/src/Functional/Form/FormTest.php
@@ -895,11 +895,8 @@ public function testRequiredAttribute() {
     $expected = 'required';
     // Test to make sure the elements have the proper required attribute.
     foreach (['textfield', 'password'] as $type) {
-      $element = $this->xpath('//input[@id=:id and @required=:expected]', [
-        ':id' => 'edit-' . $type,
-        ':expected' => $expected,
-      ]);
-      $this->assertTrue(!empty($element), new FormattableMarkup('The @type has the proper required attribute.', ['@type' => $type]));
+      $field = $this->assertSession()->fieldExists("edit-$type");
+      $this->assertEquals($expected, $field->getAttribute('required'), "The $type has the proper required attribute.");
     }
 
     // Test to make sure textarea has the proper required attribute.
diff --git a/core/modules/system/tests/src/Functional/Form/StorageTest.php b/core/modules/system/tests/src/Functional/Form/StorageTest.php
index cb36f882337e3b27023cf6b0659ffa63032755f8..2343a98915b9171181f6dd2f1f9cf6892c27cf08 100644
--- a/core/modules/system/tests/src/Functional/Form/StorageTest.php
+++ b/core/modules/system/tests/src/Functional/Form/StorageTest.php
@@ -149,9 +149,11 @@ public function testCachedFormStorageValidation() {
   public function testImmutableForm() {
     // Request the form with 'cache' query parameter to enable form caching.
     $this->drupalGet('form_test/form-storage', ['query' => ['cache' => 1, 'immutable' => 1]]);
-    $buildIdFields = $this->xpath('//input[@name="form_build_id"]');
-    $this->assertCount(1, $buildIdFields, 'One form build id field on the page');
-    $buildId = $buildIdFields[0]->getValue();
+
+    // Ensure the hidden 'form_build_id' field is unique.
+    $this->assertSession()->elementsCount('xpath', '//input[@name="form_build_id"]', 1);
+
+    $buildId = $this->assertSession()->hiddenFieldExists('form_build_id')->getValue();
 
     // Trigger validation error by submitting an empty title.
     $edit = ['title' => ''];
@@ -160,10 +162,11 @@ public function testImmutableForm() {
     // Verify that the build-id did change.
     $this->assertSession()->hiddenFieldValueNotEquals('form_build_id', $buildId);
 
+    // Ensure the hidden 'form_build_id' field is unique.
+    $this->assertSession()->elementsCount('xpath', '//input[@name="form_build_id"]', 1);
+
     // Retrieve the new build-id.
-    $buildIdFields = $this->xpath('//input[@name="form_build_id"]');
-    $this->assertCount(1, $buildIdFields, 'One form build id field on the page');
-    $buildId = (string) $buildIdFields[0]->getValue();
+    $buildId = (string) $this->assertSession()->hiddenFieldExists('form_build_id')->getValue();
 
     // Trigger validation error by again submitting an empty title.
     $edit = ['title' => ''];
@@ -178,9 +181,9 @@ public function testImmutableForm() {
    */
   public function testImmutableFormLegacyProtection() {
     $this->drupalGet('form_test/form-storage', ['query' => ['cache' => 1, 'immutable' => 1]]);
-    $build_id_fields = $this->xpath('//input[@name="form_build_id"]');
-    $this->assertCount(1, $build_id_fields, 'One form build id field on the page');
-    $build_id = $build_id_fields[0]->getValue();
+    // Ensure the hidden 'form_build_id' field is unique.
+    $this->assertSession()->elementsCount('xpath', '//input[@name="form_build_id"]', 1);
+    $build_id = $this->assertSession()->hiddenFieldExists('form_build_id')->getValue();
 
     // Try to poison the form cache.
     $response = $this->drupalGet('form-test/form-storage-legacy/' . $build_id, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']], ['X-Requested-With: XMLHttpRequest']);
diff --git a/core/modules/system/tests/src/Functional/Form/SystemConfigFormTest.php b/core/modules/system/tests/src/Functional/Form/SystemConfigFormTest.php
index b5bf7b1db79fbe2d3a542fe074d2ddd6f0e42b1d..7811f695d683d8de4e7fe4243b25256c6e8bb2d2 100644
--- a/core/modules/system/tests/src/Functional/Form/SystemConfigFormTest.php
+++ b/core/modules/system/tests/src/Functional/Form/SystemConfigFormTest.php
@@ -28,8 +28,8 @@ class SystemConfigFormTest extends BrowserTestBase {
    */
   public function testSystemConfigForm() {
     $this->drupalGet('form-test/system-config-form');
-    $element = $this->xpath('//div[@id = :id]/input[contains(@class, :class)]', [':id' => 'edit-actions', ':class' => 'button--primary']);
-    $this->assertNotEmpty($element, 'The primary action submit button was found.');
+    // Verify the primary action submit button is found.
+    $this->assertSession()->elementExists('xpath', "//div[@id = 'edit-actions']/input[contains(@class, 'button--primary')]");
     $this->submitForm([], 'Save configuration');
     $this->assertText('The configuration options have been saved.');
   }
diff --git a/core/modules/system/tests/src/Functional/Form/ValidationTest.php b/core/modules/system/tests/src/Functional/Form/ValidationTest.php
index 422995b6f0121834b1ba795f2bda49014f2b4b35..807b1b9109d40015140bd5d0ae01155adb44da51 100644
--- a/core/modules/system/tests/src/Functional/Form/ValidationTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ValidationTest.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\Tests\system\Functional\Form;
 
-use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Core\Render\Element;
 use Drupal\Tests\BrowserTestBase;
 
@@ -102,18 +101,12 @@ public function testValidateLimitErrors() {
     $this->drupalGet($path);
     $expected = 'formnovalidate';
     foreach (['partial', 'partial-numeric-index', 'substring'] as $type) {
-      $element = $this->xpath('//input[@id=:id and @formnovalidate=:expected]', [
-        ':id' => 'edit-' . $type,
-        ':expected' => $expected,
-      ]);
-      $this->assertTrue(!empty($element), new FormattableMarkup('The @type button has the proper formnovalidate attribute.', ['@type' => $type]));
+      // Verify the $type button has the proper formnovalidate attribute.
+      $this->assertSession()->elementExists('xpath', "//input[@id='edit-$type' and @formnovalidate='$expected']");
     }
     // The button with full server-side validation should not have the
     // 'formnovalidate' attribute.
-    $element = $this->xpath('//input[@id=:id and not(@formnovalidate)]', [
-      ':id' => 'edit-full',
-    ]);
-    $this->assertTrue(!empty($element), 'The button with full server-side validation does not have the formnovalidate attribute.');
+    $this->assertSession()->elementExists('xpath', "//input[@id='edit-full' and not(@formnovalidate)]");
 
     // Submit the form by pressing the 'Partial validate' button (uses
     // #limit_validation_errors) and ensure that the title field is not
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php b/core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php
index 1c3bd4e96b05217810cedfd784937ed9103775a8..bba4fc62d76bc866905b2f1d0f6af6f7cc9fc693 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php
@@ -57,7 +57,7 @@ public function testFormSettingsSubmissionHandler($theme) {
 
     // Assert the new file is uploaded as temporary. This file should not be
     // saved as permanent if settings are not submitted.
-    $image_field = $this->xpath('//input[@name="custom_logo[fids]"]')[0];
+    $image_field = $this->assertSession()->hiddenFieldExists('custom_logo[fids]');
     $file = File::load($image_field->getValue());
     $this->assertFalse($file->isPermanent());
 
@@ -65,7 +65,7 @@ public function testFormSettingsSubmissionHandler($theme) {
     \Drupal::entityTypeManager()->getStorage('file')->resetCache();
 
     // Assert the uploaded file is saved as permanent.
-    $image_field = $this->xpath('//input[@name="custom_logo[fids]"]')[0];
+    $image_field = $this->assertSession()->hiddenFieldExists('custom_logo[fids]');
     $file = File::load($image_field->getValue());
     $this->assertTrue($file->isPermanent());
   }
diff --git a/core/modules/taxonomy/tests/src/Functional/TermAutocompleteTest.php b/core/modules/taxonomy/tests/src/Functional/TermAutocompleteTest.php
index 3ecdf83195c688cf0c3be704410c37ea02834840..ab39d7babc96bcd7e1d09c3319aa495607e822d3 100644
--- a/core/modules/taxonomy/tests/src/Functional/TermAutocompleteTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TermAutocompleteTest.php
@@ -139,8 +139,8 @@ protected function setUp(): void {
 
     // Retrieve the autocomplete url.
     $this->drupalGet('node/add/article');
-    $result = $this->xpath('//input[@name="' . $this->fieldName . '[0][target_id]"]');
-    $this->autocompleteUrl = $this->getAbsoluteUrl($result[0]->getAttribute('data-autocomplete-path'));
+    $field = $this->assertSession()->fieldExists("{$this->fieldName}[0][target_id]");
+    $this->autocompleteUrl = $this->getAbsoluteUrl($field->getAttribute('data-autocomplete-path'));
   }
 
   /**
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxFormPageCacheTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxFormPageCacheTest.php
index 7f521b41de6577c9010e199f0d4517d7ea91a39e..894ffdca10b76717710853fc04cac6f4329c13c7 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxFormPageCacheTest.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxFormPageCacheTest.php
@@ -36,9 +36,9 @@ protected function setUp(): void {
    * Return the build id of the current form.
    */
   protected function getFormBuildId() {
-    $build_id_fields = $this->xpath('//input[@name="form_build_id"]');
-    $this->assertCount(1, $build_id_fields, 'One form build id field on the page');
-    return $build_id_fields[0]->getValue();
+    // Ensure the hidden 'form_build_id' field is unique.
+    $this->assertSession()->elementsCount('xpath', '//input[@name="form_build_id"]', 1);
+    return $this->assertSession()->hiddenFieldExists('form_build_id')->getValue();
   }
 
   /**
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Tests/DrupalSelenium2DriverTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Tests/DrupalSelenium2DriverTest.php
index a153e6b40c1d3b2629e7d8c0c4545fb8fb28143d..82e26753eb7666af89ff62663552c980a2dbedb5 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/Tests/DrupalSelenium2DriverTest.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Tests/DrupalSelenium2DriverTest.php
@@ -62,7 +62,7 @@ public function testGetRemoteFilePath() {
 
     // Tests that uploading multiple remote files works with remote path.
     $this->drupalGet($entity->toUrl('edit-form'));
-    $multiple_field = $this->xpath('//input[@multiple]')[0];
+    $multiple_field = $this->assertSession()->elementExists('xpath', '//input[@multiple]');
     $multiple_field->setValue(implode("\n", $remote_paths));
     $this->assertSession()->assertWaitOnAjaxRequest();
     $this->getSession()->getPage()->findButton('Save')->click();