diff --git a/core/drupalci.yml b/core/drupalci.yml
index 2085b9737b..3eecbae44b 100644
--- a/core/drupalci.yml
+++ b/core/drupalci.yml
@@ -3,48 +3,11 @@
 # https://www.drupal.org/drupalorg/docs/drupal-ci/customizing-drupalci-testing
 build:
   assessment:
-    validate_codebase:
-      phplint:
-      csslint:
-        halt-on-fail: false
-      eslint:
-        # A test must pass eslinting standards check in order to continue processing.
-        halt-on-fail: false
-      phpcs:
-        # phpcs will use core's specified version of Coder.
-        sniff-all-files: false
-        halt-on-fail: false
     testing:
-      # run_tests task is executed several times in order of performance speeds.
-      # halt-on-fail can be set on the run_tests tasks in order to fail fast.
-      # suppress-deprecations is false in order to be alerted to usages of
-      # deprecated code.
-      run_tests.phpunit:
-        types: 'PHPUnit-Unit'
-        testgroups: '--all'
-        suppress-deprecations: false
-        halt-on-fail: false
-      run_tests.kernel:
-        types: 'PHPUnit-Kernel'
-        testgroups: '--all'
-        suppress-deprecations: false
-        halt-on-fail: false
-      run_tests.simpletest:
-         types: 'Simpletest'
-         testgroups: '--all'
-         suppress-deprecations: false
-         halt-on-fail: false
-      run_tests.functional:
-        types: 'PHPUnit-Functional'
-        testgroups: '--all'
-        suppress-deprecations: false
-        halt-on-fail: false
       run_tests.javascript:
         concurrency: 15
         types: 'PHPUnit-FunctionalJavascript'
-        testgroups: '--all'
+        testgroups: '--class "Drupal\Tests\media_library\FunctionalJavascript\MediaLibraryTest"'
         suppress-deprecations: false
         halt-on-fail: false
-      # Run nightwatch testing.
-      # @see https://www.drupal.org/project/drupal/issues/2869825
-      nightwatchjs:
+        repeat: 200
diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
index 9d54f6a43a..f8150cd794 100644
--- a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
+++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\media_library\FunctionalJavascript;
 
+use Behat\Mink\Exception\ElementNotFoundException;
 use Drupal\Core\Url;
 use Drupal\field_ui\FieldUI;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
@@ -101,33 +102,32 @@ public function testAdministrationPage() {
     $assert_session->linkExists('Add media');
 
     // Verify that media from two separate types is present.
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextContains('Turtle');
+    $this->waitForText('Dog');
+    $this->waitForText('Turtle');
 
     // Test that users can filter by type.
     $page->selectFieldOption('Media type', 'Type One');
     $page->pressButton('Apply filters');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextNotContains('Turtle');
+    $this->waitForNoText('Turtle');
+    $this->waitForText('Dog');
     $page->selectFieldOption('Media type', 'Type Two');
     $page->pressButton('Apply filters');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Dog');
-    $assert_session->pageTextContains('Turtle');
+    $this->waitForNoText('Dog');
+    $this->waitForText('Turtle');
 
     // Test that selecting elements as a part of bulk operations works.
     $page->selectFieldOption('Media type', '- Any -');
     $page->pressButton('Apply filters');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Dog');
+
     // This tests that anchor tags clicked inside the preview are suppressed.
     $this->getSession()->executeScript('jQuery(".js-click-to-select-trigger a")[4].click()');
     $this->submitForm([], 'Apply to selected items');
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextNotContains('Cat');
+    $this->waitForNoText('Cat');
+    $this->waitForText('Dog');
     $this->submitForm([], 'Delete');
-    $assert_session->pageTextNotContains('Dog');
-    $assert_session->pageTextContains('Cat');
+    $this->waitForNoText('Dog');
+    $this->waitForText('Cat');
 
     // Test 'Select all media'.
     $this->getSession()->getPage()->checkField('Select all media');
@@ -135,12 +135,12 @@ public function testAdministrationPage() {
     $this->submitForm([], 'Apply to selected items');
     $this->getSession()->getPage()->pressButton('Delete');
 
-    $assert_session->pageTextNotContains('Cat');
-    $assert_session->pageTextNotContains('Turtle');
-    $assert_session->pageTextNotContains('Snake');
+    $this->waitForNoText('Cat');
+    $this->waitForNoText('Turtle');
+    $this->waitForNoText('Snake');
 
     // Test empty text.
-    $assert_session->pageTextContains('No media available.');
+    $this->waitForText('No media available.');
 
     // Verify that the "Table" link is present, click it and check address.
     $assert_session->linkExists('Table');
@@ -228,8 +228,8 @@ public function testWidgetWithoutMediaTypes() {
     $field_empty_types_message = 'There are no allowed media types configured for this field. <a href="' . $field_empty_types_url->toString() . '">Edit the field settings</a> to select the allowed media types.';
 
     $field_null_types_url = new Url('entity.field_config.node_field_edit_form', [
-        'field_config' => 'node.basic_page.field_null_types_media',
-      ] + $route_bundle_params);
+      'field_config' => 'node.basic_page.field_null_types_media',
+    ] + $route_bundle_params);
     $field_null_types_message = 'There are no allowed media types configured for this field. <a href="' . $field_null_types_url->toString() . '">Edit the field settings</a> to select the allowed media types.';
 
     // Visit a node create page.
@@ -288,25 +288,21 @@ public function testViewsAdmin() {
 
     // Assert that the widget can be seen and that there are 8 items.
     $this->drupalGet('/admin/structure/views/view/media_library/edit/widget');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementsCount('css', '.media-library-item', 8);
+    $this->waitForElementsCount('css', '.media-library-item', 8);
 
     // Assert that filtering works in live preview.
     $page->find('css', '.media-library-view .view-filters')->fillField('name', 'snake');
     $page->find('css', '.media-library-view .view-filters')->pressButton('Apply filters');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementsCount('css', '.media-library-item', 1);
+    $this->waitForElementsCount('css', '.media-library-item', 1);
 
     // Test the same routine but in the view for the table wiget.
     $this->drupalGet('/admin/structure/views/view/media_library/edit/widget_table');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementsCount('css', '.media-library-item', 8);
+    $this->waitForElementsCount('css', '.media-library-item', 8);
 
     // Assert that filtering works in live preview.
     $page->find('css', '.media-library-view .view-filters')->fillField('name', 'snake');
     $page->find('css', '.media-library-view .view-filters')->pressButton('Apply filters');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementsCount('css', '.media-library-item', 1);
+    $this->waitForElementsCount('css', '.media-library-item', 1);
 
     // We cannot test clicking the 'Insert selected' button in either view
     // because we expect an AJAX error, which would always throw an exception
@@ -409,36 +405,33 @@ public function testWidget() {
     $this->drupalGet('node/add/basic_page');
 
     // Assert that media widget instances are present.
-    $assert_session->pageTextContains('Unlimited media');
-    $assert_session->pageTextContains('Twin media');
-    $assert_session->pageTextContains('Single media type');
-    $assert_session->pageTextContains('Empty types media');
+    $this->waitForText('Unlimited media');
+    $this->waitForText('Twin media');
+    $this->waitForText('Single media type');
+    $this->waitForText('Empty types media');
 
     // Assert generic media library elements.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
+    $this->waitForText('Add or select media');
     $this->assertFalse($assert_session->elementExists('css', '.media-library-select-all')->isVisible());
     $page->find('css', '.ui-dialog-titlebar-close')->click();
 
     // Assert that the media type menu is available when more than 1 type is
     // configured for the field.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $menu = $assert_session->elementExists('css', '.media-library-menu');
+    $menu = $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $this->assertTrue($menu->hasLink('Type One'));
     $this->assertFalse($menu->hasLink('Type Two'));
     $this->assertTrue($menu->hasLink('Type Three'));
     $this->assertFalse($menu->hasLink('Type Four'));
     $page->find('css', '.ui-dialog-titlebar-close')->click();
-    $assert_session->assertWaitOnAjaxRequest();
 
     // Assert that the media type menu is available when the target_bundles
     // setting for the entity reference field is null. All types should be
     // allowed in this case.
-    $assert_session->elementExists('css', '.media-library-open-button[name^="field_null_types_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $menu = $assert_session->elementExists('css', '.media-library-menu');
+    $add_media_button = $this->assertElementExistsAfterWait('css', '.media-library-open-button[name^="field_null_types_media"]');
+    $add_media_button->click();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $this->assertTrue($menu->hasLink('Type One'));
     $this->assertTrue($menu->hasLink('Type Two'));
     $this->assertTrue($menu->hasLink('Type Three'));
@@ -449,8 +442,8 @@ public function testWidget() {
     // Assert that the media type menu is not available when only 1 type is
     // configured for the field.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_single_media_type"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementTextContains('css', '.media-library-selected-count', '0 of 1 item selected');
+    $this->waitForElementTextContains('.media-library-selected-count', '0 of 1 item selected');
+
     // Select a media item, assert the hidden selection field contains the ID of
     // the selected item.
     $checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
@@ -463,69 +456,73 @@ public function testWidget() {
 
     // Assert the menu links can be sorted through the widget configuration.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $links = $page->findAll('css', '.media-library-menu a');
     $link_titles = [];
     foreach ($links as $link) {
       $link_titles[] = $link->getText();
     }
-    $expected_link_titles = ['Type Three (active tab)', 'Type One', 'Type Two', 'Type Four'];
+    $expected_link_titles = [
+      'Type Three (active tab)',
+      'Type One',
+      'Type Two',
+      'Type Four',
+    ];
     $this->assertSame($link_titles, $expected_link_titles);
     $this->drupalGet('admin/structure/types/manage/basic_page/form-display');
     $assert_session->buttonExists('field_twin_media_settings_edit')->press();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->buttonExists('Show row weights')->press();
+    $toggle_weight_button = $this->assertElementExistsAfterWait('css', '#field-twin-media .tabledrag-toggle-weight');
+    $toggle_weight_button->press();
     $assert_session->fieldExists('fields[field_twin_media][settings_edit_form][settings][media_types][type_one][weight]')->selectOption(0);
     $assert_session->fieldExists('fields[field_twin_media][settings_edit_form][settings][media_types][type_three][weight]')->selectOption(1);
     $assert_session->fieldExists('fields[field_twin_media][settings_edit_form][settings][media_types][type_four][weight]')->selectOption(2);
     $assert_session->fieldExists('fields[field_twin_media][settings_edit_form][settings][media_types][type_two][weight]')->selectOption(3);
     $assert_session->buttonExists('Save')->press();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->buttonExists('Hide row weights')->press();
+
     $this->drupalGet('node/add/basic_page');
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $link_titles = array_map(function ($link) {
       return $link->getText();
     }, $page->findAll('css', '.media-library-menu a'));
-    $this->assertSame($link_titles, ['Type One (active tab)', 'Type Three', 'Type Four', 'Type Two']);
+    $this->assertSame($link_titles, [
+      'Type One (active tab)',
+      'Type Three',
+      'Type Four',
+      'Type Two',
+    ]);
     $page->find('css', '.ui-dialog-titlebar-close')->click();
 
     // Assert media is only visible on the tab for the related media type.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextContains('Bear');
-    $assert_session->pageTextNotContains('Turtle');
-    $page->clickLink('Type Three');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementExists('named', ['link', 'Type Three (active tab)']);
-    $assert_session->pageTextNotContains('Dog');
-    $assert_session->pageTextNotContains('Bear');
-    $assert_session->pageTextNotContains('Turtle');
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Dog');
+    $this->waitForText('Bear');
+    $this->waitForNoText('Turtle');
+    $this->clickTypeTab('Three');
+    $this->waitForNoText('Dog');
+    $this->waitForNoText('Bear');
+    $this->waitForNoText('Turtle');
     $page->find('css', '.ui-dialog-titlebar-close')->click();
 
     // Assert the exposed name filter of the view.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $session = $this->getSession();
-    $session->getPage()->fillField('Name', 'Dog');
+    $this->waitForFillField('Name', 'Dog');
     $session->getPage()->pressButton('Apply filters');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextNotContains('Bear');
-    $session->getPage()->fillField('Name', '');
+    $this->waitForText('Dog');
+    $this->waitForNoText('Bear');
+    $this->waitForFillField('Name', '');
     $session->getPage()->pressButton('Apply filters');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextContains('Bear');
+    $this->waitForText('Dog');
+    $this->waitForText('Bear');
     $page->find('css', '.ui-dialog-titlebar-close')->click();
 
     // Assert the media library contains header links to switch between the grid
     // and table display.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementExists('css', '.media-library-view .media-library-item--grid');
+    $this->assertElementExistsAfterWait('css', '.media-library-view .media-library-item--grid');
     $assert_session->elementNotExists('css', '.media-library-view .media-library-item--table');
     // Assert the 'Apply filter' button is not moved to the button pane.
     $button_pane = $assert_session->elementExists('css', '.ui-dialog-buttonpane');
@@ -534,101 +531,93 @@ public function testWidget() {
     $assert_session->linkExists('Grid');
     $page->clickLink('Table');
     // Assert the display change is correctly announced for screen readers.
-    $this->assertNotEmpty($assert_session->waitForText('Loading table view.'));
-    $this->assertNotEmpty($assert_session->waitForText('Changed to table view.'));
-    $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.media-library-view .media-library-item--table'));
+    $this->waitForText('Loading table view.');
+    $this->waitForText('Changed to table view.');
+    $this->assertElementExistsAfterWait('css', '.media-library-view .media-library-item--table');
     $assert_session->elementNotExists('css', '.media-library-view .media-library-item--grid');
     // Assert the 'Apply filter' button is not moved to the button pane.
     $assert_session->buttonExists('Insert selected', $button_pane);
     $assert_session->buttonNotExists('Apply filters', $button_pane);
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextContains('Bear');
-    $assert_session->pageTextNotContains('Turtle');
+    $this->waitForText('Dog');
+    $this->waitForText('Bear');
+    $this->waitForNoText('Turtle');
     // Assert the exposed filters can be applied.
-    $page->fillField('Name', 'Dog');
+    $this->waitForFillField('Name', 'Dog');
     $page->pressButton('Apply filters');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextNotContains('Bear');
-    $assert_session->pageTextNotContains('Turtle');
+    $this->waitForText('Dog');
+    $this->waitForNoText('Bear');
+    $this->waitForNoText('Turtle');
     $page->checkField('Select Dog');
     $assert_session->linkExists('Table');
     $page->clickLink('Grid');
     // Assert the display change is correctly announced for screen readers.
-    $this->assertNotEmpty($assert_session->waitForText('Loading grid view.'));
-    $this->assertNotEmpty($assert_session->waitForText('Changed to grid view.'));
-    $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.media-library-view .media-library-item--grid'));
+    $this->waitForText('Loading grid view.');
+    $this->waitForText('Changed to grid view.');
+    $this->assertElementExistsAfterWait('css', '.media-library-view .media-library-item--grid');
     $assert_session->elementNotExists('css', '.media-library-view .media-library-item--table');
     // Assert the exposed filters are persisted when changing display.
-    $this->assertSame('Dog', $page->findField('Name')->getValue());
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextNotContains('Bear');
-    $assert_session->pageTextNotContains('Turtle');
+    $this->waitForText('Dog');
+    $this->waitForNoText('Bear');
+    $this->waitForNoText('Turtle');
     $assert_session->linkExists('Grid');
     $assert_session->linkExists('Table');
     // Select the item.
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Added one media item.');
     // Ensure that the selection completed successfully.
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextNotContains('Bear');
-    $assert_session->pageTextNotContains('Turtle');
+    $this->waitForNoText('Add or select media');
+    $this->waitForText('Dog');
+    $this->waitForNoText('Bear');
+    $this->waitForNoText('Turtle');
     // Clear the selection.
     $assert_session->elementAttributeContains('css', '.media-library-item__remove', 'aria-label', 'Remove Dog');
     $assert_session->elementExists('css', '.media-library-item__remove')->click();
-    $this->assertNotEmpty($assert_session->waitForText('Removed Dog.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Removed Dog.');
 
     // Assert adding a single media item and removing it.
-    $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-open-button[name^="field_twin_media"]')->click();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
     $this->assertGreaterThanOrEqual(1, count($checkboxes));
     $checkboxes[0]->click();
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Added one media item.');
     // Assert the focus is set back on the open button of the media field.
     $this->assertJsCondition('jQuery("#field_twin_media-media-library-wrapper .js-media-library-open-button").is(":focus")');
     $assert_session->elementAttributeContains('css', '.media-library-item__remove', 'aria-label', 'Remove Dog');
     $assert_session->elementExists('css', '.media-library-item__remove')->click();
-    $this->assertNotEmpty($assert_session->waitForText('Removed Dog.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Removed Dog.');
     // Assert the focus is set back on the open button of the media field.
     $this->assertJsCondition('jQuery("#field_twin_media-media-library-wrapper .js-media-library-open-button").is(":focus")');
 
     // Assert we can select the same media item twice.
-    $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-open-button[name^="field_twin_media"]')->click();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $page->checkField('Select Dog');
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Added one media item.');
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $page->checkField('Select Dog');
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Added one media item.');
     // Assert the same has been added twice and remove the items again.
-    $this->assertCount(2, $page->findAll('css', '.field--name-field-twin-media .media-library-item'));
+    $this->assertElementExistsAfterWait('css', '[name="field_twin_media[selection][1][target_id]"]');
+    $this->waitForElementsCount('css', '.field--name-field-twin-media .media-library-item', 2);
     $assert_session->hiddenFieldValueEquals('field_twin_media[selection][0][target_id]', 4);
     $assert_session->hiddenFieldValueEquals('field_twin_media[selection][1][target_id]', 4);
     $assert_session->elementExists('css', '.media-library-item__remove')->click();
-    $this->assertNotEmpty($assert_session->waitForText('Removed Dog.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementExists('css', '.media-library-item__remove')->click();
-    $this->assertNotEmpty($assert_session->waitForText('Removed Dog.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForElementsCount('css', '.media-library-item__remove', 1);
+    $this->waitForText('Removed Dog.');
+    $this->assertElementExistsAfterWait('css', '.media-library-item__remove')->click();
+    $this->waitForText('Removed Dog.');
+    $assert_session->assertNoElementAfterWait('css', '.media-library-item__remove');
 
     // Assert the selection is persistent in the media library modal, and
     // the number of selected items is displayed correctly.
-    $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-open-button[name^="field_twin_media"]')->click();
     // Assert the number of selected items is displayed correctly.
-    $assert_session->elementExists('css', '.media-library-selected-count');
+    $this->assertElementExistsAfterWait('css', '.media-library-selected-count');
     $assert_session->elementTextContains('css', '.media-library-selected-count', '0 of 2 items selected');
     $assert_session->elementAttributeContains('css', '.media-library-selected-count', 'role', 'status');
     $assert_session->elementAttributeContains('css', '.media-library-selected-count', 'aria-live', 'polite');
@@ -658,10 +647,8 @@ public function testWidget() {
     $this->assertFalse($checkboxes[3]->hasAttribute('disabled'));
     // The selection should be persisted when navigating to other media types in
     // the modal.
-    $page->clickLink('Type Three');
-    $assert_session->assertWaitOnAjaxRequest();
-    $page->clickLink('Type One');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->clickTypeTab('Three');
+    $this->clickTypeTab('One');
     $checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
     $selected_checkboxes = [];
     foreach ($checkboxes as $checkbox) {
@@ -673,8 +660,7 @@ public function testWidget() {
     $assert_session->hiddenFieldValueEquals('media-library-modal-selection', implode(',', $selected_checkboxes));
     $assert_session->elementTextContains('css', '.media-library-selected-count', '1 of 2 items selected');
     // Add to selection from another type.
-    $page->clickLink('Type Two');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->clickTypeTab('Two');
     $checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
     $this->assertCount(4, $checkboxes);
     $checkboxes[0]->click();
@@ -688,24 +674,22 @@ public function testWidget() {
     $this->assertTrue($checkboxes[2]->hasAttribute('disabled'));
     $this->assertTrue($checkboxes[3]->hasAttribute('disabled'));
     // Assert the checkboxes are also disabled on other pages.
-    $page->clickLink('Type One');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->clickTypeTab('One');
     $this->assertTrue($checkboxes[0]->hasAttribute('disabled'));
     $this->assertFalse($checkboxes[1]->hasAttribute('disabled'));
     $this->assertTrue($checkboxes[2]->hasAttribute('disabled'));
     $this->assertTrue($checkboxes[3]->hasAttribute('disabled'));
     // Select the items.
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added 2 media items.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Added 2 media items.');
     // Assert the open button is disabled.
-    $open_button = $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]');
+    $open_button = $this->assertElementExistsAfterWait('css', '.media-library-open-button[name^="field_twin_media"]');
     $this->assertTrue($open_button->hasAttribute('data-disabled-focus'));
     $this->assertTrue($open_button->hasAttribute('disabled'));
     $this->assertJsCondition('jQuery("#field_twin_media-media-library-wrapper .media-library-open-button").is(":disabled")');
 
     // Ensure that the selection completed successfully.
-    $assert_session->pageTextNotContains('Add or select media');
+    $this->waitForNoText('Add or select media');
     $assert_session->elementTextNotContains('css', '#field_twin_media-media-library-wrapper', 'Dog');
     $assert_session->elementTextContains('css', '#field_twin_media-media-library-wrapper', 'Cat');
     $assert_session->elementTextContains('css', '#field_twin_media-media-library-wrapper', 'Turtle');
@@ -714,12 +698,11 @@ public function testWidget() {
     // Remove "Cat" (happens to be the first remove button on the page).
     $assert_session->elementAttributeContains('css', '.media-library-item__remove', 'aria-label', 'Remove Cat');
     $assert_session->elementExists('css', '.media-library-item__remove')->click();
-    $this->assertNotEmpty($assert_session->waitForText('Removed Cat.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Removed Cat.');
     // Assert the focus is set to the wrapper of the other selected item.
     $this->assertJsCondition('jQuery("#field_twin_media-media-library-wrapper .media-library-item").is(":focus")');
+    $this->assertElementExistsAfterWait('css', '[aria-label="Remove Turtle"]');
     $assert_session->elementTextNotContains('css', '#field_twin_media-media-library-wrapper', 'Cat');
-    $assert_session->elementTextContains('css', '#field_twin_media-media-library-wrapper', 'Turtle');
     // Assert the open button is no longer disabled.
     $open_button = $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]');
     $this->assertFalse($open_button->hasAttribute('data-disabled-focus'));
@@ -728,14 +711,13 @@ public function testWidget() {
 
     // Open the media library again and select another item.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
     $this->assertGreaterThanOrEqual(1, count($checkboxes));
     $checkboxes[0]->click();
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementTextContains('css', '#field_twin_media-media-library-wrapper', 'Dog');
+    $this->waitForText('Added one media item.');
+    $this->assertElementExistsAfterWait('css', '[aria-label="Remove Dog"]');
     $assert_session->elementTextNotContains('css', '#field_twin_media-media-library-wrapper', 'Cat');
     $assert_session->elementTextContains('css', '#field_twin_media-media-library-wrapper', 'Turtle');
     $assert_session->elementTextNotContains('css', '#field_twin_media-media-library-wrapper', 'Snake');
@@ -746,7 +728,7 @@ public function testWidget() {
 
     // Assert the selection is cleared when the modal is closed.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
     $this->assertGreaterThanOrEqual(4, count($checkboxes));
     // Nothing is selected yet.
@@ -767,7 +749,7 @@ public function testWidget() {
     // Close the dialog, reopen it and assert not is selected again.
     $page->find('css', '.ui-dialog-titlebar-close')->click();
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     $checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input');
     $this->assertGreaterThanOrEqual(4, count($checkboxes));
     $this->assertFalse($checkboxes[0]->isChecked());
@@ -782,28 +764,28 @@ public function testWidget() {
       'title[0][value]' => 'My page',
       'field_twin_media[selection][0][weight]' => '3',
     ], 'Save');
-    $assert_session->pageTextContains('Basic Page My page has been created');
+    $this->waitForText('Basic Page My page has been created');
     // We removed this item earlier.
-    $assert_session->pageTextNotContains('Cat');
+    $this->waitForNoText('Cat');
     // This item was never selected.
-    $assert_session->pageTextNotContains('Snake');
+    $this->waitForNoText('Snake');
     // "Turtle" should come after "Dog", since we changed the weight.
     $assert_session->elementExists('css', '.field--name-field-twin-media > .field__items > .field__item:last-child:contains("Turtle")');
     // Make sure everything that was selected shows up.
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextContains('Turtle');
+    $this->waitForText('Dog');
+    $this->waitForText('Turtle');
 
     // Re-edit the content and make a new selection.
     $this->drupalGet('node/1/edit');
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextNotContains('Cat');
-    $assert_session->pageTextNotContains('Bear');
-    $assert_session->pageTextNotContains('Horse');
-    $assert_session->pageTextContains('Turtle');
-    $assert_session->pageTextNotContains('Snake');
+    $this->waitForText('Dog');
+    $this->waitForNoText('Cat');
+    $this->waitForNoText('Bear');
+    $this->waitForNoText('Horse');
+    $this->waitForText('Turtle');
+    $this->waitForNoText('Snake');
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
     // Select all media items of type one (should also contain Dog, again).
     $checkbox_selector = '.media-library-view .js-click-to-select-checkbox input';
     $checkboxes = $page->findAll('css', $checkbox_selector);
@@ -813,21 +795,20 @@ public function testWidget() {
     $checkboxes[2]->click();
     $checkboxes[3]->click();
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added 4 media items.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextContains('Cat');
-    $assert_session->pageTextContains('Bear');
-    $assert_session->pageTextContains('Horse');
-    $assert_session->pageTextContains('Turtle');
-    $assert_session->pageTextNotContains('Snake');
+    $this->waitForText('Added 4 media items.');
+    $this->waitForText('Dog');
+    $this->waitForText('Cat');
+    $this->waitForText('Bear');
+    $this->waitForText('Horse');
+    $this->waitForText('Turtle');
+    $this->waitForNoText('Snake');
     $this->submitForm([], 'Save');
-    $assert_session->pageTextContains('Dog');
-    $assert_session->pageTextContains('Cat');
-    $assert_session->pageTextContains('Bear');
-    $assert_session->pageTextContains('Horse');
-    $assert_session->pageTextContains('Turtle');
-    $assert_session->pageTextNotContains('Snake');
+    $this->waitForText('Dog');
+    $this->waitForText('Cat');
+    $this->waitForText('Bear');
+    $this->waitForText('Horse');
+    $this->waitForText('Turtle');
+    $this->waitForNoText('Snake');
   }
 
   /**
@@ -852,17 +833,16 @@ public function testWidgetAnonymous() {
 
     // Add to the unlimited cardinality field.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
 
     // Select the first media item (should be Dog).
-    $page->find('css', '.media-library-view .js-click-to-select-checkbox input')->click();
+    $this->assertElementExistsAfterWait('css', '.media-library-view .js-click-to-select-checkbox input')->click();
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Added one media item.');
 
     // Ensure that the selection completed successfully.
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains('Dog');
+    $this->waitForNoText('Add or select media');
+    $this->waitForText('Dog');
 
     // Save the form.
     $assert_session->elementExists('css', '.js-media-library-widget-toggle-weight')->click();
@@ -870,8 +850,8 @@ public function testWidgetAnonymous() {
       'title[0][value]' => 'My page',
       'field_unlimited_media[selection][0][weight]' => '0',
     ], 'Save');
-    $assert_session->pageTextContains('Basic Page My page has been created');
-    $assert_session->pageTextContains('Dog');
+    $this->waitForText('Basic Page My page has been created');
+    $this->waitForText('Dog');
   }
 
   /**
@@ -910,8 +890,8 @@ public function testWidgetUpload() {
     // Visit a node create page and open the media library.
     $this->drupalGet('node/add/basic_page');
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
 
     // Assert the upload form is not visible for default tab type_three without
     // the proper permissions.
@@ -919,15 +899,12 @@ public function testWidgetUpload() {
 
     // Assert the upload form is not visible for the non-file based media type
     // type_one.
-    $page->clickLink('Type One');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementNotExists('css', '.media-library-add-form');
+    $this->clickTypeTab('One');
 
     // Assert the upload form is visible for type_four.
-    $page->clickLink('Type Four');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->fieldExists('Add files');
-    $assert_session->pageTextContains('Maximum 2 files.');
+    $this->clickTypeTab('Four');
+    $this->waitForFieldExists('Add files');
+    $this->waitForText('Maximum 2 files.');
 
     // Create a user that can create media for all media types.
     $user = $this->drupalCreateUser([
@@ -948,8 +925,8 @@ public function testWidgetUpload() {
 
     // Add to the twin media field.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
 
     // Assert the upload form is now visible for default tab type_three.
     $assert_session->elementExists('css', '.media-library-add-form');
@@ -959,9 +936,8 @@ public function testWidgetUpload() {
     $assert_session->elementExists('css', '.media-library-add-form--without-input');
     $assert_session->elementNotExists('css', '.media-library-add-form--with-input');
     $page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_image->uri));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('The media item has been created but has not yet been saved. Fill in any required fields and save to add it to the media library.');
     $this->assertJsCondition('jQuery(".media-library-add-form__added-media").is(":focus")');
-    $assert_session->pageTextContains('The media item has been created but has not yet been saved. Fill in any required fields and save to add it to the media library.');
     $assert_session->elementAttributeContains('css', '.media-library-add-form__added-media', 'aria-label', 'Added media items');
     $assert_session->elementExists('css', '.media-library-add-form--with-input');
     $assert_session->elementNotExists('css', '.media-library-add-form--without-input');
@@ -979,11 +955,14 @@ public function testWidgetUpload() {
     // Assert the name field contains the filename and the alt text is required.
     $assert_session->fieldValueEquals('Name', $png_image->filename);
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Alternative text field is required');
-    $page->fillField('Alternative text', $this->randomString());
+    $this->waitForText('Alternative text field is required');
+    $this->waitForFillField('Alternative text', 'alt text');
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
-    $assert_session->assertWaitOnAjaxRequest();
+    // Ensure the media item was saved to the library and automatically
+    // selected. The added media items should be in the first position of the
+    // add form.
+    $this->waitForText('1 of 2 items selected', 60000); //RANDOM FAIL
+    $this->waitForText($png_image->filename);
     // The file should be permanent now.
     $files = $file_storage->loadMultiple();
     $file = array_pop($files);
@@ -991,49 +970,41 @@ public function testWidgetUpload() {
     // Load the created media item.
     $media_items = Media::loadMultiple();
     $added_media = array_pop($media_items);
-    // Ensure the media item was saved to the library and automatically
-    // selected. The added media items should be in the first position of the
-    // add form.
-    $assert_session->pageTextContains('Add or select media');
-    $assert_session->pageTextContains($png_image->filename);
+
     $assert_session->fieldValueEquals('media_library_select_form[0]', $added_media->id());
     $assert_session->checkboxChecked('media_library_select_form[0]');
-    $assert_session->pageTextContains('1 of 2 items selected');
+    $this->waitForText('1 of 2 items selected');
     $assert_session->hiddenFieldValueEquals('current_selection', $added_media->id());
     // Ensure the created item is added in the widget.
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains($png_image->filename);
+    $this->waitForText('Added one media item.');
+    $this->waitForText($png_image->filename);
+    $this->waitForNoText('Add or select media');
 
     // Remove the item.
     $assert_session->elementExists('css', '.media-library-item__remove')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains($png_image->filename);
+    $assert_session->assertNoElementAfterWait('css', '.media-library-item__remove');
+    $this->waitForNoText($png_image->filename);
 
     // Assert we can also directly insert uploaded files in the widget.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Three');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
+    $this->clickTypeTab('Three');
     $png_uri_2 = $file_system->copy($png_image->uri, 'public://');
     $page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_uri_2));
-    $assert_session->assertWaitOnAjaxRequest();
-    $page->fillField('Alternative text', $this->randomString());
+    $this->waitForText('Alternative text');
+    $this->waitForFillField('Alternative text', 'alt text');
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and insert');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains($file_system->basename($png_uri_2));
+    $this->waitForText('Added one media item.', 50000); //RANDOM FAIL?
+    $this->waitForText($file_system->basename($png_uri_2), 50000);
+    $this->waitForNoText('Add or select media');
 
     // Also make sure that we can upload to the unlimited cardinality field.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Three');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
+    $this->clickTypeTab('Three');
 
     // Select a media item to check if the selection is persisted when adding
     // new items.
@@ -1041,30 +1012,30 @@ public function testWidgetUpload() {
     $checkbox = $page->findField("Select $existing_media_name");
     $selected_item_id = $checkbox->getAttribute('value');
     $checkbox->click();
-    $assert_session->pageTextContains('1 item selected');
+    $this->waitForText('1 item selected');
     $assert_session->hiddenFieldValueEquals('current_selection', $selected_item_id);
     $png_uri_3 = $file_system->copy($png_image->uri, 'public://');
     $page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_uri_3));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('The media item has been created but has not yet been saved.');
     $assert_session->checkboxChecked("Select $existing_media_name");
-    $page->fillField('Name', 'Unlimited Cardinality Image');
-    $page->fillField('Alternative text', $this->randomString());
+    $this->waitForFillField('Name', 'Unlimited Cardinality Image');
+    $this->waitForFillField('Alternative text', 'alt text');
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForNoText('Save and select', 50000);
+    // Ensure the media item was saved to the library and automatically
+    // selected. The added media items should be in the first position of the
+    // add form.
+    $this->waitForText('Add or select media');
+    $this->waitForText('Unlimited Cardinality Image');
     // Load the created media item.
     $media_items = Media::loadMultiple();
     $added_media = array_pop($media_items);
     $added_media_name = $added_media->label();
-    // Ensure the media item was saved to the library and automatically
-    // selected. The added media items should be in the first position of the
-    // add form.
-    $assert_session->pageTextContains('Add or select media');
-    $assert_session->pageTextContains('Unlimited Cardinality Image');
     $assert_session->fieldValueEquals('media_library_select_form[0]', $added_media->id());
     $assert_session->checkboxChecked('media_library_select_form[0]');
     // Assert the item that was selected before uploading the file is still
     // selected.
-    $assert_session->pageTextContains('2 items selected');
+    $this->waitForText('2 items selected');
     $assert_session->checkboxChecked("Select $added_media_name");
     $assert_session->checkboxChecked("Select $existing_media_name");
     $assert_session->hiddenFieldValueEquals('current_selection', implode(',', [$selected_item_id, $added_media->id()]));
@@ -1078,60 +1049,56 @@ public function testWidgetUpload() {
     $this->assertCount(2, $selected_checkboxes);
     // Ensure the created item is added in the widget.
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains('Unlimited Cardinality Image');
+    $this->waitForText('Added one media item.');
+    $assert_session->assertNoElementAfterWait('css', '.media-library-wrapper');
+    $this->waitForNoText('Add or select media');
+    $this->waitForText('Unlimited Cardinality Image');
 
     // Assert we can now only upload one more media item.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Four');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
+    $this->clickTypeTab('Four');
+    $this->waitForText('One file only.');
     $this->assertFalse($assert_session->fieldExists('Add file')->hasAttribute('multiple'));
-    $assert_session->pageTextContains('One file only.');
 
     // Assert media type four should only allow jpg files by trying a png file
     // first.
     $png_uri_4 = $file_system->copy($png_image->uri, 'public://');
     $page->attachFileToField('Add file', $file_system->realpath($png_uri_4));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Only files with the following extensions are allowed');
+    $this->waitForText('Only files with the following extensions are allowed');
     // Assert that jpg files are accepted by type four.
     $jpg_uri_2 = $file_system->copy($jpg_image->uri, 'public://');
     $page->attachFileToField('Add file', $file_system->realpath($jpg_uri_2));
-    $assert_session->assertWaitOnAjaxRequest();
-    $page->fillField('Alternative text', $this->randomString());
+    $this->waitForFillField('Alternative text', 'alt text');
     // The type_four media type has another optional image field.
-    $assert_session->pageTextContains('Extra Image');
+    $this->waitForText('Extra Image');
     $jpg_uri_3 = $file_system->copy($jpg_image->uri, 'public://');
     $page->attachFileToField('Extra Image', $this->container->get('file_system')->realpath($jpg_uri_3));
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText($file_system->basename($jpg_uri_3));
+    $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
+    $this->waitForNoText('Save and select');
+    // Ensure the media item was saved to the library and automatically
+    // selected.
+    $this->waitForText('Add or select media');
+    $this->waitForText($file_system->basename($jpg_uri_2));
     // Ensure that the extra image was uploaded to the correct directory.
     $files = $file_storage->loadMultiple();
     $file = array_pop($files);
     $this->assertSame('public://type-four-extra-dir', $file_system->dirname($file->getFileUri()));
-    $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
-    $assert_session->assertWaitOnAjaxRequest();
-    // Ensure the media item was saved to the library and automatically
-    // selected.
-    $assert_session->pageTextContains('Add or select media');
-    $assert_session->pageTextContains($file_system->basename($jpg_uri_2));
+
     // Ensure the created item is added in the widget.
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains($file_system->basename($jpg_uri_2));
+    $this->waitForText('Added one media item.');
+    $this->waitForText($file_system->basename($jpg_uri_2));
+    $this->waitForNoText('Add or select media');
 
     // Assert we can also remove selected items from the selection area in the
     // upload form.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Three');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('Add or select media');
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->clickTypeTab('Three');
     $checkbox = $page->findField("Select $existing_media_name");
     $selected_item_id = $checkbox->getAttribute('value');
     $checkbox->click();
@@ -1139,53 +1106,51 @@ public function testWidgetUpload() {
     $this->assertTrue($assert_session->fieldExists('Add files')->hasAttribute('multiple'));
     $png_uri_5 = $file_system->copy($png_image->uri, 'public://');
     $page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_uri_5));
-    $assert_session->assertWaitOnAjaxRequest();
+
     // Assert the pre-selected items are shown.
-    $selection_area = $assert_session->elementExists('css', '.media-library-add-form__selected-media');
+    $selection_area = $this->assertElementExistsAfterWait('css', '.media-library-add-form__selected-media');
     $assert_session->elementExists('css', 'summary', $selection_area)->click();
     $assert_session->checkboxChecked("Select $existing_media_name", $selection_area);
     $page->uncheckField("Select $existing_media_name");
-    $page->fillField('Alternative text', $this->randomString());
+    $this->waitForFillField('Alternative text', 'alt text');
     $assert_session->hiddenFieldValueEquals('current_selection', '');
     // Close the details element so that clicking the Save and select works.
     // @todo Fix dialog or test so this is not necessary to prevent random
     //   fails. https://www.drupal.org/project/drupal/issues/3055648
     $this->click('details.media-library-add-form__selected-media summary');
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForNoText('Save and select', 50000); // RANDOM FAIL?
+    $this->waitForText("Select $existing_media_name");
+    $this->waitForText('1 item selected');
     $media_items = Media::loadMultiple();
     $added_media = array_pop($media_items);
     $added_media_name = $added_media->label();
-    $assert_session->pageTextContains('1 item selected');
     $assert_session->checkboxChecked("Select $added_media_name");
     $assert_session->checkboxNotChecked("Select $existing_media_name");
     $assert_session->hiddenFieldValueEquals('current_selection', $added_media->id());
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains($file_system->basename($png_uri_5));
+    $assert_session->assertNoElementAfterWait('css', '.media-library-wrapper');
+    $this->waitForText('Added one media item.');
+    $this->waitForNoText('Add or select media');
+    $this->waitForText($file_system->basename($png_uri_5));
 
     // Assert removing an uploaded media item before save works as expected.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Three');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->clickTypeTab('Three');
     $page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_image->uri));
-    $assert_session->assertWaitOnAjaxRequest();
-    // Assert the focus is shifted to the added media items.
-    $this->assertJsCondition('jQuery(".media-library-add-form__added-media").is(":focus")');
     // Assert the media item fields are shown and the vertical tabs are no
     // longer shown.
-    $assert_session->elementExists('css', '.media-library-add-form__fields');
+    $this->assertElementExistsAfterWait('css', '.media-library-add-form__fields');
+    // Assert the focus is shifted to the added media items.
+    $this->assertJsCondition('jQuery(".media-library-add-form__added-media").is(":focus")');
     $assert_session->elementNotExists('css', '.media-library-menu');
     // Press the 'Remove button' and assert the user is sent back to the media
     // library.
     $assert_session->elementExists('css', '.media-library-add-form__remove-button')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     // Assert the remove message is shown.
-    $assert_session->pageTextContains("The media item $png_image->filename has been removed.");
+    $this->waitForText("The media item $png_image->filename has been removed.");
     // Assert the focus is shifted to the first tabbable element of the add
     // form, which should be the source field.
     $this->assertJsCondition('jQuery("#media-library-add-form-wrapper :tabbable").is(":focus")');
@@ -1195,10 +1160,9 @@ public function testWidgetUpload() {
 
     // Assert uploading multiple files.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Three');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
+    $this->clickTypeTab('Three');
     // Assert the existing items are remembered when adding and removing media.
     $checkbox = $page->findField("Select $existing_media_name");
     $checkbox->click();
@@ -1213,10 +1177,10 @@ public function testWidgetUpload() {
       $remote_paths[] = $driver->uploadFileAndGetRemoteFilePath($file_system->realpath($path));
     }
     $page->findField('Add files')->setValue(implode("\n", $remote_paths));
-    $assert_session->assertWaitOnAjaxRequest();
+
     // Assert the media item fields are shown and the vertical tabs are no
     // longer shown.
-    $assert_session->elementExists('css', '.media-library-add-form__fields');
+    $this->assertElementExistsAfterWait('css', '.media-library-add-form__fields');
     $assert_session->elementNotExists('css', '.media-library-menu');
     // Assert all files have been added.
     $assert_session->fieldValueEquals('media[0][fields][name][0][value]', $filenames[0]);
@@ -1228,13 +1192,13 @@ public function testWidgetUpload() {
     $assert_session->checkboxChecked("Select $existing_media_name", $selection_area);
     // Set alt texts for items 1 and 2, leave the alt text empty for item 3 to
     // assert the field validation does not stop users from removing items.
-    $page->fillField('media[0][fields][field_media_test_image][0][alt]', $filenames[0]);
-    $page->fillField('media[1][fields][field_media_test_image][0][alt]', $filenames[1]);
+    $this->waitForFillField('media[0][fields][field_media_test_image][0][alt]', $filenames[0]);
+    $this->waitForFillField('media[1][fields][field_media_test_image][0][alt]', $filenames[1]);
     // Remove the second file and assert the focus is shifted to the container
     // of the next media item and field values are still correct.
     $page->pressButton('media-1-remove-button');
     $this->assertJsCondition('jQuery(".media-library-add-form__media[data-media-library-added-delta=2]").is(":focus")');
-    $assert_session->pageTextContains('The media item ' . $filenames[1] . ' has been removed.');
+    $this->waitForText('The media item ' . $filenames[1] . ' has been removed.');
     // The second media item should be removed (this has the delta 1 since we
     // start counting from 0).
     $assert_session->elementNotExists('css', '.media-library-add-form__media[data-media-library-added-delta=1]');
@@ -1252,7 +1216,7 @@ public function testWidgetUpload() {
     // of the first media item and field values are still correct.
     $page->pressButton('media-2-remove-button');
     $this->assertJsCondition('jQuery(".media-library-add-form__media[data-media-library-added-delta=0]").is(":focus")');
-    $assert_session->pageTextContains('The media item ' . $filenames[2] . ' has been removed.');
+    $this->waitForText('The media item ' . $filenames[2] . ' has been removed.');
     $assert_session->elementNotExists('css', '.media-library-add-form__media[data-media-library-added-delta=1]');
     $assert_session->elementNotExists('css', '.media-library-add-form__media[data-media-library-added-delta=2]');
     $media_item_one = $assert_session->elementExists('css', '.media-library-add-form__media[data-media-library-added-delta=0]');
@@ -1278,103 +1242,98 @@ public function testWidgetOEmbed() {
 
     // Add to the unlimited media field.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
 
     // Assert the default tab for media type one does not have an oEmbed form.
     $assert_session->fieldNotExists('Add Type Five via URL');
 
     // Assert other media types don't have the oEmbed form fields.
-    $page->clickLink('Type Three');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->clickTypeTab('Three');
+    $this->waitForText('Add files');
     $assert_session->fieldNotExists('Add Type Five via URL');
 
     // Assert we can add an oEmbed video to media type five.
-    $page->clickLink('Type Five');
-    $assert_session->assertWaitOnAjaxRequest();
-    $page->fillField('Add Type Five via URL', $youtube_url);
-    $assert_session->pageTextContains('Allowed providers: YouTube, Vimeo.');
+    $this->clickTypeTab('Five');
+    $this->waitForFillField('Add Type Five via URL', $youtube_url);
+    $this->waitForText('Allowed providers: YouTube, Vimeo.');
     $page->pressButton('Add');
-    $assert_session->assertWaitOnAjaxRequest();
     // Assert the name field contains the remote video title.
+    $this->waitForText('The media item has been created but has not yet been saved.', 50000);
     $assert_session->fieldValueEquals('Name', $youtube_title);
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Load the created media item.
-    $media_items = Media::loadMultiple();
-    $added_media = array_pop($media_items);
+    $this->waitForNoText('Save and select');
 
     // Ensure the media item was saved to the library and automatically
     // selected. The added media items should be in the first position of the
     // add form.
-    $assert_session->pageTextContains('Add or select media');
-    $assert_session->pageTextContains($youtube_title);
+    $this->waitForText('Add or select media');
+    $this->waitForText($youtube_title);
+    // Load the created media item.
+    $media_items = Media::loadMultiple();
+    $added_media = array_pop($media_items);
     $assert_session->fieldValueEquals('media_library_select_form[0]', $added_media->id());
     $assert_session->checkboxChecked('media_library_select_form[0]');
 
     // Assert the created oEmbed video is correctly added to the widget.
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains($youtube_title);
+    $this->waitForText('Added one media item.');
+    $this->waitForText($youtube_title);
+    $this->waitForNoText('Add or select media');
 
     // Open the media library again for the unlimited field and go to the tab
     // for media type five.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Five');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
+    $this->clickTypeTab('Five');
 
     // Assert the video is available on the tab.
-    $assert_session->pageTextContains($youtube_title);
+    $this->waitForText($youtube_title);
 
     // Assert we can only add supported URLs.
-    $page->fillField('Add Type Five via URL', 'https://www.youtube.com/');
+    $this->waitForFillField('Add Type Five via URL', 'https://www.youtube.com/');
     $page->pressButton('Add');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('No matching provider found.');
+    $this->waitForText('No matching provider found.', 50000); // RANDOM FAIL?
+
     // Assert we can not add a video ID that doesn't exist. We need to use a
     // video ID that will not be filtered by the regex, because otherwise the
     // message 'No matching provider found.' will be returned.
-    $page->fillField('Add Type Five via URL', 'https://www.youtube.com/watch?v=PWjcqE3QKBg1');
+    $this->waitForFillField('Add Type Five via URL', 'https://www.youtube.com/watch?v=PWjcqE3QKBg1');
     $page->pressButton('Add');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Could not retrieve the oEmbed resource.');
+    $this->waitForText('Could not retrieve the oEmbed resource.', 50000); // RANDOM FAIL?
 
     // Select a media item to check if the selection is persisted when adding
     // new items.
-    $checkbox = $page->findField("Select $youtube_title");
+    $checkbox = $this->waitForFieldExists("Select $youtube_title");
     $selected_item_id = $checkbox->getAttribute('value');
     $checkbox->click();
-    $assert_session->pageTextContains('1 item selected');
+    $this->waitForText('1 item selected');
     $assert_session->hiddenFieldValueEquals('current_selection', $selected_item_id);
 
     // Assert we can add a oEmbed video with a custom name.
-    $page->fillField('Add Type Five via URL', $youtube_url);
+    $this->waitForFillField('Add Type Five via URL', $youtube_url);
     $page->pressButton('Add');
-    $assert_session->assertWaitOnAjaxRequest();
-    $page->fillField('Name', 'Custom video title');
+    $this->waitForText('The media item has been created but has not yet been saved.');
+    $this->waitForFillField('Name', 'Custom video title');
     $selection_area = $assert_session->elementExists('css', '.media-library-add-form__selected-media');
     $assert_session->checkboxChecked("Select $youtube_title", $selection_area);
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForNoText('Save and select', 50000);// RANDOM FAIL?
 
-    // Load the created media item.
-    $media_items = Media::loadMultiple();
-    $added_media = array_pop($media_items);
     // Ensure the media item was saved to the library and automatically
     // selected. The added media items should be in the first position of the
     // add form.
-    $assert_session->pageTextContains('Add or select media');
-    $assert_session->pageTextContains('Custom video title');
+    $this->waitForText('Add or select media');
+    $this->waitForText('Custom video title');
+    // Load the created media item.
+    $media_items = Media::loadMultiple();
+    $added_media = array_pop($media_items);
     $assert_session->fieldValueEquals('media_library_select_form[0]', $added_media->id());
     $assert_session->checkboxChecked('media_library_select_form[0]');
     // Assert the item that was selected before uploading the file is still
     // selected.
-    $assert_session->pageTextContains('2 items selected');
+    $this->waitForText('2 items selected');
     $assert_session->checkboxChecked("Select Custom video title");
     $assert_session->checkboxChecked("Select $youtube_title");
     $assert_session->hiddenFieldValueEquals('current_selection', implode(',', [$selected_item_id, $added_media->id()]));
@@ -1388,42 +1347,39 @@ public function testWidgetOEmbed() {
     $this->assertCount(2, $selected_checkboxes);
     // Ensure the created item is added in the widget.
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added 2 media items.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains('Custom video title');
+    $this->waitForNoText('Insert selected');
+    $this->waitForNoText('Add or select media');
+    $this->waitForText('Added 2 media items.');
+    $this->waitForText('Custom video title');
 
     // Assert we can directly insert added oEmbed media in the widget.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Five');
-    $assert_session->assertWaitOnAjaxRequest();
-    $page->fillField('Add Type Five via URL', $vimeo_url);
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
+    $this->clickTypeTab('Five');
+    $this->waitForFillField('Add Type Five via URL', $vimeo_url);
     $page->pressButton('Add');
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and insert');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains($vimeo_title);
+    $this->waitForText('The media item has been created but has not yet been saved.');
+    $this->assertElementExistsAfterWait('css', '.ui-dialog-buttonpane')->pressButton('Save and insert');
+    $this->waitForText('Added one media item.');
+    $this->waitForNoText('Add or select media');
+    $this->waitForText($vimeo_title);
 
     // Assert we can remove selected items from the selection area in the oEmbed
     // form.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Five');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
+    $this->clickTypeTab('Five');
     $checkbox = $page->findField("Select $vimeo_title");
     $selected_item_id = $checkbox->getAttribute('value');
     $checkbox->click();
     $assert_session->hiddenFieldValueEquals('current_selection', $selected_item_id);
-    $page->fillField('Add Type Five via URL', $youtube_url);
+    $this->waitForFillField('Add Type Five via URL', $youtube_url);
     $page->pressButton('Add');
-    $assert_session->assertWaitOnAjaxRequest();
-    $page->fillField('Name', 'Another video');
-    $selection_area = $assert_session->elementExists('css', '.media-library-add-form__selected-media');
+    $this->waitForText('The media item has been created but has not yet been saved.');
+    $this->waitForFillField('Name', 'Another video');
+    $selection_area = $this->assertElementExistsAfterWait('css', '.media-library-add-form__selected-media');
     $assert_session->elementExists('css', 'summary', $selection_area)->click();
     $assert_session->checkboxChecked("Select $vimeo_title", $selection_area);
     $page->uncheckField("Select $vimeo_title");
@@ -1433,40 +1389,40 @@ public function testWidgetOEmbed() {
     //   fails. https://www.drupal.org/project/drupal/issues/3055648
     $this->click('details.media-library-add-form__selected-media summary');
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Save and select');
-    $assert_session->assertWaitOnAjaxRequest();
-    $media_items = Media::loadMultiple();
-    $added_media = array_pop($media_items);
-    $assert_session->pageTextContains('1 item selected');
+    $this->waitForNoText('Save and select');
+
+    $this->waitForText("Select $vimeo_title");
+    $this->waitForText('1 item selected');
     $assert_session->checkboxChecked('Select Another video');
     $assert_session->checkboxNotChecked("Select $vimeo_title");
+    $media_items = Media::loadMultiple();
+    $added_media = array_pop($media_items);
     $assert_session->hiddenFieldValueEquals('current_selection', $added_media->id());
     $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
-    $this->assertNotEmpty($assert_session->waitForText('Added one media item.'));
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextNotContains('Add or select media');
-    $assert_session->pageTextContains('Another video');
+    $this->waitForNoText('Insert selected');
+    $this->waitForText('Added one media item.');
+    $this->waitForText('Another video');
 
     // Assert removing an added oEmbed media item before save works as expected.
     $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click();
-    $assert_session->assertWaitOnAjaxRequest();
-    $assert_session->pageTextContains('Add or select media');
-    $page->clickLink('Type Five');
-    $assert_session->assertWaitOnAjaxRequest();
-    $page->fillField('Add Type Five via URL', $youtube_url);
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
+    $this->waitForText('Add or select media');
+    $this->clickTypeTab('Five');
+    $this->waitForFillField('Add Type Five via URL', $youtube_url);
     $page->pressButton('Add');
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->waitForText('The media item has been created but has not yet been saved.');
     // Assert the focus is shifted to the added media items.
     $this->assertJsCondition('jQuery(".media-library-add-form__added-media").is(":focus")');
     // Assert the media item fields are shown and the vertical tabs are no
     // longer shown.
-    $assert_session->elementExists('css', '.media-library-add-form__fields');
+    $this->assertElementExistsAfterWait('css', '.media-library-add-form__fields');
     $assert_session->elementNotExists('css', '.media-library-menu');
     // Press the 'Remove button' and assert the user is sent back to the media
     // library.
     $assert_session->elementExists('css', '.media-library-add-form__remove-button')->click();
-    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertElementExistsAfterWait('css', '.media-library-menu');
     // Assert the remove message is shown.
-    $assert_session->pageTextContains("The media item $youtube_title has been removed.");
+    $this->waitForText("The media item $youtube_title has been removed.");
     // Assert the focus is shifted to the first tabbable element of the add
     // form, which should be the source field.
     $this->assertJsCondition('jQuery("#media-library-add-form-wrapper :tabbable").is(":focus")');
@@ -1474,4 +1430,183 @@ public function testWidgetOEmbed() {
     $assert_session->elementExists('css', '.media-library-menu');
   }
 
+  /**
+   * Asserts that text does not appear on page after a wait.
+   *
+   * @todo replace with whatever gets added in
+   *   https://www.drupal.org/node/3061852
+   */
+  protected function waitForNoText($text, $timeout = 10000) {
+    $page = $this->getSession()->getPage();
+    $result = $page->waitFor($timeout / 1000, function () use ($page, $text) {
+      $actual = preg_replace('/\s+/u', ' ', $page->getText());
+      $regex = '/' . preg_quote($text, '/') . '/ui';
+      return (bool) !preg_match($regex, $actual);
+    });
+    if (empty($result)) {
+      $this->htmlOutput();
+      $filename = $this->htmlOutputDirectory . '/' . $this->htmlOutputClassName . '-' . $this->htmlOutputCounter . '-' . $this->htmlOutputTestId . '.jpg';
+      $this->createScreenshot($filename);
+    }
+    $this->assertNotEmpty($result, "\"$text\" not found");
+  }
+
+  /**
+   * Asserts that text appears on page after a wait.
+   *
+   * @todo replace with whatever gets added in
+   *   https://www.drupal.org/node/3061852
+   */
+  protected function waitForText($text, $timeout = 20000) {
+    $assert_session = $this->assertSession();
+    $result = $assert_session->waitForText($text, $timeout);
+    if (empty($result)) {
+      $this->htmlOutput();
+      $filename = $this->htmlOutputDirectory . '/' . $this->htmlOutputClassName . '-' . $this->htmlOutputCounter . '-' . $this->htmlOutputTestId . '.jpg';
+      $this->createScreenshot($filename);
+    }
+    $this->assertNotEmpty($result, "\"$text\" found");
+  }
+
+  /**
+   * Asserts that text appears on page after a wait.
+   *
+   * @todo replace with whatever gets added in
+   *   https://www.drupal.org/node/3061852
+   */
+  protected function waitForElementTextContains($selector, $text) {
+    $assert_session = $this->assertSession();
+    $element = $assert_session->waitForElement('css', "$selector:contains('$text')");
+    $this->assertNotEmpty($element);
+  }
+
+  /**
+   * Waits for the specified selector and returns it if not empty.
+   *
+   * @param string $selector
+   *   The selector engine name. See ElementInterface::findAll() for the
+   *   supported selectors.
+   * @param string|array $locator
+   *   The selector locator.
+   * @param int $timeout
+   *   (Optional) Timeout in milliseconds, defaults to 10000.
+   *
+   * @return \Behat\Mink\Element\NodeElement
+   *   The page element node if found. If not found, the test fails.
+   *
+   * @todo replace with whatever gets added in
+   *   https://www.drupal.org/node/3061852
+   */
+  protected function assertElementExistsAfterWait($selector, $locator, $timeout = 10000) {
+    $assert_session = $this->assertSession();
+    $element = $assert_session->waitForElement($selector, $locator, $timeout);
+    $this->assertNotEmpty($element);
+    return $element;
+  }
+
+  /**
+   * Clicks a media type tab and waits for it to appear.
+   */
+  protected function clickTypeTab($type) {
+    $page = $this->getSession()->getPage();
+    $assert_session = $this->assertSession();
+    $lowercase_type = strtolower($type);
+
+    try {
+      $assert_session->elementExists('css', ".media-library-menu-type-$lowercase_type .active-tab");
+      // There is nothing to do as the type is already active.
+      return;
+    }
+    catch (ElementNotFoundException $e) {
+    }
+
+    $page->clickLink($type);
+    $this->assertElementExistsAfterWait('css', ".media-library-menu-type-$lowercase_type .active-tab");
+    $assert_session->waitForElementVisible('css', "[action='/admin/content/media-widget/type_$lowercase_type']");
+  }
+
+  /**
+   * Checks for a specified number of specific elements on page after wait.
+   *
+   * @param string $selector_type
+   *   Element selector type (css, xpath)
+   * @param string|array $selector
+   *   Element selector.
+   * @param int $count
+   *   Expected count.
+   * @param int $timeout
+   *   (Optional) Timeout in milliseconds, defaults to 10000.
+   *
+   * @todo replace with whatever gets added in
+   *   https://www.drupal.org/node/3061852
+   */
+  protected function waitForElementsCount($selector_type, $selector, $count, $timeout = 10000) {
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    $start = microtime(TRUE);
+    $end = $start + ($timeout / 1000);
+    do {
+      $nodes = $page->findAll($selector_type, $selector);
+      if (count($nodes) === $count) {
+        return;
+      }
+      usleep(100000);
+    } while (microtime(TRUE) < $end);
+
+    $assert_session->elementsCount($selector_type, $selector, $count);
+  }
+
+  /**
+   * Checks for the existence of a field on page after wait.
+   *
+   * @param string $field
+   *   The field to find.
+   * @param int $timeout
+   *   (Optional) Timeout in milliseconds, defaults to 10000.
+   *
+   * @return \Behat\Mink\Element\NodeElement|null
+   *   The element if found, otherwise null.
+   *
+   * @todo replace with whatever gets added in
+   *   https://www.drupal.org/node/3061852
+   */
+  protected function waitForFieldExists($field, $timeout = 10000) {
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    $start = microtime(TRUE);
+    $end = $start + ($timeout / 1000);
+    do {
+      $node = $page->findField($field);
+      if (!is_null($node)) {
+        return $node;
+      }
+      usleep(100000);
+    } while (microtime(TRUE) < $end);
+
+    return $assert_session->fieldExists($field);
+  }
+
+  /**
+   * Checks if a field exists and provides it a value.
+   *
+   * @param string $field
+   *   The field to find.
+   * @param string $value
+   *   The value entered in the field.
+   * @param int $timeout
+   *   (Optional) Timeout in milliseconds, defaults to 10000.
+   *
+   * @todo replace with whatever gets added in
+   *   https://www.drupal.org/node/3061852
+   */
+  protected function waitForFillField($field, $value, $timeout = 10000) {
+    $page = $this->getSession()->getPage();
+
+    if ($this->waitForFieldExists($field, $timeout)) {
+      $page->fillField($field, $value);
+    }
+  }
+
 }
